/* ═══════════════════════════════════════════════════════════════════
   Shop listing — toolbar and spec filters

   CLAUDE.md §9.1: the SAME filters on phone and desktop, in a
   different form factor. Bottom sheet below 900px, docked column
   above it. Nothing is removed on mobile.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Toolbar / filters — same controls on both, different form factor
      (CLAUDE.md §9.1). Phone: a full-width sheet-style stack. ────── */
/* STICKY ON A PHONE, AND IT IS THE ONLY THING THAT IS. Search-in-these
   -results and Filters used to scroll away after the first row of
   tiles, so narrowing a 692-product listing meant scrolling back to the
   top to reach the control and back down to see what it did. The header
   gave up its sticky position for this (chrome.css): one bar is pinned,
   and it is the one that acts on what is under it.

   Full bleed on the page ground rather than the card it is at rest,
   because a rounded card pinned to the top edge shows the grid sliding
   through the gap above its corners. The card comes back at 768px,
   where the header is sticky again and this is not. */
.toolbar{display:flex;flex-direction:row;align-items:center;gap:var(--sp-2);
  position:sticky;top:0;z-index:var(--z-sticky);
  margin-inline:calc(var(--gutter) * -1);padding:var(--sp-3) var(--gutter);
  background:var(--bg);border-bottom:1px solid var(--line);
  margin-bottom:var(--sp-4)}
/* THE GRID DIMS WHILE THE LIVE SEARCH RE-QUERIES, rather than being
   replaced by a skeleton. A skeleton on every keystroke-pause is a page
   that flashes while you type, and it collapses the grid's height so the
   whole listing jumps under the field. Dimming says "this is about to
   change" and holds the layout still while it does. */
#shopGrid.is-loading{opacity:.5;transition:opacity .12s ease}

/* The search takes the slack; the button keeps its label. Neither may set
   a pixel min-width — that is what pushes the 360px viewport (§1.4). */
.toolbar .inv-search-wrap{flex:1 1 0;min-width:0}
.toolbar .filter-open{flex:0 0 auto}


/* ── Filters ─────────────────────────────────────────────────────────
   CLAUDE.md §9.1: the SAME filters on both, different form factor.
   Phone gets a bottom sheet behind a Filters button; from 900px up the
   identical controls sit in a panel beside the grid. Nothing is removed
   on mobile — a filter you cannot reach is a filter you do not have. */
.filter-bar{display:flex;gap:var(--sp-2);align-items:center;flex-wrap:wrap;
  margin-bottom:var(--sp-4)}
.filter-open{display:inline-flex;align-items:center;gap:var(--sp-2);
  min-height:var(--tap);padding:0 var(--sp-4);border-radius:var(--r);
  border:1px solid var(--line);background:var(--surface);color:var(--ink);font-weight:700}
.filter-open .ico{width:22px;height:22px}
.filter-open .n{background:var(--brand);color:var(--logo-ink);border-radius:var(--r-full);
  padding:1px 8px;font-size:.8125rem;font-variant-numeric:tabular-nums}
.filter-clear{min-height:var(--tap);color:var(--brand-ink);font-weight:700;font-size:.9375rem}

.filter-panel{position:fixed;top:0;right:0;bottom:0;left:0;inset:0;
  z-index:var(--z-drawer);display:none}
.filter-panel.open{display:block}
.filter-panel .sheet{
  position:absolute;left:0;right:0;bottom:0;max-height:88vh;
  background:var(--surface);border-radius:var(--r-lg) var(--r-lg) 0 0;
  display:flex;flex-direction:column;overflow:hidden}
.filter-panel .backdrop-f{position:absolute;top:0;right:0;bottom:0;left:0;inset:0;
  background:var(--scrim)}
.filter-head{display:flex;align-items:center;justify-content:space-between;
  padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--line)}
.filter-head h2{font-size:1.125rem}
.filter-body{flex:1 1 auto;overflow-y:auto;padding:var(--sp-4);
  display:flex;flex-direction:column;gap:var(--sp-5)}
.filter-grp h3{font-size:.8125rem;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-3);margin-bottom:var(--sp-2)}
.chip-row{display:flex;flex-wrap:wrap;gap:var(--sp-2)}
/* Converter type is the one genuinely MULTI-select facet, so it is a real
   checkbox — native semantics, native keyboard, and a tick the customer
   can see is a tick. The input itself is visually replaced, not removed:
   it stays in the accessibility tree and keeps focus. */
.chip{display:inline-flex;align-items:center;gap:var(--sp-2);min-height:var(--tap);
  padding:0 var(--sp-3) 0 var(--sp-2);border-radius:var(--r-full);border:1px solid var(--line);
  background:var(--surface);color:var(--ink);font-size:.9375rem;font-weight:600;
  cursor:pointer;position:relative}
.chip input{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
.chip-tick{flex:0 0 auto;width:22px;height:22px;border-radius:var(--r-sm);
  border:2px solid var(--line);background:var(--surface);
  display:flex;align-items:center;justify-content:center}
.chip-tick svg{width:16px;height:16px;fill:none;stroke:var(--brand);stroke-width:3.2;
  stroke-linecap:round;stroke-linejoin:round;opacity:0}
.chip .n{color:var(--ink-3);font-variant-numeric:tabular-nums;font-weight:500}
.chip:hover{border-color:var(--brand-dark)}
.chip:focus-within{outline:2px solid var(--brand);outline-offset:2px}
/* NOT :has(). The selected look is the only thing that tells a customer
   a filter tap registered — the real input is visually hidden, and on the
   mobile sheet nothing applies until the Apply button — and :has() is
   absent from Firefox before 121, Safari before 15.4 and every Android
   WebView before Chromium 105. A ticked chip was pixel-identical to an
   unticked one on all of them. `is-on` is set at render time from the URL
   and kept in step on `change` (filters.js).
   The tick itself also rides the general sibling combinator, which is
   supported everywhere, so it is right even before the class lands. */
.chip.is-on{background:var(--brand);border-color:var(--brand-dark);
  color:var(--logo-ink)}
.chip.is-on .chip-tick{background:var(--logo-ink);border-color:var(--logo-ink)}
.chip input:checked ~ .chip-tick svg{opacity:1}
.chip.is-on .n{color:var(--logo-ink);opacity:.75}
/* §9.10 — the extra chips are present in the DOM at all times; the
   disclosure only changes whether they are laid out. */
.chip.is-extra{display:none}
.filter-grp.show-all .chip.is-extra{display:inline-flex}
.disclose{display:inline-flex;align-items:center;min-height:var(--tap);
  margin-top:var(--sp-2);color:var(--brand-ink);font-weight:700;font-size:1rem;
  text-decoration:underline;text-underline-offset:3px}
.disclose:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

.filter-note{font-size:.875rem;color:var(--ink-3);margin-top:var(--sp-1);
  line-height:1.5}

/* ── Bounded sliders ─────────────────────────────────────────────────
   The track only spans values the shop actually stocks, plus one "Any"
   notch at the left. A 28px thumb inside a 44px row is the §9.x target
   without a 44px-tall bar across the panel. */
/* The stocked bounds sit either side of the track, so the customer can
   read the whole range at a glance instead of discovering it by dragging.
   The live value goes up beside the heading where it is legible at 16px
   rather than squeezed onto the rail. */
.slider-head{display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--sp-2);margin-bottom:var(--sp-1)}
.slider-head h3{margin-bottom:0}
.slider-out{flex:0 0 auto;font-size:1rem;font-weight:800;color:var(--brand-ink);
  font-variant-numeric:tabular-nums}
.slider-bound{flex:0 0 auto;font-size:.8125rem;color:var(--ink-3);white-space:nowrap;
  font-variant-numeric:tabular-nums}
.slider{display:flex;align-items:center;gap:var(--sp-2)}
.slider-in{flex:1 1 0;min-width:0;height:var(--tap);background:transparent;
  -webkit-appearance:none;appearance:none;cursor:pointer}
.slider-in:focus-visible{outline:2px solid var(--brand);outline-offset:4px;border-radius:var(--r-sm)}
.slider-in::-webkit-slider-runnable-track{height:6px;border-radius:var(--r-full);
  background:var(--surface-2);border:1px solid var(--line)}
.slider-in::-moz-range-track{height:6px;border-radius:var(--r-full);
  background:var(--surface-2);border:1px solid var(--line)}
.slider-in::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
  width:28px;height:28px;margin-top:-12px;border-radius:var(--r-full);
  background:var(--brand);border:2px solid var(--brand-dark);box-shadow:var(--shadow-sm)}
.slider-in::-moz-range-thumb{width:28px;height:28px;border-radius:var(--r-full);
  background:var(--brand);border:2px solid var(--brand-dark);box-shadow:var(--shadow-sm)}

.filter-foot{position:sticky;bottom:0;background:var(--surface);border-top:1px solid var(--line);
  padding:var(--sp-3) var(--sp-4);display:flex;gap:var(--sp-2);
  padding-bottom:calc(var(--sp-3) + env(safe-area-inset-bottom,0px))}
.filter-foot .btn{flex:1 1 0}


@media (min-width:900px){
  /* Same markup, same controls — docked instead of sheeted. */
  .shop-layout{display:grid;grid-template-columns:264px minmax(0,1fr);gap:var(--sp-6);
    align-items:start}
  /* The filter column eats 264px, so the grid beside it drops a track. */
  .shop-layout .prod-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
  .filter-open{display:none}
  .filter-panel{position:sticky;right:auto;bottom:auto;left:auto;inset:auto;
    top:calc(var(--sp-4) + 96px);display:block;z-index:1}
  .filter-panel .backdrop-f{display:none}
  .filter-panel .sheet{position:static;max-height:none;border-radius:var(--r);
    border:1px solid var(--line)}
  .filter-panel .filter-head .icon-btn{display:none}
  .chip{min-height:38px;padding:0 var(--sp-3);font-size:.875rem}
}
@media (min-width:1200px){
  .shop-layout .prod-grid{grid-template-columns:repeat(4,minmax(0,1fr))}
}


/* ═══ ≥768 — the header is sticky again, so the toolbar is not ═════ */
@media (min-width:768px){
  .toolbar{position:static;margin-inline:0;padding:var(--sp-3);
    background:var(--surface);border:1px solid var(--line);
    border-radius:var(--r)}
}
