/* ═══════════════════════════════════════════════════════════════════
   Base — reset, typography, buttons, entry fields, section scaffolding
   and the shared loading / empty / error states.

   Colours come from tokens.css. Nothing here may define one.
   ═══════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════
   Nerdshed storefront — MOBILE FIRST.

   Everything outside a media query IS the phone layout. Every @media is
   `min-width`, so the page starts at 360px and only gains capability as
   the screen grows. Nothing is written for desktop and walked back.

   UI_UX.md §9.3 states its breakpoints as max-width because the dash was
   built desktop-down. Same numbers, read upward:

     base   360–479   ≥480   ≥640   ≥768 (tab bar retires)   ≥900   ≥1200

   CLAUDE.md §9.1: one UI adapted, not two. The breakpoint changes the
   form factor, never the feature set.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Base ───────────────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box}
*{margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  /* CLAUDE.md §9.2 — 16px body minimum. */
  font-family:var(--font-sans);font-size:16px;line-height:1.6;
  background:var(--bg);color:var(--ink-2);
  -webkit-font-smoothing:antialiased;
}
img,svg{max-width:100%}
img{height:auto;display:block}
ul,ol{list-style:none}
a{color:var(--brand-ink);text-decoration:none}
h1,h2,h3,h4{color:var(--ink);line-height:1.2;text-wrap:balance}
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}
input,select,textarea,button{accent-color:var(--brand)}

/* Visually hidden. `max-width` and `!important` are not belt-and-braces
   here: `.toolbar > *{width:100%}` has higher specificity than `.sr-only`
   and was stretching the hidden "Sort by" label to the full column,
   pushing the document 28px wider than the viewport on every category
   page. A hidden label must be un-stretchable by any layout rule. */
.sr-only{position:absolute!important;width:1px!important;max-width:1px!important;
  height:1px!important;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.skip{position:absolute;left:-9999px;top:0;z-index:9999;background:var(--brand);
  color:var(--logo-ink);padding:var(--sp-3) var(--sp-4);font-weight:700;
  border-radius:0 0 var(--r) 0}
.skip:focus{left:0}

:where(a,button,input,select,[tabindex]):focus-visible{
  outline:3px solid var(--brand);outline-offset:2px;border-radius:var(--r-sm)}

/* §2 SVG only. CLAUDE.md §9.3: icons paired with text run LARGE —
   1.5em off the parent so the pair scales together. */
.ico{width:1.5em;height:1.5em;flex:0 0 auto;fill:none;stroke:currentColor;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}

.wrap{width:100%;max-width:var(--page);margin-inline:auto;padding-inline:var(--gutter)}

/* ── Buttons ────────────────────────────────────────────────────── */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
  min-height:var(--tap);padding:0 var(--sp-5);border-radius:var(--r);
  font-size:1rem;font-weight:700;border:1px solid transparent;min-width:0;
  transition:background .15s,border-color .15s,color .15s;text-align:center;
  line-height:1.2;
}
.btn .ico{width:1.35em;height:1.35em}          /* ≥20px inside a button */
.btn-primary{background:var(--brand);color:var(--logo-ink);border-color:var(--brand-dark)}
.btn-primary:hover{background:var(--brand-dark)}
.btn-primary[disabled]{background:var(--surface-2);color:var(--ink-3);
  border-color:var(--line);cursor:not-allowed}
.btn-dark{background:var(--dark);color:var(--on-fill);border-color:var(--dark)}
.btn-dark:hover{background:var(--ink-2)}
.btn-quiet{background:var(--surface);color:var(--ink);border-color:var(--line)}
.btn-quiet:hover{border-color:var(--brand);color:var(--brand-ink)}
.btn-outline{background:transparent;color:var(--ink);border-color:var(--ink-3)}
.btn-outline:hover{border-color:var(--brand-dark);color:var(--brand-ink);background:var(--brand-xlight)}
.btn-block{width:100%}
.btn-sm{min-height:38px;font-size:.9375rem;padding:0 var(--sp-3)}

.icon-btn{
  min-width:var(--tap);min-height:var(--tap);border-radius:var(--r);
  display:inline-flex;align-items:center;justify-content:center;
  color:var(--ink-2);position:relative;flex:0 0 auto;
}
.icon-btn:hover{background:var(--surface-2);color:var(--ink)}
.icon-btn .ico{width:26px;height:26px}          /* §9.3 icon-alone ≥24px */

/* ── Entry fields — one canonical class, §4.2 ───────────────────── */
/* WEBKIT IGNORES min-height ON <select>. .field-in below sets
   min-height:var(--tap), and Safari computes it to 18px and renders a
   24px control — under the 44px floor on every iPhone, for BOTH the
   sort dropdown (§9.9 put it in the filter panel) and the checkout's
   state field. Measured across engines: Chromium and Gecko were 44px
   and only WebKit was short, so no Chromium harness could have caught
   it. `height` IS honoured on a select in every engine, so it is set
   explicitly rather than left to min-height. */
select.field-in{height:var(--tap)}

.field-in{
  width:100%;min-width:0;min-height:var(--tap);
  padding:var(--sp-2) var(--sp-3);
  background:var(--surface);color:var(--ink);
  border:1px solid var(--line);border-radius:6px;
  font-family:inherit;font-size:1rem;
}
.field-in::placeholder{color:var(--ink-3)}
.field-in:focus{outline:none;border-color:var(--brand);
  box-shadow:0 0 0 3px var(--brand-light)}

/* §4.3 magnifier-prefix search — exact structure everywhere */
.inv-search-wrap{position:relative;display:flex;align-items:center;flex:1 1 auto;min-width:0}
.inv-search-icon{position:absolute;left:.6rem;width:22px;height:22px;color:var(--ink-3);
  pointer-events:none;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.inv-search-wrap input{padding-left:2.4rem}


/* ── Sections ───────────────────────────────────────────────────── */
.sec{padding-block:var(--sp-8)}
.sec-alt{background:var(--surface-2)}
.sec-head{display:flex;align-items:flex-end;justify-content:space-between;
  gap:var(--sp-3);margin-bottom:var(--sp-5);flex-wrap:wrap}
.sec-head h2{font-size:1.375rem;font-weight:800;letter-spacing:-.01em}
.sec-head .sub{font-size:var(--fs-pair-sub);line-height:var(--lh-pair-sub);
  color:var(--ink-3);margin-top:4px}
.sec-head .rule{width:38px;height:4px;background:var(--brand);border-radius:2px;
  margin-top:var(--sp-2)}
.more{display:inline-flex;align-items:center;gap:var(--sp-1);
  font-size:.9375rem;font-weight:700;color:var(--brand-ink);min-height:var(--tap)}
.more .ico{width:20px;height:20px}


/* ── States ─────────────────────────────────────────────────────── */
.skeleton{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r);overflow:hidden}
.skeleton .bar,.skeleton .box{
  background:linear-gradient(90deg,var(--surface-2) 25%,var(--line-soft) 37%,var(--surface-2) 63%);
  background-size:400% 100%;animation:shimmer 1.4s ease infinite}
.skeleton .bar{height:12px;border-radius:var(--r-sm);margin:var(--sp-2) var(--sp-3)}
.skeleton .box{aspect-ratio:1/1;margin:var(--sp-2) var(--sp-2) 0;border-radius:var(--r-sm)}
@keyframes shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}
.empty{text-align:center;padding:var(--sp-12) var(--sp-4);color:var(--ink-3)}
.empty .ico{width:48px;height:48px;color:var(--line);stroke-width:1.4;margin:0 auto var(--sp-3)}
.empty h2{font-size:1.25rem;margin-bottom:var(--sp-2)}
/* An empty grid always offers a way on — a range hidden from the menu
   for being out of stock can still be reached by a bookmark. */
.empty .btn{margin-top:var(--sp-4)}
.err{background:var(--surface);border:1px solid var(--red);border-left:4px solid var(--red);
  border-radius:0 var(--r) var(--r) 0;padding:var(--sp-4);color:var(--ink-2);font-size:1rem}
.err b{display:block;font-size:var(--fs-pair-head);color:var(--ink);margin-bottom:var(--sp-1)}
.pager{display:flex;align-items:center;justify-content:center;gap:var(--sp-3);
  margin-top:var(--sp-6);flex-wrap:wrap}
.pager .n{font-size:.9375rem;color:var(--ink-3);font-variant-numeric:tabular-nums}
.center{text-align:center}

/* ═══ Breakpoints owned by this file ════════════════════════════ */
@media (min-width:480px){
  :root{--gutter:var(--sp-5)}
}
@media (min-width:640px){
  .sec{padding-block:var(--sp-10)}
  .sec-head h2{font-size:1.5rem}
}
@media (min-width:900px){
  .sec-head h2{font-size:1.625rem}
}
@media (min-width:1200px){
  :root{--gutter:var(--sp-8)}
}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important}
}

/* ═══ 404 ══════════════════════════════════════════════════════════
   Not a dead end. The URL is a search query the shop already has, so
   this page runs it and shows what it found; the ranges and the
   Assistant sit below that. Order follows §9.9 — the useful thing
   first, "go home" is not an answer to "I cannot find the part".
   ═════════════════════════════════════════════════════════════════ */
.nf{padding-block:var(--sp-6) var(--sp-8)}
.nf-head{display:flex;flex-direction:column;gap:var(--sp-2);margin-bottom:var(--sp-5)}
.nf-head .nf-ico{width:40px;height:40px;color:var(--brand-ink)}
.nf-head h1{font-size:1.5rem;font-weight:800;color:var(--ink)}
.nf-said{font-size:1rem;color:var(--ink-2);max-width:60ch;line-height:1.6}
.nf-said code{font-family:var(--font-mono);font-size:.9375rem;
  background:var(--surface-2);border-radius:var(--r-sm);padding:2px 6px;
  overflow-wrap:anywhere}

.nf-search{margin-bottom:var(--sp-6)}
.nf-search .search-wrap{max-width:520px}

.nf-hits{margin-bottom:var(--sp-8)}
.nf-hits h2{font-size:1.125rem;font-weight:800;color:var(--ink);
  margin-bottom:var(--sp-3)}

.nf-next h2{font-size:1.125rem;font-weight:800;color:var(--ink);
  margin-bottom:var(--sp-3)}
.nf-links{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin-bottom:var(--sp-5)}
.nf-links .btn{flex:0 1 auto}
.nf-links .ico{width:1.25em;height:1.25em}

/* Every range, as a wrapping set of links — no scroller to discover and
   nothing hidden behind a control on the one page where the customer is
   already lost. */
.nf-cats{display:flex;flex-wrap:wrap;gap:var(--sp-2)}
.nf-cats a{display:inline-flex;align-items:center;min-height:var(--tap);
  padding:0 var(--sp-3);border:1px solid var(--line);border-radius:var(--r-full);
  font-size:1rem;font-weight:600;color:var(--ink-2);background:var(--surface)}
.nf-cats a:hover{border-color:var(--brand);color:var(--brand-ink)}
.nf-cats a:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

@media (min-width:768px){
  .nf-head h1{font-size:1.875rem}
  .nf-head{flex-direction:row;align-items:center;gap:var(--sp-4);flex-wrap:wrap}
  .nf-said{flex:1 1 100%}
}

/* The no-JavaScript fallback (views/index.html). Only ever painted when
   the module did not run, so it carries no state and no tokens beyond
   the ones already loaded by this stylesheet's cascade. */
.ns-fallback{max-width:60ch;padding-block:var(--sp-10)}
.ns-fallback h1{font-size:1.5rem;font-weight:800;color:var(--ink);
  margin-bottom:var(--sp-4)}
.ns-fallback p{margin-bottom:var(--sp-4);line-height:1.7}
.ns-fallback .btn{width:auto}
