/* =============================================================
   36 — PRODUCT SHELF   ("Most popular")

   Owns: template-parts/home/products.php only.
   Loads after main.css / woocommerce.css and after every media
   query in them, so every rule here carries its own breakpoints.

   ROUND 2 — WHAT THE CRITIC REJECTED, AND THE ANSWER

   The verdict: "five plates sit at identical weight, cut straight
   into a near-flat ground with three near-identical photographs
   filling 3 of 5 slots, so the row reads as inventory rather than
   an authored composition — where panel B earns its authority from
   one dominant image, one dominant line and real atmospheric
   depth."

   Three separate failures, three separate answers.

   1 · ONE DOMINANT IMAGE.  The row of equals is gone. The template
       lifts the most popular piece out of the track; §5 sets it as
       a full-height plate roughly three times a card's area, with
       its type sitting ON the photograph under a multi-stop scrim —
       the hero's own move. Everything else is the supporting cast.

   2 · ONE DOMINANT LINE.  §2 takes the section title to the hero's
       own setting: Fraunces 300, clamp to 4.4rem, line-height 0.94,
       -0.045em, last word italic in the accent. The feature name
       under it is deliberately less than half its size, so the
       hierarchy title > subject > shelf is never in question.

   3 · ATMOSPHERIC DEPTH.  §1 replaces the 12% radial that did not
       read with a three-layer ground: a 20% --color-primary dawn in
       the upper left, a --night-2 floor under the track, and the
       fall between them — masked to nothing at both section edges
       so no seam is drawn against the neighbours that share this
       ground. §6 then grades every photograph to one key
       (`saturate(0.88) contrast(1.04)`) under a FULL-HEIGHT scrim
       running 16% at the top to 62% at the foot, so the bright
       bokeh tin and the near-black studio shots read as one set,
       and gives each plate a soft cast shadow plus a 1px inset ring
       so it lifts off the ground instead of being cut into it.

   Two measured slips from the same note, both fixed by
   construction rather than by a magic number:
     · the readout now carries the tab's own font-size and the tab's
       own padding-bottom, so the "03 / 17" position figure cannot sit
       off the tab baseline;
     · the track's clip box now ends exactly at the container edge
       (see THE N-UP MATH), so the last plate lands flush with the
       progress rule and the BROWSE THE FULL SHOP underline instead
       of ragging one gap short.

   THE N-UP MATH (load bearing — do not "simplify")
   main.js derives its page size as
       floor(parentWidth / (itemWidth + gap))
   where parent is `.products-carousel`. Let the visible column be W
   and the gap g. We want N items and N-1 gaps to fill exactly W, and
   we want that floor to land on N, not N-1.

       .products-carousel-wrap  width W, overflow hidden   <- the cut
       .products-carousel       width W + g                <- what JS measures
       item flex-basis          (100% - N*g)/N  of (W + g)
                              = (W - (N-1)*g) / N

   Then N items + (N-1) gaps = W exactly (flush right edge), while
   itemWidth + gap = (W + g)/N, so floor((W + g) / ((W + g)/N)) = N.
   The one spare gap overflows to the right and is clipped away by
   the wrap. Change any one of the three and the row rags again.
   ============================================================= */

.products-section {
  position: relative;
  /* Transparent, not --color-bg-light. The sections either side sit on the
     page ground; a lighter band here would draw a hard horizontal seam at
     both boundaries. Depth comes from the wash below, not from a fill. */
  background: transparent;
  overflow-x: clip;
  isolation: isolate;

  /* One curve for the whole section. Falls back to the hero's own
     bezier if 05-motion.css is ever not present. */
  --pc-ease: var(--ease-brand, cubic-bezier(0.2, 0.75, 0.2, 1));

  /* Declared on the section, not the track: `.products-carousel` needs the
     same value to size its clip box (see THE N-UP MATH). */
  --pc-gap: clamp(14px, 1.7vw, 24px);
  --pc-split: clamp(28px, 3.9vw, 62px);
  --pc-feature: 36%;
}

/* Vertical rhythm is owned by the shared system in 05-motion.css
   (`body :is(.products-section, …)`), which outranks a bare class
   here on purpose. Nothing in this file sets section padding. */


/* -------------------------------------------------------------
   1 · THE GROUND
   Not a fill and not a hairline band — a light source, a floor,
   and the fall between them. Masked to nothing at the top and
   bottom edges, because the neighbours share this ground and a
   wash that reached a boundary would draw a seam across the full
   width of the page.
   ------------------------------------------------------------- */

.products-section::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    /* the light: a green dawn behind the title and the subject */
    radial-gradient(98% 64% at -8% 16%,
      color-mix(in srgb, var(--color-primary) 20%, transparent) 0%,
      color-mix(in srgb, var(--color-primary) 9%, transparent) 38%,
      color-mix(in srgb, var(--color-primary) 3%, transparent) 62%,
      transparent 84%),
    /* the floor the shelf stands on */
    radial-gradient(106% 42% at 46% 76%,
      color-mix(in srgb, var(--night-2) 90%, transparent) 0%,
      color-mix(in srgb, var(--night-2) 44%, transparent) 44%,
      transparent 78%),
    /* the fall between them */
    linear-gradient(180deg,
      transparent 0%,
      color-mix(in srgb, var(--night-2) 16%, transparent) 34%,
      color-mix(in srgb, var(--night-2) 42%, transparent) 70%,
      color-mix(in srgb, var(--night-2) 10%, transparent) 100%);
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, black 9%, black 91%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, black 9%, black 91%, transparent 100%);
}


/* -------------------------------------------------------------
   2 · SECTION HEADER — the dominant line
   Two direct children, because motion.js staggers exactly those:
   the kicker is beat one, the title block is beat two.
   ------------------------------------------------------------- */

.products-section .products-kicker {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin: 0 0 clamp(20px, 2.2vw, 34px);
  font-family: var(--font-primary);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.products-section .products-kicker-rule {
  flex: none;
  display: block;
  width: 34px;
  height: 1px;
  background: var(--color-border-strong);
}

.products-section .products-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: clamp(28px, 4vw, 80px);
}

.products-section .products-head-main {
  min-width: 0;
}

/* The hero's setting, one size down. This is the line that has to
   carry the chapter. */
.products-section .products-section-header .section-title {
  margin-top: 0;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: clamp(2.5rem, 5vw, 4.4rem);
  line-height: 0.94;
  letter-spacing: -0.045em;
  color: var(--color-dark);
  text-wrap: balance;
}

.products-section .products-section-header .section-title em {
  font-style: italic;
  font-weight: 300;
  color: var(--color-accent);
}

.products-section .products-section-header .section-title::after { content: none; }

.products-section .products-section-subtitle {
  max-width: 44ch;
  margin-top: 0;
  font-size: clamp(0.92rem, 1vw, 1.02rem);
  line-height: var(--leading-relaxed);
  color: var(--color-text-light);
}

.products-section .products-head-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: 8px;
  border-bottom: 1px solid var(--color-border-strong);
  font-family: var(--font-primary);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-text-light);
  transition: color 0.4s var(--pc-ease), border-color 0.4s var(--pc-ease);
}

.products-section .products-head-link svg {
  width: 15px;
  height: 15px;
  transition: transform 0.45s var(--pc-ease);
}

.products-section .products-head-link:hover,
.products-section .products-head-link:focus-visible {
  color: var(--color-accent);
  border-bottom-color: var(--color-accent);
}

.products-section .products-head-link:hover svg,
.products-section .products-head-link:focus-visible svg { transform: translateX(6px); }


/* -------------------------------------------------------------
   3 · THE WRAPPER THAT USED TO BE A PANEL
   main.js scopes the carousel to `.products-section-card`, so the
   node stays. It carries no ground, no border and no padding.
   ------------------------------------------------------------- */

.products-section .products-section-card {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
}


/* -------------------------------------------------------------
   4 · INDEX RAIL — filters left, navigation right, one hairline
   ------------------------------------------------------------- */

.products-section .products-toolbar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(20px, 3vw, 56px);
  border-bottom: 1px solid var(--color-border);
  margin-bottom: clamp(30px, 3.6vw, 58px);
}

.products-section .products-filter-tabs {
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-end;
  gap: clamp(16px, 2.1vw, 34px);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
}

.products-section .products-filter-tabs::-webkit-scrollbar { display: none; }

/* The fade only appears once the row genuinely overflows — the class
   is set from the template's own chrome script, never guessed in CSS,
   so a row that fits never has its last label dimmed. */
.products-section .products-filter-tabs.is-scrollable {
  -webkit-mask-image: linear-gradient(90deg, black calc(100% - 64px), transparent);
  mask-image: linear-gradient(90deg, black calc(100% - 64px), transparent);
}

/* --pc-label-pad is the shared foot of the rail. The tabs use it and so
   does the readout, which is why their ink bottoms cannot disagree. */
.products-section .products-toolbar { --pc-label-pad: clamp(15px, 1.6vw, 21px); }

.products-section .products-filter-tab {
  position: relative;
  flex: none;
  padding: 0 0 var(--pc-label-pad);
  border: 0;
  border-radius: 0;
  background: none;
  font-family: var(--font-primary);
  font-size: 0.74rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: color 0.4s var(--pc-ease);
}

.products-section .products-filter-tab::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  /* bottom: 0, not -1px: the strip is an overflow-x scroller and
     overflow-y is therefore `hidden`, which would clip anything hanging
     below the box. A 2px bar seated on the toolbar's own hairline reads
     as an underline drawn on the rule. */
  bottom: 0;
  height: 2px;
  background: var(--color-accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.5s var(--pc-ease);
}

.products-section .products-filter-tab:hover { color: var(--color-text); }

.products-section .products-filter-tab.active {
  border-color: transparent;
  color: var(--color-dark);
}

.products-section .products-filter-tab.active::after { transform: scaleX(1); }

.products-section .products-filter-tab:focus-visible {
  outline: 1px solid var(--color-accent);
  outline-offset: 5px;
}

/* --- navigation cluster --- */

.products-section .products-nav {
  flex: none;
  display: flex;
  align-items: flex-end;
  gap: clamp(14px, 1.7vw, 26px);
  padding-bottom: 0;
}

/* Same size, same letterspacing, same padding-bottom as a tab. The shared
   baseline is now structural rather than a number that drifts. */
.products-section .products-nav-count {
  margin: 0;
  padding-bottom: var(--pc-label-pad);
  font-family: var(--font-primary);
  font-size: 0.74rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: 0.13em;
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
}

.products-section .products-nav-count b {
  font-weight: 600;
  color: var(--color-dark);
}

.products-section .products-nav-count span { padding: 0 3px; }

/* One pill split by a hairline, not two loose discs. Chrome that looks
   drawn rather than dropped in. */
.products-section .products-nav-btns {
  display: flex;
  align-items: stretch;
  margin-bottom: clamp(6px, 0.7vw, 10px);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: color-mix(in srgb, var(--night-2) 36%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.products-section .products-carousel-btn {
  flex: none;
  width: clamp(46px, 3.7vw, 56px);
  height: clamp(36px, 2.9vw, 42px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--color-text-light);
  cursor: pointer;
  transition: color 0.4s var(--pc-ease),
              background-color 0.4s var(--pc-ease),
              opacity 0.4s var(--pc-ease);
}

.products-section .products-carousel-next {
  border-left: 1px solid var(--color-border);
}

.products-section .products-carousel-btn svg { width: 16px; height: 16px; }

.products-section .products-carousel-btn:hover,
.products-section .products-carousel-btn:focus-visible {
  color: var(--color-dark);
  background-color: color-mix(in srgb, var(--color-accent) 12%, transparent);
}

.products-section .products-carousel-btn:focus-visible {
  outline: 1px solid var(--color-accent);
  outline-offset: -3px;
}

.products-section .products-carousel-btn:active {
  background-color: color-mix(in srgb, var(--color-accent) 20%, transparent);
}

/* Dimmed, never disabled: the state is mirrored from main.js's own
   bounds, and if that mirror is ever wrong the button must still work. */
.products-section .products-carousel-btn.is-disabled {
  opacity: 0.3;
  color: var(--color-text-muted);
}

.products-section .products-carousel-btn.is-disabled:hover {
  background-color: transparent;
  color: var(--color-text-muted);
}


/* -------------------------------------------------------------
   5 · THE STAGE — one subject, one shelf
   The feature spans both rows so its foot lands exactly on the
   progress rule; the rule spans only the track's column so the
   fill it draws lines up with the plates it describes.
   ------------------------------------------------------------- */

.products-section .products-stage {
  display: grid;
  grid-template-columns: minmax(0, var(--pc-feature)) minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: var(--pc-split);
  align-items: stretch;
}

.products-section .products-feature {
  grid-column: 1;
  grid-row: 1 / span 2;
  min-width: 0;
  margin: 0;
}

.products-section .products-feature-card {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  min-height: clamp(400px, 34vw, 560px);
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--night-2);
  color: inherit;
  text-decoration: none;
  /* The subject is lit and lifted harder than the shelf. */
  box-shadow:
    0 46px 82px -44px rgba(0, 0, 0, 0.95),
    0 18px 38px -26px rgba(0, 0, 0, 0.7);
  transition: box-shadow 0.6s var(--pc-ease);
}

.products-section .products-feature-img {
  position: absolute;
  inset: 0;
  margin: 0;
}

.products-section .products-feature-img img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  /* Above centre, so the subject rises clear of the type's stage instead of
     sitting behind it. */
  object-position: 50% 38%;
  /* Graded to the same key as every plate on the shelf. */
  filter: saturate(0.88) contrast(1.04);
  transition: transform 1.3s var(--pc-ease);
}

/* Multi-stop scrim: a touch of shade at the head so the kicker sits, then
   a long fall to a near-solid foot the display type can live on. The 1px
   inset ring rides on this layer because an inset box-shadow on the plate
   itself would be painted under the photograph. */
.products-section .products-feature-veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  pointer-events: none;
  border-radius: inherit;
  background:
    /* the barest vignette at the head — enough to seat the plate, not enough
       to take the light off the subject */
    linear-gradient(180deg,
      color-mix(in srgb, var(--night-2) 24%, transparent) 0%,
      transparent 22%),
    /* the stage: near solid where the display type sits, gone by two thirds,
       so the upper half of this plate stays the brightest thing in the row */
    linear-gradient(0deg,
      color-mix(in srgb, var(--night-2) 98%, transparent) 0%,
      color-mix(in srgb, var(--night-2) 95%, transparent) 24%,
      color-mix(in srgb, var(--night-2) 88%, transparent) 36%,
      color-mix(in srgb, var(--night-2) 70%, transparent) 48%,
      color-mix(in srgb, var(--night-2) 32%, transparent) 64%,
      color-mix(in srgb, var(--night-2) 6%, transparent) 80%,
      transparent 92%);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--paper) 13%, transparent);
  transition: opacity 0.6s var(--pc-ease);
}

.products-section .products-feature-body {
  position: absolute;
  z-index: 2;
  inset: auto clamp(20px, 2.3vw, 36px) clamp(22px, 2.4vw, 38px);
  display: flex;
  flex-direction: column;
  gap: clamp(12px, 1.3vw, 18px);
}

.products-section .products-feature-kicker {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  font-family: var(--font-primary);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--color-text-light);
}

.products-section .products-feature-rule {
  flex: none;
  display: block;
  width: 28px;
  height: 1px;
  background: color-mix(in srgb, var(--color-accent) 62%, transparent);
}

.products-section .products-feature-kicker b {
  font-weight: 600;
  color: var(--color-accent);
}

.products-section .products-feature-kicker span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.products-section .products-feature-kicker span::before {
  content: "\00b7";
  margin-right: 0.55em;
  color: var(--color-text-muted);
}

/* Deliberately less than half the section title. The chapter head leads;
   the subject follows. */
.products-section .products-feature-name {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: clamp(1.45rem, 1.95vw, 2.05rem);
  line-height: 1.06;
  letter-spacing: -0.032em;
  color: var(--ink);
  text-wrap: balance;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.products-section .products-feature-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-5);
  margin-top: clamp(4px, 0.6vw, 10px);
  padding-top: clamp(14px, 1.5vw, 20px);
  border-top: 1px solid color-mix(in srgb, var(--paper) 16%, transparent);
}

.products-section .products-feature-price {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-3);
  min-width: 0;
}

.products-section .products-feature-price .price-current {
  font-family: var(--font-heading);
  font-size: clamp(1.2rem, 1.35vw, 1.5rem);
  font-weight: 400;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.products-section .products-feature-price .price-original {
  font-family: var(--font-primary);
  font-size: 0.82rem;
  color: var(--color-text-light);
  text-decoration: line-through;
}

.products-section .products-feature-cta {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-primary);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-text-light);
  transition: color 0.4s var(--pc-ease);
}

.products-section .products-feature-cta svg {
  width: 14px;
  height: 14px;
  transition: transform 0.45s var(--pc-ease);
}

.products-section .products-feature-card:hover .products-feature-img img { transform: scale(1.045); }
.products-section .products-feature-card:hover .products-feature-cta { color: var(--color-accent); }
.products-section .products-feature-card:hover .products-feature-cta svg { transform: translateX(6px); }
.products-section .products-feature-card:hover {
  box-shadow:
    0 56px 96px -44px rgba(0, 0, 0, 1),
    0 20px 44px -26px rgba(0, 0, 0, 0.78);
}

.products-section .products-feature-card:focus-visible {
  outline: 1px solid var(--color-accent);
  outline-offset: 3px;
}


/* -------------------------------------------------------------
   6 · THE SHELF — track, clip box, plates
   ------------------------------------------------------------- */

/* The cut. Its right edge IS the container edge, which is what puts the
   last plate flush with the progress rule and the header link's underline.
   The 8px of left slack is sub-pixel insurance, not decoration: main.js
   steps the track by `first.offsetWidth + gap`, and offsetWidth is an
   integer while the real flex width is fractional; over twenty steps the
   rounding walks the track a pixel or two past true and would shave the
   leading plate. Padding the clip box on the left — and only the left —
   gives that drift somewhere to go. */
.products-section .products-carousel-wrap {
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
  display: block;
  margin-left: -8px;
  padding-left: 8px;
  overflow: hidden;
}

/* One gap wider than the cut. See THE N-UP MATH at the top of the file —
   main.js measures this element, and this width is what makes its
   floor() land on N instead of N-1. */
.products-section .products-carousel {
  display: block;
  width: calc(100% + var(--pc-gap));
  overflow: visible;
}

.products-section .products-carousel-track {
  display: flex;
  align-items: stretch;
  gap: var(--pc-gap);
  transition: transform 0.78s var(--pc-ease);
}

.products-section .product-carousel-item {
  --pc-per: 3;
  flex: 0 0 calc((100% - var(--pc-per) * var(--pc-gap)) / var(--pc-per));
  min-width: 0;
  display: flex;
}

.products-section .product-carousel-item > * { width: 100%; }
.products-section .product-carousel-item.ts-hidden { display: none; }

/* --- card: plate, then type. No box. --- */

.products-section .product-carousel-card {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  overflow: visible;
  box-shadow: none;
  text-align: left;
  color: inherit;
}

.products-section .product-carousel-card:hover {
  transform: none;
  box-shadow: none;
  border-color: transparent;
}

/* The plate. No border — the frame is a 1px inset ring carried by the
   scrim above the photograph, so it reads as light catching an edge
   rather than as a drawn box. Under it, a soft cast shadow: the plate
   now sits ON the ground instead of being cut into it. */
.products-section .product-carousel-img {
  position: relative;
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;
  margin: 0;
  overflow: hidden;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--night-2);
  box-shadow:
    0 30px 54px -30px rgba(0, 0, 0, 0.9),
    0 10px 26px -16px rgba(0, 0, 0, 0.62);
  transition: box-shadow 0.55s var(--pc-ease);
}

.products-section .product-carousel-img img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  padding: 0;
  object-fit: cover;
  object-position: center;
  /* One key for twenty-three photographs shot in twenty-three rooms. */
  filter: saturate(0.88) contrast(1.04);
  transition: transform 1.1s var(--pc-ease), filter 0.6s var(--pc-ease);
}

/* FULL-HEIGHT scrim, not a foot of shade: 16% at the head, 62% at the
   foot. That is what pulls a bright bokeh tin and a near-black studio
   still into the same set. It also carries the 1px inset ring, because
   an inset shadow on the plate itself would be painted beneath the img. */
.products-section .product-carousel-img::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--night-2) 22%, transparent) 0%,
    color-mix(in srgb, var(--night-2) 34%, transparent) 44%,
    color-mix(in srgb, var(--night-2) 72%, transparent) 100%
  );
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--paper) 11%, transparent);
  transition: opacity 0.5s var(--pc-ease);
}

/* main.css uses this pseudo-element as a 1px foot rule under the
   photo. The plate is framed now, so it is free — it becomes the
   affordance the old grey rectangles never were. */
.products-section .product-carousel-img::after {
  content: "\2192";
  position: absolute;
  inset: auto var(--space-3) var(--space-3) auto;
  z-index: 2;
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid color-mix(in srgb, var(--paper) 30%, transparent);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--night-2) 64%, transparent);
  -webkit-backdrop-filter: blur(7px);
  backdrop-filter: blur(7px);
  font-family: var(--font-primary);
  font-size: 0.92rem;
  line-height: 1;
  color: var(--ink);
  opacity: 0;
  transform: translateY(9px);
  transition: opacity 0.45s var(--pc-ease), transform 0.45s var(--pc-ease);
}

.products-section .product-carousel-card:hover .product-carousel-img {
  box-shadow:
    0 38px 66px -30px rgba(0, 0, 0, 0.95),
    0 12px 30px -16px rgba(0, 0, 0, 0.7);
}
.products-section .product-carousel-card:hover .product-carousel-img img { transform: scale(1.055); }
.products-section .product-carousel-card:hover .product-carousel-img::before { opacity: 0.62; }
.products-section .product-carousel-card:hover .product-carousel-img::after { opacity: 1; transform: translateY(0); }

.products-section .product-carousel-card:focus-visible { outline: none; }
.products-section .product-carousel-card:focus-visible .product-carousel-img {
  box-shadow:
    0 0 0 2px color-mix(in srgb, var(--color-accent) 55%, transparent),
    0 30px 54px -30px rgba(0, 0, 0, 0.9);
}

/* --- the type --- */

.products-section .product-carousel-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: clamp(11px, 1.2vw, 16px);
  margin: clamp(16px, 1.7vw, 24px) 0 0;
  padding: 0;
}

.products-section .product-carousel-cats {
  margin: 0;
  font-family: var(--font-primary);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 0.4s var(--pc-ease);
}

.products-section .product-carousel-name {
  margin: 0;
  font-family: var(--font-primary);
  font-size: clamp(0.92rem, 0.98vw, 1rem);
  font-weight: 500;
  line-height: 1.45;
  letter-spacing: -0.004em;
  color: var(--color-dark);
  text-align: left;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* em, never rem: the font-size clamps at the breakpoints below and a
     rem floor would leak a clipped third line under the clamp. */
  min-height: calc(2 * 1.45em);
  transition: color 0.4s var(--pc-ease);
}

.products-section .product-carousel-rating {
  margin: 0;
  justify-content: flex-start;
}

.products-section .product-carousel-rating:empty { display: none; }

.products-section .product-carousel-price {
  margin: auto 0 0;
  padding-top: clamp(13px, 1.4vw, 18px);
  border-top: 1px solid var(--color-border);
  display: flex;
  align-items: baseline;
  justify-content: flex-start;
  gap: var(--space-3);
  flex-wrap: wrap;
  transition: border-color 0.4s var(--pc-ease);
}

/* The price is the one figure on the card that should carry the
   brand's display voice, the way the hero's numerals do. */
.products-section .product-carousel-price .price-current {
  font-family: var(--font-heading);
  font-size: clamp(1.08rem, 1.2vw, 1.28rem);
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--color-dark);
  font-variant-numeric: tabular-nums;
}

.products-section .product-carousel-price .price-original {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  text-decoration: line-through;
}

.products-section .product-carousel-card:hover .product-carousel-name { color: var(--color-accent); }
.products-section .product-carousel-card:hover .product-carousel-cats { color: var(--color-text-light); }
.products-section .product-carousel-card:hover .product-carousel-price {
  border-top-color: color-mix(in srgb, var(--color-accent) 44%, var(--color-border));
}


/* -------------------------------------------------------------
   7 · PROGRESS RULE
   The hero closes on a rule with a lit segment. So does this — and
   it is scoped to the track's own column, so its left edge is the
   first plate's edge and its right edge is the last plate's.
   Neutral, not accent: the accent is spent on the title, the
   subject's numeral and the active filter, and a fourth use would
   be one too many.
   ------------------------------------------------------------- */

.products-section .products-rail {
  grid-column: 2;
  grid-row: 2;
  position: relative;
  height: 2px;
  margin-top: clamp(28px, 3.2vw, 48px);
  background: var(--color-border);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.products-section .products-rail-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  min-width: 8px;
  background: color-mix(in srgb, var(--paper) 72%, transparent);
  border-radius: inherit;
  transition: left 0.78s var(--pc-ease), width 0.5s var(--pc-ease);
}


/* -------------------------------------------------------------
   8 · FILTER SWAP
   A new set arrives; it should not simply appear.
   ------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  .products-section .products-carousel-track.pc-swap .product-carousel-item:not(.ts-hidden) {
    animation: pc-swap-in 0.55s var(--pc-ease) backwards;
  }
}

@keyframes pc-swap-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}


/* =============================================================
   BREAKPOINTS
   This file loads after every media query in main.css, so these
   are the ones that decide.
   ============================================================= */

@media (max-width: 1280px) {
  .products-section { --pc-feature: 38%; }
}

@media (max-width: 1100px) {
  /* Six category labels stop fitting beside the readout around here, and the
     strip's fade would run straight into it. The arrows are the load-bearing
     half of the cluster; the readout steps back and gives the index its room. */
  .products-section .products-nav-count { display: none; }
}

@media (max-width: 1024px) {
  /* The split stops paying its way here: the feature column is too narrow
     for display type and the shelf is too narrow for three plates. Stack,
     and let the subject run full width as a landscape plate. */
  .products-section {
    --pc-split: 0px;
  }
  .products-section .products-stage {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto;
    row-gap: clamp(30px, 4.4vw, 52px);
  }
  .products-section .products-feature {
    grid-column: 1;
    grid-row: 1;
  }
  .products-section .products-carousel-wrap {
    grid-column: 1;
    grid-row: 2;
  }
  .products-section .products-rail {
    grid-column: 1;
    grid-row: 3;
    margin-top: clamp(24px, 3vw, 34px);
  }
  .products-section .products-feature-card {
    height: auto;
    min-height: 0;
    aspect-ratio: 16 / 9;
  }
  .products-section .products-feature-name { font-size: clamp(1.6rem, 3.4vw, 2.4rem); }
  .products-section .products-feature-body { inset: auto clamp(22px, 3.4vw, 40px) clamp(24px, 3.4vw, 40px); max-width: 40ch; }

  .products-section .products-head {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: clamp(22px, 3vw, 34px);
  }
  .products-section .products-head-link { justify-self: start; }
}

@media (max-width: 900px) {
  .products-section .products-feature-card { aspect-ratio: 3 / 2; }
}

@media (max-width: 760px) {
  .products-section .products-nav { gap: var(--space-2); }
  .products-section .products-toolbar { gap: var(--space-5); --pc-label-pad: 14px; }
  .products-section .products-filter-tabs { gap: clamp(15px, 4vw, 24px); }
  .products-section .products-filter-tab {
    font-size: 0.72rem;
    letter-spacing: 0.11em;
  }
  .products-section .products-nav-btns { margin-bottom: 5px; }
  .products-section .products-carousel-btn { width: 46px; height: 36px; }
  .products-section .products-carousel-btn svg { width: 15px; height: 15px; }
}

@media (max-width: 680px) {
  .products-section { --pc-gap: 14px; }
  .products-section .product-carousel-item { --pc-per: 2; }
  .products-section .products-feature-card { aspect-ratio: 4 / 3; }
  .products-section .products-feature-body { max-width: none; }
}

@media (max-width: 560px) {
  .products-section .products-section-header .section-title { font-size: clamp(2.2rem, 10.4vw, 3.1rem); }
  .products-section .products-section-subtitle { font-size: 0.92rem; }
  .products-section .product-carousel-body { gap: 10px; margin-top: 14px; }
  .products-section .product-carousel-price { padding-top: 12px; }
  .products-section .products-feature-name { font-size: clamp(1.4rem, 6.2vw, 1.85rem); }
  .products-section .products-feature-card { aspect-ratio: 5 / 4; }
}

@media (max-width: 480px) {
  /* A peek of the next plate is the only honest scroll hint at this
     width; the arrows stay, because main.js has no swipe handler. */
  .products-section .product-carousel-item { --pc-per: 1.5; }
  .products-section .product-carousel-cats { font-size: 0.72rem; letter-spacing: 0.12em; }
  .products-section .product-carousel-name { font-size: 0.9rem; }
  .products-section .products-feature-card { aspect-ratio: 1 / 1; }
  .products-section .products-feature-foot { flex-wrap: wrap; gap: var(--space-3); }
  .products-section .product-carousel-img::after { width: 30px; height: 30px; font-size: 0.85rem; }
}

/* Hover lift and image zoom are meaningless on touch and leave a card
   stuck mid-transform after a tap; the plate keeps its frame instead. */
@media (hover: none) {
  .products-section .product-carousel-card:hover .product-carousel-img img { transform: none; }
  .products-section .product-carousel-card:hover .product-carousel-img::after { opacity: 0; }
  .products-section .products-feature-card:hover .products-feature-img img { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .products-section .products-carousel-track,
  .products-section .products-rail-fill,
  .products-section .product-carousel-img img,
  .products-section .products-feature-img img,
  .products-section .products-head-link svg { transition-duration: 0.01ms; }
}
