@layer components {
  /* Filter bars — the row of controls above a list.
   *
   * Layout only, and deliberately. A filter bar is a GET form whose fields are
   * whatever that screen filters by, so there is no partial to render: the
   * pieces are already here. Each filter is a .combobox, or a .popover holding
   * whatever doesn't fit in one, and Apply is a .btn. A partial that took "the
   * filters" as an argument would either be a hash shaped like a form — which
   * is a form, written twice — or a block, which is this row with extra steps.
   *
   * The combobox goes in the row, not inside a filter popover. Not for clipping
   * any more — a surface is fixed and escapes .popover__surface's own scroller
   * like any other — but because a combobox already brings its own anchored
   * surface, so the popover around it was never buying anything. Two overlays
   * deep to choose one value is two things to dismiss.
   *
   * The form is a real GET form, so the filtered list has a URL: it can be
   * bookmarked, linked to a colleague, and reloaded. That is also what makes
   * removing a single filter a plain link rather than a controller — see
   * __applied below.
   */
  .filter-bar {
    --filter-bar-search-width: 16rem;

    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-half);
  }

  /* Sized to sit in the row rather than to fill it, and a pill because it is
     one control among buttons rather than a field in a form.
     Add the .grow utility where the search is the only field in the row and
     should take what is left of it. */
  .filter-bar__search {
    inline-size: var(--filter-bar-search-width);
    max-inline-size: 100%;
    padding: var(--control-padding-block) var(--control-padding-inline);
    border: var(--border);
    border-radius: var(--radius-pill);
    background: var(--color-canvas);
    font: inherit;
    color: inherit;
  }

  /* A magnifier inside the field.
   *
   * Its own wrapper because there is nowhere else to put it: an <input> has no
   * children, and a background-image cannot take currentColor — which is the same
   * reason every icon in the app is inlined SVG rather than an <img>. The wrapper
   * carries the growing, so .filter-bar__search keeps its own width when it is
   * used bare.
   *
   * The icon is pointer-events: none so that the whole field is still one target:
   * clicking the magnifier puts the caret in the box, which is what everybody
   * expects of it and what it would otherwise not do. */
  .filter-bar__field {
    --filter-bar-field-inset: var(--control-padding-inline);

    position: relative;
    display: flex;
    align-items: center;
    flex: 1;
    min-inline-size: var(--filter-bar-search-width);

    .filter-bar__search {
      inline-size: 100%;
      padding-inline-start: calc(var(--filter-bar-field-inset) * 2 + 1em);
    }
  }

  .filter-bar__icon {
    position: absolute;
    inset-inline-start: var(--filter-bar-field-inset);
    color: var(--color-ink-soft);
    pointer-events: none;
  }

  /* The far end of the row. Two names for one arrangement, and each is true
     where it is used: __actions is Apply and clearing everything, which act on
     all the filters; __end is a control that belongs at the end rather than
     among them, which is a search box and the controls beside it. Folded because
     they are the same three declarations and drifting apart would show up as one
     of them not lining up with the other.

     __end wraps, because it can hold three comboboxes and a field: below the
     wide band that is more than a row has, and a group that refuses to wrap
     pushes the search off the end of the screen. */
  .filter-bar__actions,
  .filter-bar__end {
    display: flex;
    align-items: center;
    gap: var(--space-half);
    margin-inline-start: auto;
  }

  .filter-bar__end {
    flex-wrap: wrap;
    justify-content: flex-end;
  }

  /* What is applied right now, under the row.
   *
   * Every one is a .badge whose × is a link back to the same page without that
   * filter. No JavaScript and no state: the server already knows what is
   * applied, because it is in the URL that produced the page. */
  .filter-bar__applied {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-quarter);
    margin-block-start: var(--space-half);
    color: var(--color-ink-soft);
    font-size: var(--text-small);
  }

  /* Below the small band the search takes the whole row and the actions stop
     being pushed to an end that is no longer there. */
  @media (width < 40rem) {
    .filter-bar__search {
      inline-size: 100%;
    }

    .filter-bar__actions,
    .filter-bar__end {
      margin-inline-start: 0;
    }
  }
}
