/* ============================================================
   DUCKIT — catalog page only.
   Hero = oversized count over a carbon crop; body = sticky chassis
   filter over a dense card grid.
   ============================================================ */

/* ------------------------------------------------------------ hero */

.chero {
  position: relative;
  min-height: var(--band-h);
  display: grid;
  align-items: end;
  isolation: isolate;
}
.chero__media { position: absolute; inset: 0; z-index: -2; }
.chero__media img { width: 100%; height: 100%; object-fit: cover; }
.chero::before {
  content: '';
  position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(180deg, rgba(10,10,11,0.16) 0%, transparent 32%, rgba(10,10,11,0.86) 88%, var(--ink) 100%),
    linear-gradient(90deg, rgba(10,10,11,0.40), transparent 62%);
}
.chero__in { padding-block: clamp(44px, 6vw, 88px); }
.chero__h { font-size: clamp(2rem, 1.2rem + 3vw, 3.5rem); margin: 16px 0 20px; letter-spacing: -0.03em; }
.chero__n {
  color: var(--torch);
  font-size: clamp(3rem, 1.6rem + 6vw, 6.5rem);
  display: inline-block;
  line-height: 0.86;
  letter-spacing: -0.04em;
}
.chero__p { color: rgba(255, 255, 255, 0.84); max-width: 44ch; font-size: var(--t-md); }

/* ------------------------------------------------------------ sticky filter */

.cat { padding-bottom: var(--sec-y); }

/* Every filter down the left, the parts on the right. Car, service, category
   and style all live in the one rail, so narrowing by two of them is two
   clicks in the same column rather than a trip back to the top of the page.
   .cat__panel is display:contents here so the rail and the tools bar land in
   the grid directly; on a phone it stops being transparent and becomes the
   sheet that holds them both. */
.cat__in {
  display: grid;
  grid-template-columns: 272px minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  gap: 0 clamp(20px, 2.4vw, 38px);
  padding-top: clamp(20px, 2.4vw, 32px);
}
.cat__panel, .cat__sheet-body { display: contents; }
/* Only the count, reset and sort stay over the grid, so the bar is the width of
   the grid rather than the page - it no longer covers the rail, which is why
   the rail sticks to the header alone below. */
.cat__bar   { grid-column: 2; grid-row: 1; }
.cat__rail  { grid-column: 1; grid-row: 1 / span 2; }
.cat__main  { grid-column: 2; grid-row: 2; min-width: 0; }
/* A filter change scrolls back to here. html's scroll-padding-top only clears
   the site header, but .cat__bar sticks below it, so without the bar's own
   height the first row of parts lands underneath the chips. --barh is published
   on .cat__in by site.js and is 0 on phones, where the bar sits in the sheet and
   does not stick. */
.cat__main { scroll-margin-top: var(--barh, 0px); }
/* phone-only furniture */
.cat__mbar, .cat__sheet-head, .cat__sheet-foot, .cat__scrim { display: none; }

/* ---- left rail: car tree, then service, category and style */
.cat__rail {
  position: sticky;
  /* the tools bar sits over the grid column only, so this clears the site
     header and nothing else */
  top: calc(var(--header-h) + 16px);
  align-self: start;
  max-height: calc(100svh - var(--header-h) - 32px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
  border-right: 1px solid var(--line);
  padding-right: 14px;
}
.cat__rail-h {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* white, not muted — see .cat__lab */
  color: var(--white);
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 8px;
}
.tree, .tree__kids { list-style: none; }
.tree__kids { margin-left: 12px; border-left: 1px solid var(--line); }
.tree__kids[hidden] { display: none; }

/* disclosure arrow on the nodes that have a branch under them */
.tw__chev { display: grid; flex: none; color: var(--mute-dim); transition: transform 0.2s var(--ease); }
.tw__chev svg { width: 14px; height: 14px; }
.tw[aria-expanded='true'] .tw__chev { transform: rotate(180deg); color: var(--torch); }

.tw {
  width: 100%;
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 7px 9px;
  background: transparent;
  border: 0;
  border-left: 2px solid transparent;
  text-align: left;
  transition: color 0.16s var(--ease), background 0.16s var(--ease), border-color 0.16s var(--ease), transform 90ms var(--ease-in);
}
.tw__l {
  flex: 1;
  min-width: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--mute);
}
.tw__s {
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.06em;
  color: var(--mute-dim);
  white-space: nowrap;
}
.tw__n {
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.06em;
  color: var(--mute-dim);
  flex: none;
}
/* marque sits a size up; series and chassis step down from it */
.tw--car .tw__l { font-size: 0.9375rem; color: var(--white); }
.tw--all .tw__l { font-size: 0.75rem; }
.tw--series .tw__l { font-size: 0.75rem; }
.tw--model .tw__l { font-family: var(--mono); font-weight: 500; font-size: 0.6875rem; letter-spacing: 0.1em; }
.tw:hover { background: var(--ink-3); }
.tw:hover .tw__l { color: var(--white); }
.tw[aria-pressed='true'] { border-left-color: var(--torch); background: var(--torch-dim); }
.tw[aria-pressed='true'] .tw__l { color: var(--torch); }
/* nothing catalogued under this branch yet — still selectable, the empty state
   invites the enquiry */
.tw--empty .tw__l, .tw--empty .tw__s, .tw--empty .tw__n { opacity: 0.42; }

/* ---- over the grid: item count, reset and sort */
.cat__bar {
  position: sticky;
  top: var(--header-h);
  z-index: 40;
  background: rgba(10, 10, 11, 0.9);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
  margin-bottom: clamp(20px, 2.4vw, 32px);
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-block: 12px;
}

.cat__rows { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.cat__row { display: flex; align-items: center; gap: 12px; min-width: 0; }
.cat__row[hidden] { display: none; }
/* White, not the muted grey these started as: at 10px uppercase mono they are
   the only thing naming the chip row beside them, and muted grey on the dark
   bar read as disabled rather than as a label. Same for CAR and the count. */
.cat__lab {
  flex: none; width: 64px;
  font-family: var(--mono); font-size: 0.625rem; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--white);
}

/* Both labels are <button>s so they can fold their group away on a phone. On
   desktop nothing folds, so they are stripped back to reading as plain labels —
   default cursor, no button chrome, and the chevron and selection summary that
   only make sense next to a collapsed group stay hidden. */
.cat__lab, .cat__rail-h {
  -webkit-appearance: none; appearance: none;
  background: none;
  text-align: left; cursor: default;
}
/* .cat__rail-h was a <p>: block-level, so its rule's border-bottom ran the full
   width of the rail. A button shrink-wraps, which would pull that rule in to
   the width of the word "Car" - so it is put back explicitly. */
.cat__rail-h { display: block; width: 100%; }
.cat__lab-sel, .cat__lab-chev { display: none; }

/* ---- the rail's part filters: service, category, style
   Down the rail these read as a list, not as chips: a block of notched buttons
   wrapping in a 272px column is a mosaic to pick a path out of, and the car
   tree directly above is already a list. Same row as .tw, so the whole column
   is one control - name on the left, its count on the right, the picked one
   marked by a torch rule down its edge. Nothing scrolls sideways, so the pager
   has nothing left to do either. */
.cat__rail .cat__rows { margin-top: 20px; gap: 4px; }
.cat__rail .cat__row { display: block; }
.cat__rail .chips__wrap { display: contents; }
.cat__rail .chips { display: block; flex: none; overflow: visible; padding-bottom: 16px; }
.cat__rail .chips__pager { display: none; }

.cat__rail .chip {
  width: 100%;
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 7px 9px;
  background: transparent;
  border: 0;
  border-left: 2px solid transparent;
  /* the fin notch belongs to a button; these are rows */
  clip-path: none;
  text-align: left;
}
.cat__rail .chip__l {
  flex: 1;
  min-width: 0;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--mute);
  white-space: normal;
}
.cat__rail .chip__n {
  position: static;
  flex: none;
  color: var(--mute-dim);
}
.cat__rail .chip:hover { background: var(--ink-3); border-color: transparent; }
.cat__rail .chip:hover .chip__l { color: var(--white); }
.cat__rail .chip[aria-pressed='true'] {
  background: var(--torch-dim);
  border-left-color: var(--torch);
  color: inherit;
}
.cat__rail .chip[aria-pressed='true'] .chip__l { color: var(--torch); }
.cat__rail .chip[aria-pressed='true'] .chip__n { color: var(--mute-dim); }
/* nothing catalogued behind it yet - set back, still pickable, exactly as the
   tree treats an empty branch */
.cat__rail .chip--empty { opacity: 0.42; }
.cat__rail .chip--empty:hover { opacity: 1; }

/* Every group folds, the tree included. Opened all at once they are ~90
   options - a scroll of their own before the first part is even on screen.
   Shut, the whole filter is four lines and one click deep; the label carries
   whatever is picked inside it so a folded group still reads. site.js decides
   what starts open (data-shut in the markup marks the long ones). */
.cat__rail .cat__lab,
.cat__rail .cat__rail-h {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 34px;
  padding-bottom: 8px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}
.cat__rail .cat__lab-t { flex: none; }
.cat__rail .cat__lab-sel {
  display: block;
  flex: 1;
  min-width: 0;
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.06em;
  text-transform: none;
  color: var(--torch);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cat__rail .cat__lab-chev {
  display: block;
  flex: none;
  width: 16px;
  height: 16px;
  margin-left: auto;
  color: var(--mute);
  transition: transform 0.2s var(--ease);
}
.cat__rail .cat__lab:hover, .cat__rail .cat__rail-h:hover { color: var(--torch); }
.cat__rail .cat__row.is-shut .chips__wrap,
.cat__rail.is-shut > .tree { display: none; }
.cat__rail .cat__row.is-shut .cat__lab-chev,
.cat__rail.is-shut > .cat__rail-h .cat__lab-chev { transform: rotate(-90deg); }

/* "All" pinned | the rest paging | the pager button */
.chips__wrap { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; }
.chip--all { flex: none; }

.chips {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  /* no scrollbar — the pager button is the affordance now. Keyboard and
     trackpad scrolling still work; this only hides the bar. */
  scrollbar-width: none;
  -ms-overflow-style: none;
  /* scroll-behavior stays at the default `auto` on purpose — the pager asks for
     a smooth scroll from JS and force-lands it if the browser silently skips
     the animation, which it cannot do if CSS is also mandating smooth. */
  flex: 1;
  min-width: 0;
}
.chips::-webkit-scrollbar { display: none; }

/* One button, far right, advances a page. On the last page it turns around and
   goes back to the start, so the row is a loop rather than a dead end. */
.chips__pager {
  flex: none;
  display: grid;
  place-items: center;
  width: 36px;
  align-self: stretch;
  background: var(--ink-3);
  border: 1px solid var(--line);
  color: var(--mute);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%);
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease), color 0.18s var(--ease), transform 90ms var(--ease-in);
}
.chips__pager[hidden] { display: none; }
.chips__pager svg { width: 16px; height: 16px; transition: transform 0.2s var(--ease); }
.chips__pager:hover { background: var(--torch); border-color: var(--torch); color: var(--ink); }
.chips__pager.is-rewind svg { transform: rotate(180deg); }

.chip {
  flex: none;
  position: relative;
  display: flex;
  align-items: center;
  padding: 8px 13px;
  background: var(--ink-3);
  border: 1px solid var(--line);
  /* same fin notch as the buttons, scaled down */
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%);
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease), color 0.18s var(--ease), transform 90ms var(--ease-in);
}
.chip[hidden] { display: none; }
.chip__l {
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  white-space: nowrap;
}
/* live result count, written by the cascade. Empty on the "All" chip. */
.chip__n {
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.06em;
  color: var(--mute-dim);
  position: absolute;
  top: 4px; right: 6px;
}
.chip__n:empty { display: none; }
/* An option with nothing behind it yet. It stays selectable - picking it is a
   legitimate question with a legitimate answer - but it is set back so the
   stocked options read first. Its count renders as a real 0 rather than being
   suppressed, which is the point of showing it at all. */
.chip--empty { opacity: 0.45; }
.chip--empty:hover { opacity: 1; }
.chip--empty .chip__l { font-weight: 600; }

.chip:hover { border-color: var(--line-strong); background: var(--ink-4); }
.chip[aria-pressed='true'] { background: var(--torch); border-color: var(--torch); color: var(--ink); }
.chip[aria-pressed='true'] .chip__n { color: rgba(10, 10, 11, 0.6); }

.cat__reset {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--mute);
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  transition: color 0.18s var(--ease), border-color 0.18s var(--ease), transform 90ms var(--ease-in);
}
.cat__reset svg { width: 12px; height: 12px; }
.cat__reset:hover:not([disabled]) { color: var(--torch); border-color: var(--torch); }
/* visible but inert while nothing is filtered, so the control's position is
   learned before it is needed rather than appearing from nowhere */
.cat__reset[disabled] { opacity: 0.38; cursor: default; }
.cat__reset[hidden] { display: none; }

.cat__count {
  flex: none;
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--white);
}

/* count + reset + sort, one line above the chip rows so the rows get the
   full width — with 33 categories and 41 styles they need it */
.cat__tools {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-bottom: 2px;
}
.cat__tools .cat__count { margin-right: auto; }
.cat__sort { position: relative; display: inline-flex; align-items: center; }
.cat__sort select {
  appearance: none;
  -webkit-appearance: none;
  padding: 9px 34px 9px 14px;
  background: var(--ink-3);
  border: 1px solid var(--line);
  color: var(--white);
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%);
  cursor: pointer;
  transition: border-color 0.18s var(--ease), background 0.18s var(--ease);
}
.cat__sort select:hover { border-color: var(--line-strong); background: var(--ink-4); }
.cat__sort svg { position: absolute; right: 12px; width: 15px; height: 15px; color: var(--mute); pointer-events: none; }
.cat__sort option { background: var(--ink-3); color: var(--white); }

/* ============================================================ phone
   Below 900px the whole filter panel becomes a full-screen sheet behind one
   Filter button. Three stacked horizontal scrollers is no way to shop on a
   phone; inside the sheet the chip rows wrap and the car tree stays vertical,
   which is what it wants to be anyway. */
@media (max-width: 900px) {
  /* Block, not grid. A grid item's containing block is its own grid area, and a
     sticky box can never leave its containing block - .cat__mbar was the only
     in-flow item in an auto-sized row (.cat__panel is position:fixed here), so
     that row was exactly as tall as the bar and its sticky travel was 0px. The
     Filter button scrolled away after ~65px and never came back, over a list
     ~10,900px long, and below 900px that sheet is the only filtering UI there
     is. Under block layout the containing block becomes .cat__in itself, so the
     bar travels the whole list. Every grid-column/grid-row left on the children
     is inert once the container stops being a grid. */
  .cat__in { display: block; }

  /* the sticky handle */
  .cat__mbar {
    margin-bottom: 14px; /* replaces the grid gap dropped above */
    position: sticky;
    top: var(--header-h);
    z-index: 41;
    display: flex;
    align-items: center;
    gap: 14px;
    padding-block: 10px;
    background: rgba(10, 10, 11, 0.92);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--line);
  }
  .cat__open {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    min-height: 44px;
    padding: 0 16px;
    background: var(--ink-3);
    border: 1px solid var(--line-strong);
    color: var(--white);
    font-family: var(--display);
    font-weight: 700;
    font-size: 0.8125rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    clip-path: polygon(0 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%);
  }
  .cat__open svg { width: 17px; height: 17px; }
  .cat__badge {
    display: grid;
    place-items: center;
    min-width: 20px; height: 20px;
    padding-inline: 5px;
    background: var(--torch);
    color: var(--ink);
    font-family: var(--mono);
    font-size: 0.6875rem;
  }
  .cat__badge[hidden] { display: none; }
  .cat__mbar .cat__count { font-size: 0.75rem; }

  /* the sheet itself */
  .cat__scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 88;
    background: rgba(10, 10, 11, 0.72);
  }
  .cat__scrim[hidden] { display: none; }
  .cat__panel {
    display: flex;
    flex-direction: column;
    position: fixed;
    inset: 0;
    z-index: 89;
    background: var(--ink);
    transform: translateY(100%);
    visibility: hidden;
    transition: transform 0.28s var(--ease), visibility 0.28s;
  }
  .cat__panel.open { transform: none; visibility: visible; }

  .cat__sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex: none;
    padding: 14px var(--pad-x);
    border-bottom: 1px solid var(--line);
  }
  .cat__sheet-h {
    font-family: var(--display); font-weight: 700; font-size: 1rem;
    letter-spacing: 0.06em; text-transform: uppercase;
  }
  .cat__sheet-x {
    display: grid; place-items: center;
    width: 44px; height: 44px;
    margin-right: -10px;
    background: transparent; border: 0; color: var(--white);
  }
  .cat__sheet-x svg { width: 20px; height: 20px; }

  /* rail + chip rows share ONE scroll inside the sheet — two nested scroll
     areas on a phone is a trap you can't get out of with a thumb */
  .cat__sheet-body {
    display: block;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  .cat__rail, .cat__bar {
    grid-column: auto; grid-row: auto;
    position: static;
    align-self: stretch;
    width: auto;
    max-height: none;
    overflow: visible;
    border: 0;
    margin: 0;
    padding: 16px var(--pad-x) 0;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .cat__rail { border-bottom: 1px solid var(--line); padding-bottom: 14px; }
  .cat__bar  { padding-bottom: 24px; }
  .cat__rail-h { font-size: 0.6875rem; }

  /* touch-sized tree rows */
  .tw { min-height: 44px; padding: 9px 10px; }
  .tw__l { font-size: 0.875rem; }
  .tw--car .tw__l { font-size: 1rem; }
  .tw--series .tw__l, .tw--all .tw__l { font-size: 0.8125rem; }
  .tw--model .tw__l { font-size: 0.8125rem; }
  .tw__s, .tw__n { font-size: 0.6875rem; }

  /* chip rows wrap instead of scrolling sideways */
  .cat__rows { gap: 14px; }
  .cat__row { display: block; }

  /* Every group opened at once put ~40 chips in the sheet before the visitor had
     chosen anything. On a phone each label becomes the group's toggle: it folds
     the group away and carries whatever is currently picked, so a closed sheet
     reads as a summary of the filter rather than a wall. */
  .cat__lab, .cat__rail-h {
    display: flex; align-items: center; gap: 10px;
    width: 100%; min-height: 44px;
    margin-bottom: 7px; font-size: 0.6875rem;
    cursor: pointer;
  }
  .cat__lab-t { flex: none; }
  .cat__lab-sel {
    display: block; flex: 1; min-width: 0;
    font-size: 0.6875rem; letter-spacing: 0.04em;
    text-transform: none; color: var(--torch);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .cat__lab-chev {
    display: block; flex: none; width: 18px; height: 18px;
    color: var(--mute); transition: transform 0.2s var(--ease);
  }
  /* The rail holds every group now, so shutting it must fold the car tree
     alone - hence the child combinators; the three chip groups carry their own
     .is-shut. */
  .cat__row.is-shut .chips__wrap,
  .cat__rail.is-shut > .tree { display: none; }
  .cat__row.is-shut .cat__lab-chev,
  .cat__rail.is-shut > .cat__rail-h .cat__lab-chev { transform: rotate(-90deg); }
  /* nothing scrolls sideways here, so the pager has nothing to do — site.js
     hides it on its own once the row stops overflowing */
  .chips__wrap { display: contents; }
  .chips { display: flex; flex-wrap: wrap; overflow: visible; padding-bottom: 0; gap: 8px; }
  .chip--all { margin-bottom: 8px; }
  .chip { min-height: 40px; padding: 9px 14px; }
  .chip__l { font-size: 0.8125rem; }
  .chip__n { font-size: 0.6875rem; top: 3px; right: 5px; }
  /* the sheet's groups are the same list as the desktop rail's, sized for a
     thumb rather than a cursor */
  .cat__rail .chip { min-height: 44px; align-items: center; padding: 9px 10px; }
  .cat__rail .chip--all { margin-bottom: 0; }
  .cat__rail .chip__l { font-size: 0.875rem; }
  .cat__rail .chip__n { font-size: 0.6875rem; }

  /* tools move under the rows, sort full width */
  .cat__bar .cat__tools {
    order: 2;
    flex-wrap: wrap;
    gap: 10px;
    padding-top: 16px;
  }
  /* the sheet's footer already carries Reset and the live count */
  .cat__bar .cat__count, .cat__bar .cat__reset { display: none; }
  .cat__sort, .cat__sort select { width: 100%; }
  .cat__sort select { min-height: 44px; }

  .cat__sheet-foot {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 10px;
    flex: none;
    padding: 12px var(--pad-x) calc(12px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--line);
    background: var(--ink-2);
  }
  .cat__sheet-foot .btn { justify-content: center; min-height: 46px; }
}

/* ------------------------------------------------------------ grid */

.cat__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
}
/* an author `display` beats the UA sheet's [hidden] rule, so say it explicitly —
   the car picker takes the grid's place and both were rendering */
.cat__grid[hidden] { display: none; }
@media (max-width: 1280px) { .cat__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .cat__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .cat__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; } }
/* one card per row on a phone — at 390px two columns leave a 167px photo, which
   is too small to judge a bumper by */
@media (max-width: 560px)  { .cat__grid { grid-template-columns: minmax(0, 1fr); gap: 14px; } }

.card {
  display: flex;
  flex-direction: column;
  background: var(--ink-2);
  border: 1px solid var(--line);
  overflow: hidden;
  transition: border-color 0.22s var(--ease), transform 0.22s var(--ease);
}
.card:hover { border-color: var(--torch); transform: translateY(-3px); }
.card[hidden] { display: none; }

.card__shot { padding: 0; display: block; position: relative; overflow: hidden; background: var(--ink-4); }
/* 4:3 to match the car cards, which draw from the same pool of part photos -
   with object-fit:contain a single image cannot fill a 1:1 and a 4:3 box, so
   two ratios would mean permanent letterboxing in one grid or the other. The
   img width/height attributes already declare 4:3. */
.card__shot img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  transition: transform 0.45s var(--ease);
}
.card:hover .card__shot img { transform: scale(1.06); }
.card__shot::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 62%, rgba(10, 10, 11, 0.55));
  pointer-events: none;
}

.card__body {
  padding: 14px 15px 17px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  flex: 1;
}
.card__plat {
  font-family: var(--plate);
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--torch);
}
/* the part code above the title — 10px is fine on a monitor, not on a phone */
@media (max-width: 767px) {
  .card__plat { font-size: 0.875rem; }
  .card__h { font-size: 1rem; }
}
/* A part name is a code as much as a phrase, so the card sets it in the
   monospace rather than the body sans it used to inherit. The card and the part
   page are the only two places this happens. */
.card__h {
  font-family: var(--mono);
  font-weight: 600;
  font-size: 0.9375rem;
  line-height: 1.2;
  letter-spacing: -0.01em;
}
/* part type left, price right — the price is optional, so the row is a
   two-column flex with a spacer rather than a float that collapses */
.card__meta {
  margin-top: auto;
  padding-top: 6px;
  font-family: var(--mono);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  font-size: var(--t-xs);
  color: var(--mute-dim);
}
.card__price {
  flex: none;
  font-family: var(--mono);
  letter-spacing: 0.02em;
  color: var(--paper);
}

.cat__empty {
  padding: clamp(40px, 6vw, 72px) 0;
  text-align: center;
  color: var(--mute);
  font-size: var(--t-md);
}

/* ------------------------------------------------------------ pagination */

.pager {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-top: clamp(36px, 5vw, 60px);
}
.pager[hidden] { display: none; }

.pager__btn {
  min-width: 44px;
  height: 44px;
  padding: 0 10px;
  display: grid;
  place-items: center;
  background: var(--ink-3);
  border: 1px solid var(--line);
  color: var(--mute);
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.9375rem;
  letter-spacing: 0.02em;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease), color 0.18s var(--ease);
}
.pager__btn:hover { border-color: var(--line-strong); color: var(--white); background: var(--ink-4); }
.pager__btn[aria-current='true'] {
  background: var(--torch);
  border-color: var(--torch);
  color: var(--ink);
}
.pager__btn:disabled { opacity: 0.32; pointer-events: none; }
.pager__edge svg { width: 18px; height: 18px; }

/* ------------------------------------------------------------ car picker
   What the grid shows before a car is chosen. Same tiles as the rail, but
   sized to be looked at rather than scanned. */

.cat__cars[hidden] { display: none; }
/* [hidden] is a user-agent display:none and loses to any author display, which
   is why every box that is toggled has to turn itself off explicitly - the same
   reason .chip[hidden] and .cat__row[hidden] exist. Without these the picker
   drew both of its steps at once. */
.cat__cars-grid[hidden] { display: none; }
.carcard[hidden] { display: none; }
.cat__cars-all[hidden] { display: none; }
.cat__cars-h {
  margin-bottom: clamp(16px, 2vw, 24px);
  color: var(--mute);
  font-size: var(--t-sm);
}
/* The row between the two picker steps: back to the makes on the left, and the
   way past the second step on the right for anyone who wants the marque whole
   rather than one of its cars. */
.cat__cars-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: clamp(16px, 2vw, 24px);
}
.cat__cars-back,
.cat__cars-all {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: 0;
  padding: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--t-sm);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.18s var(--ease);
}
.cat__cars-back { color: var(--mute); }
.cat__cars-back:hover { color: var(--white); }
.cat__cars-all { color: var(--torch); }
.cat__cars-back svg,
.cat__cars-all svg { width: 16px; height: 16px; transition: transform 0.25s var(--ease); }
.cat__cars-back:hover svg { transform: translateX(-4px); }
.cat__cars-all:hover svg { transform: translateX(4px); }

.cat__cars-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gap);
}

.carcard {
  position: relative;
  display: block;
  padding: 0;
  overflow: hidden;
  background: var(--ink-2);
  border: 1px solid var(--line);
  text-align: left;
  transition: border-color 0.22s var(--ease), transform 0.22s var(--ease);
}
.carcard:hover { border-color: var(--torch); transform: translateY(-3px); }

.carcard__media { display: block; position: relative; overflow: hidden; background: var(--ink-4); }
.carcard__media img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: contain;
  transition: transform 0.45s var(--ease);
}
.carcard:hover .carcard__media img { transform: scale(1.06); }
/* scrim so the label block reads over any photo */
.carcard__media::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    transparent 24%,
    rgba(10, 10, 11, 0.34) 52%,
    rgba(10, 10, 11, 0.74) 76%,
    rgba(10, 10, 11, 0.93) 100%);
}

.carcard__body {
  position: absolute;
  inset: auto 0 0 0;
  display: block;
  padding: 13px 15px;
}
.carcard__h {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: -0.01em;
  color: var(--white);
}
/* The marque and the count share one quiet line under the name. The series used
   to sit above it in torch orange, which on a photograph - rather than on the
   page - disappeared into a dark car body at 10px. Down here it has the label
   block's own backing behind it, and the name gets the top of the block to
   itself. */
.carcard__meta {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  margin-top: 5px;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  color: var(--mute);
}
.carcard__marque { text-transform: uppercase; }
/* Drawn rather than written, so a screen reader announces "BMW 3 Series, 19
   parts" instead of reading a pipe out, and so it only exists when there is a
   marque to separate - a brand-level card has none. */
.carcard__marque + .carcard__count::before {
  content: '';
  display: inline-block;
  width: 1px;
  height: 0.7em;
  margin: 0 8px;
  background: var(--line-strong);
  vertical-align: baseline;
}
.carcard__meta b { color: var(--white); font-weight: 500; }
/* nothing catalogued for it yet — still clickable, the empty state invites
   the enquiry, same as the rail */
.carcard--empty { opacity: 0.55; }

@media (max-width: 1280px) { .cat__cars-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .cat__cars-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .cat__cars-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; } }
@media (max-width: 560px) {
  .cat__cars-grid { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .carcard__h { font-size: 1.35rem; }
}

/* Phone type scale — see the note in shell.css. Original values x 0.9. */
@media (max-width: 767px) {
  .chero__h { font-size: clamp(1.8rem, 1.08rem + 2.7vw, 3.15rem); }
  .chero__n { font-size: clamp(2.7rem, 1.44rem + 5.4vw, 5.85rem); }
}

/* ============================================================ press
   .chip and .tw are wrapped in hover:hover because both sit inside a scroller —
   .chips scrolls horizontally, .tw lives in the vertical .cat__rail. Once the
   document touchstart listener exists, iOS applies :active on touchdown and
   holds it through a drag, so a chip used to fling the row sideways would stay
   depressed for the whole gesture. On touch both already answer instantly via
   aria-pressed, so nothing is lost. The pager and reset are not in a scroller.

   .cat__sort select is deliberately absent: Firefox holds :active on a <select>
   for as long as its native popup is open, so it would sit depressed for the
   entire menu interaction. Do not add it back for symmetry. */
@media (hover: hover) {
  .chip:active,
  .tw:active { transform: translateY(1px); }
}
.chips__pager:active,
.cat__reset:not([disabled]):active { transform: translateY(1px); }
