/* Catalog — /paddles/browse only.

   The faceted grid (.pn-*): filter rail, results toolbar, card grid, card
   metric bars, pagination, and the static brand index the generator writes
   below the app. Was the first third of paddle-finder.css; split out because
   all 514 detail pages were downloading 29 KB of it to render a page that has
   no grid.

   Needs, and does not define: .pn-tag (paddle-shared.css) and --pt-tray-h, which
   paddle-tray.js sets on <body> and .pn-rail parks its sheet above.

   Geometry, type scale and interaction timing follow the handoff prototype;
   every colour comes from style.css's :root. No new tokens are defined here.

   Breakpoint is 940px, not the site's 900px nav breakpoint. They are unrelated
   questions: 900px is where six nav links stop fitting on one line, 940px is
   where a 264px rail plus a 236px card minimum stops leaving room for two
   cards. Moving either to match the other breaks the one it was measured for. */

/* ==========================================================================
   Catalog — /paddles/browse
   ========================================================================== */

.pn-app {
  margin-top: var(--space-2);
}

/* Search + collection chips. Sits under the hero copy, above the split. */
.pn-tools {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  margin-bottom: var(--space-3);
}

/* The pill owns the border and the focus ring, not the <input> — same pattern
   as .global-search in style.css. That is what lets an optional magnifier icon
   and the clear button sit inside the box without the input needing to reserve
   left padding for an icon that may not be rendered. */
/* Search + the rent callout, side by side. Both wrap to their own line below
   ~640px of available width, so the callout never squeezes the search field. */
.pn-tools-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.85rem;
}

.pn-search {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  /* 24rem, not the old 30rem. The clear button pins to the pill's right edge,
     so every extra rem of pill is another rem between the text you typed and
     the control that clears it — at 30rem that gap was 402px. Narrower also
     leaves the row width for the rent callout beside it. */
  flex: 1 1 18rem;
  max-width: 24rem;
  /* A flex item's default min-width is `auto` — its CONTENT's minimum, which it
     refuses to shrink below no matter what flex-shrink says. That was fine while
     the clear button was a 26px icon box, but it now sizes to the word "Clear"
     (51.2px), and at a 320px viewport the pill's content minimum grew 16.4px
     past the 264px column every other block on the page sits in. The giveaway
     was that it only misaligned once you TYPED — the button is display:none
     until then, so an empty field measured correctly. There is no page-level
     scrollbar (28px of gutter absorbs it), so it reads as the search box being
     crooked rather than as a break. `min-width: 0` lets flex-shrink do its job;
     .pn-search-input already carries the same fix for the same reason. */
  min-width: 0;
  min-height: 46px;
  padding: 0 0.75rem;
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
  transition: border-color 0.15s var(--ease);
}

.pn-search:focus-within {
  border-color: var(--bay);
  outline: 3px solid var(--bay);
  outline-offset: 2px;
}

.pn-search svg {
  flex: none;
  width: 16px;
  height: 16px;
  color: var(--ink-faint);
}

.pn-search-input {
  flex: 1;
  min-width: 0;
  border: 0;
  padding: 0.6rem 0;
  background: transparent;
  font-family: var(--font-body);
  font-size: 0.9375rem;
  color: var(--ink);
}

.pn-search-input::placeholder {
  color: var(--ink-faint);
}

/* See .pn-search:focus-within — the pill draws the ring, so the input must not
   draw a second one inside it. This beats the sitewide :focus-visible rule by
   coming later in the cascade at equal specificity. */
.pn-search-input:focus,
.pn-search-input:focus-visible {
  outline: none;
}

/* Sized to its own label. This was `width: 26px; font-size: 1rem` — an icon
   button's box — while the markup has always said the word "Clear", which needs
   39px. The text overflowed the box by 13px, so it sat outside its own hit area
   and its hover pill clipped the word. Auto width + padding makes the button
   the shape of what it contains. */
.pn-search-clear {
  flex: none;
  display: grid;
  place-items: center;
  height: 26px;
  padding: 0 0.55rem;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--ink-faint);
  font-family: var(--font-display);
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}

.pn-search-clear:hover {
  background: var(--fog-dim);
  color: var(--ink);
}

.pn-collections {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* Commission disclosure — the .affiliate-note skin for the catalog. Sentence
   and class come from assets/paddle-cta.js; only the look is here.

   The disclosure OPENS the results — it sits between the toolbar and the first
   row of cards, so it needs air below it, separating it from the grid.

   Small and muted, which is what this line was always described as ("one
   compact muted line") but never actually got: the catalog is the one surface
   that doesn't load a sheet defining the base, so it rendered at full body size
   while every other surface's ran at 11–12px. At body size the shared sentence
   is a three-line slab above the first card. */
.pn-results [data-role="disclosure"] .affiliate-note--grid {
  margin: 0 0 var(--space-2);
  max-width: 78ch;
  font-family: var(--font-body);
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--ink-faint);
}

.pn-results [data-role="disclosure"] .affiliate-note--grid a {
  color: var(--ink-soft);
}

.pn-chip {
  padding: 0.42em 0.85em;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--paper);
  font-family: var(--font-display);
  font-size: 0.78125rem;
  font-weight: 700;
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease), color 0.15s var(--ease),
    box-shadow 0.15s var(--ease);
}

.pn-chip:hover {
  border-color: var(--bay);
  color: var(--bay-deep);
  box-shadow: 0 2px 8px -4px rgba(var(--ink-rgb), 0.35);
}

/* A collection is a saved filter set, so the active one has to read as a
   *state*, not a hover. Solid teal, and the border goes with it so the pill
   doesn't gain a visible seam. */
.pn-chip.is-on {
  background: var(--bay);
  border-color: var(--bay);
  color: var(--fog);
}

/* ---------- layout split ---------- */

.pn-layout {
  display: grid;
  grid-template-columns: 264px minmax(0, 1fr);
  gap: 28px;
  align-items: start;
}

/* min-width:0 on the results column, without which a long paddle name inside
   the grid sets a min-content floor and pushes the whole layout wider than the
   container instead of wrapping. */
.pn-results {
  min-width: 0;
}

/* ---------- filter rail ---------- */

/* --header-h is kept live by nav.js's ResizeObserver on .site-header, so the
   rail stays clear of the sticky header even when it wraps to two lines at
   900-983px. The 130px fallback covers the frame before that JS runs. */
.pn-rail {
  position: sticky;
  top: calc(var(--header-h, 130px) + 12px);
  /* The tray is fixed to the viewport, so body's padding-bottom does not move
     it off a sticky element — the rail has to reserve that height itself or
     its last two facets sit behind the tray with no way to scroll to them. */
  max-height: calc(100vh - var(--header-h, 130px) - var(--pt-tray-h, 0px) - 24px);
  overflow-y: auto;
  padding: 0.85rem 1rem 1rem;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
}

.pn-rail-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.7rem;
  padding-bottom: 0.55rem;
  border-bottom: 1px solid var(--rule-08);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* Clay, not teal: "Reset all" undoes work, and the site reserves teal for
   links that take you somewhere. It only appears when something is active, so
   it is never a permanently-shouting control. */
.pn-reset {
  padding: 0;
  border: 0;
  background: transparent;
  font-family: var(--font-display);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  color: var(--poppy-deep);
  cursor: pointer;
}

.pn-reset:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Ten facets stack in here, so every rem of per-facet chrome is paid ten times:
   at 0.75rem the padding alone was 240px of the rail's 2075px scroll height. */
.pn-facet {
  padding: 0.55rem 0;
  border-top: 1px solid var(--rule-08);
}

.pn-facet:first-of-type {
  border-top: 0;
  padding-top: 0;
}

/* Works as a plain heading or as a <summary> — if the facet is a <details>,
   the default triangle would collide with the caps label. */
.pn-facet-head {
  margin: 0 0 0.35rem;
  font-family: var(--font-display);
  font-size: 0.78125rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--ink);
  cursor: default;
}

summary.pn-facet-head {
  cursor: pointer;
  list-style: none;
}

summary.pn-facet-head::-webkit-details-marker {
  display: none;
}

/* No gap: .pn-opt's min-height already separates the rows, and a gap on top of
   it was paying twice for the same air across ~45 visible rows. */
.pn-opts {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The row is a <label>, so the whole line including the count is a click
   target for the checkbox. */
/* 26px is a deliberate floor, not a free knob. The whole row is the label, so
   it is a full-width click target and only its HEIGHT is at risk; WCAG 2.2
   2.5.8 (Target Size, Minimum, AA) wants 24×24 CSS px, so 26px tightens the
   list while keeping headroom over that limit. Do not take this below 24. */
.pn-opt {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 26px;
  cursor: pointer;
  color: var(--body-ink);
}

/* accent-color rather than a hand-rolled ::before checkmark: it gives the
   design's filled-teal box with a light tick, keeps the native focus ring the
   rest of the site uses, and cannot desync from the input's real state. */
.pn-opt input[type="checkbox"] {
  flex: none;
  width: 15px;
  height: 15px;
  margin: 0;
  accent-color: var(--bay);
  cursor: pointer;
}

.pn-opt-label {
  flex: 1;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 0.8125rem;
  line-height: 1.35;
}

.pn-opt-count {
  flex: none;
  font-family: var(--font-mono);
  font-size: 0.65625rem;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

/* Greyed, never hidden. A zero option that vanishes makes the facet list
   reflow under the cursor on every checkbox click, and it hides the fact that
   the combination you just built has no widebody paddles left in it. */
.pn-opt.is-zero {
  color: var(--ink-faint);
}

.pn-opt.is-zero .pn-opt-count {
  opacity: 0.6;
}

.pn-more {
  margin-top: 0.35rem;
  padding: 0;
  border: 0;
  background: transparent;
  font-family: var(--font-display);
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--bay);
  cursor: pointer;
}

.pn-more:hover {
  color: var(--bay-deep);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* The lab-coverage footnote. It is the rail's only prose and it is load-
   bearing honesty — 133 of 486 paddles have no bench numbers, and the facet
   counts are meaningless without it. */
.pn-rail-note {
  margin: 1rem 0 0;
  padding-top: 0.75rem;
  border-top: 1px solid var(--rule-08);
  font-family: var(--font-body);
  font-size: 0.71875rem;
  line-height: 1.5;
  color: var(--ink-faint);
}

/* Desktop has no toggle and no sheet close — both are the bottom-sheet's
   controls and only exist below 940px. */
.pn-rail-toggle,
.pn-sheet-close {
  display: none;
}

/* ---------- results toolbar ---------- */

.pn-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.9rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--rule-08);
}

.pn-count {
  flex: none;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.pn-count strong {
  color: var(--ink);
  font-weight: 700;
}

.pn-active {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  min-width: 0;
}

.pn-active-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  padding: 0.22em 0.5em 0.22em 0.7em;
  border: 0;
  border-radius: 999px;
  background: var(--bay-tint);
  font-family: var(--font-display);
  font-size: 0.71875rem;
  font-weight: 700;
  color: var(--bay-deep);
  cursor: pointer;
}

.pn-active-chip:hover {
  background: var(--bay);
  color: var(--fog);
}

/* margin-left:auto rather than justify-content:space-between, so the sort
   stays hard right whether or not there are active-filter chips between it and
   the count. */
.pn-sort {
  margin-left: auto;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
  background: var(--paper);
  font-family: var(--font-display);
  font-size: 0.8125rem;
  color: var(--ink);
  cursor: pointer;
}

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

/* auto-FILL, not auto-fit. auto-fit collapses the empty tracks, so a filter
   that leaves one result renders one card stretched across the full row — the
   same trap paddles.css already documents at .pg-grid. */
.pn-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(236px, 1fr));
  gap: 14px;
}

/* position:relative is load-bearing: it is the containing block for
   .pn-card-link's stretched overlay and for the compare toggle. */
.pn-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 14px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  transition: transform 0.15s var(--ease), border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

/* Selected-into-the-compare-tray ring. The catalog was the one surface whose
   Compare button changed only itself, so a card 400px up the page gave no sign
   it was in the comparison — the detail page's and the head-to-head's cards
   have always rung. Same signal, this sheet's border token: the .pd-cc-card
   ring is --cc-teal because that card owns a page-local palette, and --bay is
   the same colour in the site's own. */
.pn-card.is-comparing {
  border-color: var(--bay);
}

/* :focus-within as well as :hover — the whole card is one tab stop for the
   stretched link, so a keyboard user must see the same lift a mouse user does
   or the card gives no feedback at all. */
.pn-card:hover,
.pn-card:focus-within {
  transform: translateY(-2px);
  border-color: rgba(var(--bay-rgb), 0.45);
  box-shadow: 0 4px 14px -4px rgba(var(--ink-rgb), 0.22);
}

/* The catalog card's art well. It was empty for a long time — the handoff's
   118px silhouette was pulled because there were no product photos. There are
   now: assets/paddle-images/<id>.png, a face-on transparent cutout per paddle
   (see PADDLE_IMAGES_SETUP.md), so the well holds THIS paddle, not a drawing of
   a generic one.

   STATIC positioning is mandatory here and was the original bug: when this well
   was position:relative it became the containing block for
   .pn-card-link::after, which clipped the whole-card stretched link to the top
   of the card and left ~3/4 of it unclickable. Leave it static — the image
   sits below the ::after overlay in the stacking order, so a click anywhere on
   it still follows the card link to the detail page. */
.pn-card-img {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 150px;
  margin-bottom: 0.15rem;
  background: radial-gradient(120% 115% at 50% 32%, rgba(var(--ink-rgb), 0.045), transparent 68%);
  border-radius: var(--radius-s);
}
.pn-card-img-el {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 3px 8px rgba(var(--ink-rgb), 0.16));
}

.pn-card-brand {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--optic-text);
}

.pn-card-name {
  margin: 0;
  font-family: var(--font-headline);
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--ink);
}

.pn-card-link {
  color: inherit;
  text-decoration: none;
}

/* THE STRETCHED LINK. The card is not wrapped in an <a> because it contains
   two other independent controls (buy, compare) and nested interactive content
   is invalid HTML and unusable with a screen reader. Instead this pseudo-
   element covers the card, so the accessible name stays "11SIX24 Vaquero" and
   not the card's entire text content. */
.pn-card-link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.pn-card-link:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}

.pn-card-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

.pn-card-specs {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  color: var(--ink-faint);
}

/* ---------- card metric bars ---------- */

/* The three bars share ONE grid so their tracks line up, which means the grid
   lives on the container and each row is display:contents. A per-row grid with
   a fixed 42px label column let "FORGIVENESS" (11 characters of mono) overrun
   into its own bar; sizing the column to `auto` only works if every row is
   measured together, otherwise each row picks its own label width and the three
   tracks start at three different x positions. */
.pn-bars {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.3rem 0.4rem;
}

.pn-bar {
  display: contents;
}

.pn-bar-label {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.pn-track {
  height: 5px;
  border-radius: 999px;
  background: rgba(var(--ink-rgb), 0.1);
  overflow: hidden;
}

/* Width is set inline by the renderer from a banded percentile. A bar POSITION
   is a legal use of the licensed lab data; a printed percentile is not — see
   PADDLE_DATA_SETUP.md. .pn-bar-word carries the word instead. */
/* display:block is load-bearing. A <span> is inline by default, and an inline
   box ignores both width and height — every bar on all 486 cards rendered at
   zero width, which read as "we measured this paddle at nothing". */
.pn-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
}

.pn-fill--spin {
  background: var(--bay);
}

.pn-fill--power {
  background: var(--poppy);
}

/* Control's site identity is teal — the Control play-style chip is
   --bay-tint/--bay-deep — so its bar is teal-family too. --bay-deep, a shade
   down from Spin's --bay, so the two teals stay distinct across the Power bar
   that sits between them. */
.pn-fill--control {
  background: var(--bay-deep);
}

.pn-fill--forgiveness {
  background: var(--optic-text);
}

.pn-bar-word {
  font-family: var(--font-display);
  font-size: 0.65625rem;
  font-weight: 700;
  color: var(--ink-soft);
  white-space: nowrap;
}

/* 133 paddles have no bench numbers. Saying so in italics beats three empty
   tracks, which read as "measured at zero". */
.pn-nolab {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.6875rem;
  font-style: italic;
  line-height: 1.4;
  color: var(--ink-faint);
}

/* ---------- card foot ---------- */

/* margin-top:auto pins the price row to the bottom, so prices line up across a
   row of cards whose names wrap to different heights. */
/* Wraps, because the foot holds three things of wildly unequal width — a
   4-character price, a button whose label runs from "Buy on Amazon" to
   "Visit brand site", and an approval note. Held on one nowrap line at a 236px
   card, the button was squeezed to 67px and its label broke mid-word. The note
   is allowed to fall to its own line instead. */
.pn-card-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.35rem 0.5rem;
  margin-top: auto;
  padding-top: 0.55rem;
  border-top: 1px solid var(--rule-08);
}

/* The note takes the full width once it wraps, so it reads as a caption under
   the price row rather than as a third column fighting for space. */
.pn-card-foot .pn-note {
  flex: 1 0 100%;
}

.pn-buy {
  white-space: nowrap;
}

.pn-price {
  font-family: var(--font-mono);
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--ink);
}

/* z-index above .pn-card-link::after, or the stretched overlay swallows the
   click and every buy button navigates to the detail page instead of the
   vendor — silently costing every affiliate click on the catalog. */
.pn-buy {
  position: relative;
  z-index: 2;
  padding: 0.3em 0.7em;
  border: 1px solid rgba(var(--bay-rgb), 0.4);
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: 0.71875rem;
  font-weight: 700;
  color: var(--bay-deep);
  text-decoration: none;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), color 0.15s var(--ease);
}

.pn-buy:hover {
  background: var(--bay);
  border-color: var(--bay);
  color: var(--fog);
  text-decoration: none;
}

/* The "· affiliate link" note, which is conditional on the link actually being
   one (see assets/affiliate-links.js).

   --ink-soft at 0.6875rem, NOT --ink-faint at 0.625rem: this note is a legal
   disclosure sitting next to a commissioned buy button, so it has to be
   readable as real text. --ink-faint measures 2.90:1 on --paper and 2.69:1 on
   --fog — both fail WCAG AA (4.5:1). --ink-soft is 5.94:1 and 5.51:1. This is
   the same call, for the same reason, as the one recorded at style.css:1610. */
.pn-note {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

/* Same z-index reasoning as .pn-buy. */
.pn-compare {
  position: absolute;
  z-index: 2;
  top: 8px;
  right: 8px;
  padding: 0.28em 0.6em;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: rgba(var(--fog-rgb), 0.92);
  font-family: var(--font-display);
  font-size: 0.65625rem;
  font-weight: 700;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), color 0.15s var(--ease);
}

.pn-compare:hover {
  border-color: var(--bay);
  color: var(--bay-deep);
}

.pn-compare.is-on {
  background: var(--bay);
  border-color: var(--bay);
  color: var(--fog);
}

/* ---------- empty state & pagination ---------- */

.pn-empty {
  padding: var(--space-3) var(--space-2);
  border: 1px dashed var(--rule-25);
  border-radius: var(--radius-m);
  text-align: center;
  color: var(--ink-soft);
}

.pn-empty h2 {
  margin: 0 0 0.4rem;
  font-size: 1.375rem;
}

.pn-empty p {
  margin: 0 0 1.1rem;
  font-size: 0.875rem;
}

.pn-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  margin-top: var(--space-3);
}

.pn-page {
  min-width: 36px;
  min-height: 36px;
  padding: 0 0.55rem;
  border: 1px solid var(--line-strong);
  border-radius: 8px;
  background: var(--paper);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--ink);
  cursor: pointer;
  font-variant-numeric: tabular-nums;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease), color 0.15s var(--ease);
}

.pn-page:hover {
  border-color: var(--bay);
  color: var(--bay-deep);
}

.pn-page.is-current {
  background: var(--bay);
  border-color: var(--bay);
  color: var(--fog);
  font-weight: 700;
}

/* Prev/Next at the ends of the range. Kept in the DOM rather than removed so
   the pager's width doesn't jump between page 1 and page 2. */
.pn-page[disabled],
.pn-page[aria-disabled="true"] {
  color: var(--ink-faint);
  cursor: default;
}

.pn-page[disabled]:hover,
.pn-page[aria-disabled="true"]:hover {
  border-color: var(--line-strong);
  color: var(--ink-faint);
}

.pn-pager-note {
  flex-basis: 100%;
  margin: 0.5rem 0 0;
  text-align: center;
  font-family: var(--font-body);
  font-size: 0.71875rem;
  color: var(--ink-faint);
}

/* ==========================================================================
   Responsive
   ========================================================================== */

/* 940px: the rail becomes a bottom sheet and the grid goes single-column.
   See the file header for why this is not the 900px nav breakpoint. */
@media (max-width: 940px) {
  .pn-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The sheet's opener. Full width and 44px tall — it is the ONLY way to reach
     the facets at this size, so it cannot be a 30px pill. */
  .pn-rail-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-height: 44px;
    padding: 0 1rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-s);
    background: var(--paper);
    font-family: var(--font-display);
    font-size: 0.875rem;
    font-weight: 700;
    color: var(--ink);
    cursor: pointer;
  }

  /* The rail leaves the flow entirely: as a sheet it must not reserve a row of
     the grid above the results while closed. `bottom: var(--pt-tray-h)` parks
     it on top of the compare tray rather than under it. */
  .pn-rail {
    position: fixed;
    z-index: 1000;
    inset: auto 0 var(--pt-tray-h, 0px) 0;
    max-height: 78vh;
    border-radius: var(--radius-m) var(--radius-m) 0 0;
    border-width: 1px 0 0;
    box-shadow: 0 -14px 34px -12px rgba(var(--ink-rgb), 0.4);
    transform: translateY(101%);
    visibility: hidden;
    transition: transform 0.22s var(--ease), visibility 0.22s var(--ease);
  }

  /* visibility, not display: a display swap cannot be transitioned, and
     leaving it visible off-screen puts ten facets' worth of checkboxes in the
     tab order behind a closed sheet. 101% clears the shadow too. */
  .pn-rail.is-open {
    transform: translateY(0);
    visibility: visible;
  }

  .pn-rail-head {
    position: sticky;
    top: 0;
    z-index: 1;
    margin: -1rem -1.1rem 0.9rem;
    padding: 1rem 1.1rem 0.7rem;
    background: var(--paper);
  }

  .pn-sheet-close {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    margin: -0.6rem -0.6rem -0.6rem 0;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--ink-soft);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
  }

  /* 44px minimum on every option row — a 15px checkbox is a 15px target
     without it, and the label is what people actually tap. */
  .pn-opt {
    min-height: 44px;
  }

  .pn-opt-label {
    font-size: 0.875rem;
  }

  /* The active-filter chips are desktop-only by design: at this size the
     "Filters (n)" button already carries the count and the sheet carries Reset
     all, so a second wrapping row of chips would push the first card below the
     fold to say the same thing twice. */
  .pn-active {
    display: none;
  }

  .pn-page {
    min-width: 44px;
    min-height: 44px;
  }
}

@media (max-width: 560px) {
  .pn-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(236px, 100%), 1fr));
  }
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */

/* style.css's global block already flattens every transition-duration, but it
   cannot undo a transform that only exists in a :hover rule — the lift lands
   instantly instead of not at all. These rules match the base hover selectors
   exactly and win on source order. */
@media (prefers-reduced-motion: reduce) {
  .pn-card:hover,
  .pn-card:focus-within {
    transform: none;
  }
}

/* ---------------------------------------------------- static brand index ---
   The no-JS / pre-JS catalog: every paddle, grouped by brand, generated into
   paddles/browse.html by scripts/generate-paddle-pages.mjs. paddle-finder.js
   removes it as soon as the interactive grid mounts, so on a normal visit this
   is painted once and gone — it must therefore be cheap to render and must not
   shift the grid when it disappears (it sits below #paddle-finder-app, so it
   doesn't). Multi-column so 514 links stay scannable instead of becoming a
   half-metre scroll. */
.pn-index {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--rule-25);
}

.pn-index-head {
  margin: 0 0 0.3rem;
  font-size: 1.15rem;
}

.pn-index-note {
  margin: 0 0 var(--space-2);
  color: var(--ink-soft);
  font-size: 0.9rem;
  max-width: 60ch;
}

.pn-index-cols {
  columns: 3 16rem;
  column-gap: var(--space-3);
}

.pn-index-group {
  break-inside: avoid;
  margin-bottom: var(--space-2);
}

.pn-index-brand {
  margin: 0 0 0.25rem;
  font-size: 0.82rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.pn-index-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.pn-index-list li {
  margin: 0 0 0.15rem;
  font-size: 0.92rem;
  line-height: 1.35;
}
