/* Engine · product detail page styles.
   Buy hero, gallery carousel + thumb rail, sticky bar, trust row,
   compare table, related, FAQ, guarantee, narrow-viewport hardening.
 */

/* ===========================================================================
   PDP — ported from app/product/[slug]/product-view.tsx + sections/buy-hero.tsx
   Reference metrics (measured on cofactorsupply.com):
     crumb bar   padding 12px 24px 0
     buy hero    grid, gap 40px → lg:56px, padding 16px 24px 32px, lg 2 cols
     chapters    py-20 → lg:py-24 (80/96px), dark bands on --ink-band
   =========================================================================== */

/* ---- crumbs --------------------------------------------------------------- */
.pdp-crumbs { padding-top: var(--p-space-3); }
.pdp-back {
  display: inline-flex; align-items: center; gap: var(--p-space-1);
  padding-block: var(--p-space-1-5); margin-left: calc(var(--p-space-0-5) * -1);
  font-size: var(--p-text-14); font-weight: 500; color: var(--ink-2); text-decoration: none;
  transition: color 150ms var(--ease-out-quart);
}
.pdp-back:hover { color: var(--foreground); }
.pdp-back__chev { transform: rotate(90deg); }
.pdp-crumbs__trail { display: none; }
@media (min-width: 640px) {
  .pdp-back { display: none; }
  .pdp-crumbs__trail { display: flex; flex-wrap: wrap; }}


/* ---- buy hero ------------------------------------------------------------- */
.pdp-hero {
  display: grid; gap: var(--p-space-10);
  padding-top: var(--p-space-4); padding-bottom: var(--p-space-8);
}
@media (min-width: 1024px) {
  .pdp-hero { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--p-space-14); padding-top: var(--p-space-4); }}


/* gallery */
.pdp-gallery { display: flex; min-width: 0; flex-direction: column; gap: var(--p-space-5); }
.pdp-gallery__code {
  position: absolute; top: 0.875rem; left: 0.875rem;
  display: inline-flex; align-items: center;
  border-radius: var(--p-radius-pill); border: 1px solid var(--border);
  background: color-mix(in oklab, var(--background) 85%, transparent);
  backdrop-filter: blur(6px); box-shadow: var(--elevation-whisper);
  padding: var(--p-space-1) var(--p-space-2-5);
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-11); font-weight: 600; letter-spacing: var(--p-tracking-6); color: var(--foreground);
}
/* The thumbnail rail was defined TWICE in this file — a 4.5rem square set
   here and a 4rem portrait set under "thumbnail rail" below. Same selectors,
   so the later block silently won every shared property and this one's
   brand-ring active state was dead code that read as the live design. One
   definition now, further down, where the rail's layout already lives. */

/* buy box */
/* ONE FLOW, NO SUB-WRAPPERS. The column used to nest a __head group inside
   itself, which meant two competing gap values and a spacing rhythm that
   changed halfway down. Every child is now a direct sibling on one gap, and
   the handful of pairs that must sit tighter than that say so themselves. */
.pdp-buybox { display: flex; min-width: 0; flex-direction: column; gap: var(--p-space-5); }
.pdp-buybox > .proofstars { margin-top: calc(var(--p-space-3) * -1); }
.pdp-buybox > .pdp-sub    { margin-top: calc(var(--p-space-4) * -1); }
.pdp-buybox > .pdp-rule   { margin-block: calc(var(--p-space-1) * -1); }
.pdp-buybox > .paymarks   { margin-top: calc(var(--p-space-2) * -1); }

/* ---- the face pile --------------------------------------------------------
   The reference's opening move, and the strongest thing in that column: two
   customer faces, their names, and how many others. Ours is built from the
   review table — see ms_social_proof_card(). */
.proofcard {
  display: flex; align-items: center; gap: var(--p-space-3);
  padding: var(--p-space-2-5) var(--p-space-3-5);
  border-radius: var(--radius-lg); background: var(--surface-2);
  text-decoration: none; color: inherit;
  transition: background-color var(--p-duration-base) var(--ease-default);
}
@media (hover: hover) and (pointer: fine) {
  .proofcard:hover { background: var(--brand-muted); }
}
@media (prefers-reduced-motion: reduce) { .proofcard { transition: none; } }
.proofcard__t { font-size: var(--p-text-14); line-height: 1.4; color: var(--ink-2); text-wrap: pretty; }
.proofcard__t strong { font-weight: 600; color: var(--foreground); }

.facepile { display: flex; flex-shrink: 0; }
/* SOLID, NOT TINTED. Pale discs with pale initials, overlapped by a third, put
   two low-contrast monograms on top of each other and neither could be read.
   Filled, they separate on colour and the overlap can stay — and the ring is
   the card's own ground, so they read as two discs rather than one smear. */
.facepile__face {
  display: grid; place-items: center;
  width: var(--p-space-9); height: var(--p-space-9);
  border-radius: var(--p-radius-circle);
  background: var(--brand); color: var(--brand-foreground);
  font-size: var(--p-text-12); font-weight: 600; letter-spacing: var(--p-tracking-4);
  box-shadow: 0 0 0 2px var(--surface-2);
}
/* The first disc sits ON TOP, so the pile reads left-to-right in the same
   order as the names beside it. `order: -1` on the second one also put the
   front disc on top — by moving it to the front, which silently reversed the
   pair: the discs read "FZ, EJ" under the words "Elsa J., Fatima Z.".
   z-index leaves DOM order alone. */
.facepile__face { position: relative; }
.facepile__face:first-child { z-index: 1; }
.facepile__face + .facepile__face { margin-left: calc(var(--p-space-2-5) * -1); }

/* ---- stars + volume -------------------------------------------------------- */
.proofstars {
  display: inline-flex; align-items: center; gap: var(--p-space-2-5);
  align-self: flex-start; text-decoration: none;
  transition: opacity var(--p-duration-base) var(--ease-default);
}
@media (hover: hover) and (pointer: fine) { .proofstars:hover { opacity: 0.7; } }
@media (prefers-reduced-motion: reduce) { .proofstars { transition: none; } }
.proofstars .stars__fill { color: var(--brand); }
.proofstars__n { font-size: var(--p-text-15); font-weight: 600; color: var(--foreground); }

.statuspill {
  display: inline-flex; align-items: center; gap: 0.4375rem;
  border-radius: var(--p-radius-pill); padding: 0.3125rem 0.6875rem;
  font-size: var(--p-text-12); font-weight: 600; letter-spacing: 0.02em;
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--ink-2);
}
.statuspill__dot { width: 0.375rem; height: 0.375rem; border-radius: var(--p-radius-pill); background: currentColor; }
.statuspill--positive { color: var(--stock-in); border-color: color-mix(in oklab, var(--stock-in) 30%, transparent); background: color-mix(in oklab, var(--stock-in) 8%, transparent); }
.statuspill--neutral  { color: var(--ink-3); }

.pdp-title {
  /* The product name is the loudest word on the page and it is always lowercase,
     so it is a display setting, not a heading setting — the display face,
     matching .title-2xl's metrics exactly. Weight drops 700→600: 700 at 40px
     out-weighs the wordmark sitting directly above it in the header. */
  /* MUCH BIGGER. The reference sets the product name as the loudest object on
     the page by a wide margin, and at 2.5rem ours was competing with the price
     rather than leading it. */
  margin: 0; font-family: var(--font-display); font-size: clamp(2.25rem, 4.4vw, 3.25rem);
  line-height: 1.05; letter-spacing: var(--p-tracking-n25); font-weight: 600;
  color: var(--foreground); text-wrap: balance;
}

/* The one-line descriptor under the name. Was .pdp-tagline — large italic
   brand-blue display type, which read as a second headline directly under the
   first. The reference runs a plain grey line here and the title keeps the
   whole voice. */
.pdp-sub { margin: 0; font-size: var(--p-text-16); line-height: 1.5; color: var(--ink-2); max-width: 44ch; }

.pdp-rule { height: 0; margin: 0; border: 0; border-top: 1px solid var(--border); }
.pdp-price { display: flex; flex-wrap: wrap; align-items: center; gap: var(--p-space-2) var(--p-space-3); margin: 0; }
.pdp-price__amount {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-36); line-height: 1.1; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--foreground);
}
@media (min-width: 640px) { .pdp-price__amount { font-size: var(--p-text-48); }}

.pdp-price__amount .woocommerce-Price-amount { color: inherit; font: inherit; }
.pdp-price__unit {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-14); font-variant-numeric: tabular-nums; color: var(--ink-3);
}
@media (min-width: 1024px) { .pdp-price__unit { font-size: var(--p-text-16); }}


/* add to cart */
.pdp-atc { display: flex; flex-direction: column; gap: var(--p-space-3-5); }
@media (min-width: 480px) { .pdp-atc { flex-direction: row; align-items: stretch; }}

/* THE WRAPPER IS THE CONTROL. The stepper reads as one object, so the border,
   the radius, the fill and the focus ring all belong to `.pdp-qty`; the input
   and the two buttons inside it are chromeless passengers. Giving the input
   its own frame is what made this render as a box inside a box — see the
   :where() note at the top of the skincare form baseline for how it got one. */
.pdp-qty {
  display: inline-flex; align-items: center; flex-shrink: 0;
  border: 1px solid var(--border-strong); border-radius: var(--radius-lg);
  background: var(--background); height: 3rem;
  transition: border-color 150ms var(--ease-out-quart), box-shadow 150ms var(--ease-out-quart);
}
.pdp-qty:hover, .pdp-qty.is-hover { border-color: var(--border-hover); }
/* :focus-within, not :focus on the input — tabbing to either button or the
   field lights the whole control, which is the thing that actually has focus
   as far as the eye is concerned. */
.pdp-qty:focus-within, .pdp-qty.is-focus {
  border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--brand) 16%, transparent);
}
.pdp-qty button {
  display: grid; place-items: center; width: 2.75rem; height: 100%;
  border: none; background: transparent; color: var(--ink-2); cursor: pointer;
  border-radius: inherit;
  transition: background-color 150ms var(--ease-out-quart),
              color 150ms var(--ease-out-quart),
              transform 150ms var(--ease-out-quart);
}
.pdp-qty button:hover { background: var(--muted); color: var(--foreground); }
/* Instant feedback on press. 0.94 on a 44px target is ~2.6px of travel —
   readable at a glance, nowhere near a bounce. */
.pdp-qty button:active { transform: scale(0.94); }
.pdp-qty button:focus-visible { outline: none; }
/* At qty 1 the minus does nothing. Saying so is cheaper than letting someone
   press a live-looking button and watch nothing happen. */
.pdp-qty button:disabled { color: var(--ink-4); cursor: default; background: transparent; transform: none; }
.pdp-qty__input {
  width: 2.5rem; height: 100%; min-height: 0; padding: 0;
  border: none; background: transparent; box-shadow: none; text-align: center;
  font-family: var(--font-mono), ui-monospace, monospace; font-size: var(--p-text-15);
  font-variant-numeric: tabular-nums; color: var(--foreground); outline: none;
  appearance: textfield; -moz-appearance: textfield;
}
.pdp-qty__input:hover, .pdp-qty__input:focus { border: none; box-shadow: none; }
.pdp-qty__input::-webkit-outer-spin-button,
.pdp-qty__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
@media (prefers-reduced-motion: reduce) {
  .pdp-qty, .pdp-qty button { transition: none; }
  .pdp-qty button:active { transform: none; }
}
/* `flex: 1` is flex-basis 0 — fine in the row layout, but under 480px the
   container is a COLUMN, so the basis applies to HEIGHT and the primary
   button collapsed to 22px on every phone (measured 2026-08-19). The flex
   grow belongs to the row layout only. */
.pdp-add { flex: 0 0 auto; }
@media (min-width: 480px) { .pdp-add { flex: 1 1 0%; } }
.pdp-add[data-loading] { opacity: 0.6; pointer-events: none; }

/* ---- chapters ------------------------------------------------------------- */
.pdp-chapter { padding-block: var(--p-space-20); }
@media (min-width: 1024px) { .pdp-chapter { padding-block: var(--p-space-24); }}

.pdp-chapter--related, .pdp-chapter--faq { border-top: 1px solid var(--border); }
.pdp-chapter--tint { border-block: 1px solid var(--border); background: color-mix(in oklab, var(--surface-2) 42%, var(--background)); }

/* the dark data chapter — dark is only ever used where it carries data */
.pdp-chapter--ink { background: var(--ink-band); color: color-mix(in oklab, var(--brand-foreground) 82%, transparent); }
.pdp-chapter__grid { display: grid; gap: var(--p-space-10); align-items: center; }
@media (min-width: 900px) { .pdp-chapter__grid { grid-template-columns: 1fr 1fr; gap: var(--p-space-16); }}

.pdp-chapter__title {
  margin: var(--p-space-2-5) 0 0; font-size: clamp(1.75rem, 3vw, 2.25rem); line-height: 1.1;
  letter-spacing: var(--p-tracking-n25); font-weight: 600; color: var(--brand-foreground); text-wrap: balance;
}
.pdp-chapter__body {
  margin: var(--p-space-4) 0 0; max-width: 52ch; line-height: 1.65;
  color: color-mix(in oklab, var(--brand-foreground) 72%, transparent);
}

.pdp-chapter__note {
  margin: var(--p-space-7) 0 0; font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-11); letter-spacing: 0.1em; text-transform: uppercase;
  color: color-mix(in oklab, var(--brand-foreground) 45%, transparent);
}
.pdp-faq__sub { margin: var(--p-space-2) 0 0; color: var(--muted-foreground); }

/* ---- sticky buy bar ------------------------------------------------------- */
.pdp-stickybar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
  border-top: 1px solid var(--border);
  background: color-mix(in oklab, var(--background) 92%, transparent);
  backdrop-filter: blur(16px) saturate(150%);
  box-shadow: var(--shadow-glassbar);
  transform: translateY(100%);
  transition: transform 260ms var(--ease-drawer);
}
.pdp-stickybar[hidden] { display: none; }
.pdp-stickybar.is-in { transform: none; }
@media (prefers-reduced-motion: reduce) { .pdp-stickybar { transition: none; }}

.pdp-stickybar__inner {
  display: flex; align-items: center; justify-content: space-between; gap: var(--p-space-4);
  padding-block: var(--p-space-2-5);
}
.pdp-stickybar__id { display: flex; align-items: center; gap: var(--p-space-3); min-width: 0; }
.pdp-stickybar__img { width: 2.75rem; height: 2.75rem; object-fit: cover; border-radius: var(--radius-md); background: var(--surface); }
.pdp-stickybar__name {
  margin: 0; font-size: var(--p-text-14); font-weight: 600; color: var(--foreground);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pdp-stickybar__price {
  margin: 0; font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-13); font-variant-numeric: tabular-nums; color: var(--ink-3);
}
.pdp-stickybar__price .woocommerce-Price-amount { color: inherit; font: inherit; }
.pdp-stickybar__cta { flex-shrink: 0; }
@media (max-width: 479px) { .pdp-stickybar__id { display: none; } .pdp-stickybar__cta { width: 100%; }}


/* ===========================================================================
   ENRICHED CONVERSION SECTIONS
   Beat structure modelled on Fempia / AG1 / Grüns / Eight Sleep: each scroll
   answers the next objection in a DIFFERENT visual format.
   =========================================================================== */

/* ---- annotated gallery callouts (Fempia / Grüns device) -------------------- */
.pdp-callout {
  position: absolute; right: 0.875rem; z-index: 10;
  display: flex; flex-direction: column; gap: var(--p-space-px);
  padding: 0.4375rem 0.6875rem;
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: color-mix(in oklab, var(--background) 88%, transparent);
  backdrop-filter: blur(8px); box-shadow: var(--elevation-card);
  animation: pdp-callout-in 600ms var(--ease-out-quart) backwards;
}
.pdp-callout--0 { top: 28%; animation-delay: 220ms; }
.pdp-callout--1 { top: 46%; animation-delay: 340ms; }
@keyframes pdp-callout-in { from { opacity: 0; transform: translateX(10px); } }
@media (prefers-reduced-motion: reduce) { .pdp-callout { animation: none; }}

.pdp-callout__k {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: 9px; letter-spacing: var(--p-tracking-6); text-transform: uppercase; color: var(--ink-3);
}
.pdp-callout__v {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-13); font-weight: 600; font-variant-numeric: tabular-nums; color: var(--foreground);
}
@media (max-width: 639px) { .pdp-callout { display: none; }}


/* ---- benefit chips in the buy box (2-up icon grid) ------------------------- */
/* ---- the three benefits ----------------------------------------------------
   ONE COLUMN, NO CARDS. They were two-up in bordered boxes at 13px — three
   items in a two-column grid leaves a hole, the boxes added a third border
   weight to a column that already has the ladder's, and 13px put the single
   most persuasive copy on the page below body size. The reference runs them
   as a plain list at reading size behind a filled tick, which is what a
   benefit list is: a list. */
.pdp-benefits {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--p-space-2-5);
}
.pdp-benefit {
  display: flex; align-items: flex-start; gap: var(--p-space-3);
  font-size: var(--p-text-16); line-height: 1.45; color: var(--foreground);
  text-wrap: pretty;
}
.pdp-benefit__tick {
  display: grid; place-items: center; flex-shrink: 0;
  width: var(--p-space-6); height: var(--p-space-6);
  /* Optical, not geometric: the tick's own crossbar sits above the glyph's
     centre, so the disc needs a hair of top offset to sit on the text's
     x-height rather than float above the first line. */
  margin-top: var(--p-space-px);
  border-radius: var(--p-radius-circle); background: var(--brand); color: var(--brand-foreground);
}

/* ---- trust strip under the CTA -------------------------------------------- */
.pdp-trustrow {
  list-style: none; margin: 0; padding: var(--p-space-3-5) 0 0;
  display: flex; flex-wrap: wrap; gap: var(--p-space-3) var(--p-space-5);
  border-top: 1px solid var(--border);
}
.pdp-trustrow li { display: inline-flex; align-items: center; gap: 0.4375rem; font-size: var(--p-text-13); color: var(--ink-2); }
.pdp-trustrow svg { color: var(--brand); flex-shrink: 0; }

/* ---- marquee band ---------------------------------------------------------- */
.pdp-marquee {
  overflow: hidden; border-block: 1px solid var(--border);
  background: var(--ink-band); padding-block: var(--p-space-3-5);
}
.pdp-marquee__track {
  display: flex; align-items: center; gap: var(--p-space-6); width: max-content;
  animation: pdp-marquee 38s linear infinite;
}
@keyframes pdp-marquee { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .pdp-marquee__track { animation: none; }}

.pdp-marquee__item {
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-12); letter-spacing: var(--p-tracking-14); text-transform: uppercase; white-space: nowrap;
  color: color-mix(in oklab, var(--brand-foreground) 78%, transparent);
}
.pdp-marquee__dot { width: 3px; height: 3px; border-radius: var(--p-radius-pill); background: var(--brand); flex-shrink: 0; }

/* ---- what's inside: the anatomy diagram ------------------------------------ */
.pdp-chapter--inside { border-top: 1px solid var(--border); }


/* ---- the comparison ---------------------------------------------------------
   A real <table> in a double-bezel tray: hairline outer shell, white inner
   core, and OUR column drawn as one solid brand band running the full height
   of it.

   WHAT WAS WRONG. The old build was a <div>/<span> grid with the "us" column
   painted as a 5%-of-brand ghost tint with rounded ends — while every row's
   own border-bottom ran straight under it and out the other side. Two objects
   claiming the same edge is exactly what "not aligned properly" looks like:
   the band read as a sticker laid on top of the table rather than a column of
   it. The band now OWNS its column and the hairlines stop where it starts.

   It also read small because it was small: an 11px mono header, 12px in the
   one text cell, 15px labels — under a 40px headline, in a 1280px band. The
   labels are prose and now sit on the 17px body-lg step, which is the type
   scale's own floor for prose plus one.

   And it was stranded: capped at 56rem (64rem in skincare) and left-aligned
   inside the axis, so the right third of the band was empty. The tray takes
   the full measure.  */
.pdp-chapter--compare { border-top: 1px solid var(--border); background: color-mix(in oklab, var(--surface-2) 42%, var(--background)); }

/* Head is an editorial split — the claim on the left, the terms of the claim
   on the right — so the two hang off the same baseline instead of a centred
   block sitting above a left-aligned one. */
.pdp-compare__head { display: grid; gap: var(--p-space-4); margin-bottom: var(--p-space-10); }
@media (min-width: 900px) {
  .pdp-compare__head {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    align-items: end; gap: var(--p-space-12);
  }
}
.pdp-compare__headline > .ms-eyebrow { margin-bottom: var(--p-space-3); }
.pdp-compare__headline > * { margin: 0; }
.pdp-compare__lede {
  margin: 0; max-width: 34rem;
  font-size: var(--p-text-17); line-height: var(--leading-body);
  color: var(--ink-3); text-wrap: pretty;
}

/* The tray. Outer shell + inner core, concentric: the inner radius is the
   outer minus the shell's padding, so the two curves stay parallel. */
.pdp-compare {
  padding: var(--p-space-2);
  border: 1px solid var(--border);
  border-radius: var(--p-radius-base);
  background: color-mix(in oklab, var(--surface-2) 70%, var(--card));
  box-shadow: var(--elevation-lift);
}
.pdp-compare__table {
  width: 100%;
  table-layout: fixed;
  border-collapse: separate;   /* the band needs its own corner radii */
  border-spacing: 0;
  background: var(--card);
  border-radius: calc(var(--p-radius-base) - var(--p-space-2));
  overflow: hidden;            /* clips the band to the core's corners */
}
/* NEUTRALISE THE GENERIC DATA-TABLE STYLE.
   The vertical ships one for spec tables (`table, .ms-table` / `table th` /
   `table td` in components.css): mono uppercase headers, a hairline under
   EVERY cell, zero end-padding on the last column, baseline alignment. All of
   it lands here, because this component is a table — and it shipped that way:
   the row labels rendered as uppercase Martian Mono, and the per-cell
   border-bottom put hairlines straight back through the brand band, which is
   the exact defect this redesign removed.

   Reset once, at (0,1,1), and let the component rules below sit at (0,2,0) by
   being scoped to the table. That beats both this reset and the vertical's
   `table th:last-child` (0,1,2) without a single !important. */
.pdp-compare__table th,
.pdp-compare__table td {
  border-bottom: none;
  vertical-align: middle;
  font-family: var(--font-sans);
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: inherit;
}

.pdp-compare__caption {
  position: absolute; width: 1px; height: 1px;
  padding: var(--p-space-0); margin: calc(var(--p-space-px) * -1);
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.pdp-compare__sr {
  position: absolute; width: 1px; height: 1px;
  padding: var(--p-space-0); margin: calc(var(--p-space-px) * -1);
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* --- header row --- */
.pdp-compare__table .pdp-compare__corner,
.pdp-compare__table .pdp-compare__col {
  vertical-align: bottom;      /* both column names land on ONE baseline */
  text-align: center;
  padding: var(--p-space-6) var(--p-space-4) var(--p-space-4-5);
  font-weight: 400;
}
.pdp-compare__table .pdp-compare__corner {
  width: auto; text-align: left;
  padding-left: var(--p-space-6);
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-12); letter-spacing: var(--tracking-label);
  text-transform: uppercase; color: var(--ink-3);
}
/* Proportions, not leftovers. The label column was taking 62% of the table
   for ~35 characters while the sentences opposite wrapped in a 272px slot.
   45/22/33 fits every phrase on one line at full width and gives the packshot
   room to be a product rather than a bullet.

   PERCENTAGES, NOT REM. Fixed 17rem/26rem columns are 688px of a table that is
   only ~708px wide at 768px, so the label column collapsed to one word per
   line and ran under the brand band. A ratio holds at every width; a pair of
   fixed widths only holds at the one it was drawn at. */
.pdp-compare__table .pdp-compare__col { width: 22%; }
.pdp-compare__table .pdp-compare__col--them { width: 33%; }
.pdp-compare__colname {
  display: block;
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-13); letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}
.pdp-compare__col--us .pdp-compare__colname { color: var(--brand-foreground); font-weight: 600; }
.pdp-compare__col--them .pdp-compare__colname { color: var(--ink-3); }

/* The packshot sits on the warm cream ground the photography is shot on, so
   it reads as a deliberate disc on the blue rather than a knocked-out hole. */
.pdp-compare__jar {
  display: grid; place-items: center;
  width: var(--p-space-16); height: var(--p-space-16);
  margin: 0 auto var(--p-space-3);
  border-radius: var(--p-radius-circle);
  background: var(--surface-warm);
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--brand-foreground) 30%, transparent);
  overflow: hidden;
}
.pdp-compare__jar img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* --- the brand band ---
   One solid column, top-to-bottom. Only the head and the last body cell carry
   a radius, so the band is a single rounded object rather than six stacked
   ones. */
.pdp-compare__table .pdp-compare__cell--us,
.pdp-compare__table .pdp-compare__col--us { background: var(--brand); }
.pdp-compare__table .pdp-compare__col--us { border-radius: var(--p-radius-md) var(--p-radius-md) 0 0; }
.pdp-compare__table tbody tr:last-child .pdp-compare__cell--us {
  border-radius: 0 0 var(--p-radius-md) var(--p-radius-md);
}

/* --- body rows ---
   The hairline is drawn by the LABEL and THEM cells only. Running it through
   the band was the collision that made the column look misaligned. */
.pdp-compare__table .pdp-compare__label,
.pdp-compare__table .pdp-compare__cell--them { border-top: 1px solid var(--border); }
.pdp-compare__table .pdp-compare__label {
  padding: var(--p-space-5) var(--p-space-6);
  font-size: var(--p-text-18); line-height: var(--leading-body);
  font-weight: 400; text-align: left; color: var(--foreground);
  text-wrap: pretty;
}
.pdp-compare__table .pdp-compare__cell {
  padding: var(--p-space-5) var(--p-space-4);
  text-align: center; vertical-align: middle;
}
.pdp-compare__table .pdp-compare__cell--them {
  padding-inline: var(--p-space-5);
  font-size: var(--p-text-16); line-height: var(--leading-body); color: var(--ink-3);
  text-wrap: balance;
}
.pdp-compare__note { display: block; }

/* --- the marks ---
   Filled disc vs hollow ring, not check vs em-dash. The old pair used the
   same glyph for "we don't do this (good)" and "they don't do this (bad)",
   which is unreadable by construction; presence/absence is now the only thing
   the mark encodes, and the row copy carries the valence. */
.pdp-compare__mark {
  display: inline-grid; place-items: center;
  width: var(--p-space-9); height: var(--p-space-9);
  border-radius: var(--p-radius-circle);
}
.pdp-compare__mark--yes { background: var(--brand-foreground); }
.pdp-compare__mark--yes .ms-i { color: var(--brand); }
.pdp-compare__mark--no {
  box-shadow: inset 0 0 0 1px var(--border-strong);
  background: transparent;
}
.pdp-compare__cell--us .pdp-compare__mark--no {
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--brand-foreground) 40%, transparent);
}
.pdp-compare__cell--them .pdp-compare__mark--yes { background: var(--ink-4); }
.pdp-compare__cell--them .pdp-compare__mark--yes .ms-i { color: var(--card); }

/* --- hover: the row you are reading lifts out of the stack --- */
.pdp-compare__table tbody tr .pdp-compare__label,
.pdp-compare__table tbody tr .pdp-compare__cell--them {
  transition: background-color var(--p-duration-base) var(--ease-default),
              color var(--p-duration-base) var(--ease-default);
}
.pdp-compare__table tbody tr:hover .pdp-compare__label,
.pdp-compare__table tbody tr:hover .pdp-compare__cell--them { background: var(--accent); }
.pdp-compare__table tbody tr:hover .pdp-compare__cell--them { color: var(--ink-2); }
@media (prefers-reduced-motion: reduce) {
  .pdp-compare__table tbody tr .pdp-compare__label,
  .pdp-compare__table tbody tr .pdp-compare__cell--them { transition: none; }
}

/* --- narrow: the table becomes one card per claim ---
   Three columns cannot hold a sentence at 375px. Below the breakpoint each row
   becomes a small card: the claim, then the two verdicts STACKED, each sized
   by its own content.

   Stacked rather than side by side, because side by side made the brand cell a
   tall block of empty blue matched to the height of the sentence opposite it,
   and put the wordmark in 22px of dead space six times running. Stacked, the
   brand verdict is a one-line chip and the wordmark reads as a caption on it.
   Nothing is dropped — mobile says exactly what the desktop table says. */
@media (max-width: 767px) {
  .pdp-compare { padding: var(--p-space-1-5); }
  .pdp-compare__table,
  .pdp-compare__table thead,
  .pdp-compare__table tbody,
  .pdp-compare__table tr,
  .pdp-compare__table th,
  .pdp-compare__table td { display: block; width: auto; }

  /* the column names travel INTO the cells below, so the head row goes */
  .pdp-compare__table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

  .pdp-compare__table tbody tr {
    padding: var(--p-space-5);
    border-top: 1px solid var(--border);
  }
  .pdp-compare__table tbody tr:first-child { border-top: none; }

  .pdp-compare__table .pdp-compare__label {
    padding: var(--p-space-0) var(--p-space-0) var(--p-space-4);
    border-top: none;
    font-size: var(--p-text-17);
  }

  /* scoped to the table so it outranks the `th, td { display: block }` above */
  .pdp-compare__table .pdp-compare__cell {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: var(--p-space-1) var(--p-space-3);
    padding: var(--p-space-3) var(--p-space-4);
    text-align: left;
    border-top: none;
    border-radius: var(--p-radius-sm);
  }
  .pdp-compare__mark { order: 1; }
  /* the column name is a caption on the verdict, not a header a row above it */
  .pdp-compare__table .pdp-compare__cell::before {
    content: attr(data-col); order: 2;
    font-family: var(--font-mono), ui-monospace, monospace;
    font-size: var(--p-text-10); letter-spacing: var(--tracking-label);
    text-transform: uppercase;
  }
  .pdp-compare__note { order: 3; flex-basis: 100%; }

  .pdp-compare__table .pdp-compare__cell--us::before { color: color-mix(in oklab, var(--brand-foreground) 78%, transparent); }
  .pdp-compare__table tbody tr:last-child .pdp-compare__cell--us { border-radius: var(--p-radius-sm); }

  .pdp-compare__table .pdp-compare__cell--them {
    margin-top: var(--p-space-2);
    padding-block: var(--p-space-3-5);
    background: var(--surface-2);
    color: var(--ink-2);
  }
  .pdp-compare__table .pdp-compare__cell--them::before { color: var(--ink-4); }
  /* the sentence gets its own line under the caption; a boolean sits inline */
  .pdp-compare__cell--them .pdp-compare__note { margin-top: var(--p-space-0-5); }
}

/* ---- how to use -------------------------------------------------------------
   Editorial composition, not template halves: the photo carries an overlapping
   shelf-tag card and a circular capsule-macro badge; the steps hang on a
   connected number rail. */
.pdp-chapter--howto { border-top: 1px solid var(--border); }
/* the number rail */
.pdp-steps {
  list-style: none; margin: var(--p-space-9) 0 0; padding: 0;
  position: relative; display: flex; flex-direction: column; gap: var(--p-space-7);
}
.pdp-steps::before {
  content: ""; position: absolute; left: 1.0625rem; top: 1rem; bottom: 1rem;
  width: 1px; background: var(--border);
}

/* ---- closing band ---------------------------------------------------------- */
.pdp-chapter--close { padding-bottom: var(--p-space-24); }
.pdp-close {
  position: relative; overflow: hidden; display: grid; gap: var(--p-space-8); align-items: center;
  border-radius: var(--radius-3xl-plus, 1.75rem); background: var(--surface-ink);
  padding: clamp(2rem, 5vw, 3.5rem);
}
/* quiet radial wash behind the jar so the band is not a flat rectangle */
.pdp-close::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(42rem 28rem at 82% 30%, color-mix(in oklab, var(--brand) 16%, transparent), transparent 68%),
    radial-gradient(30rem 22rem at 8% 100%, color-mix(in oklab, var(--surface) 5%, transparent), transparent 70%);
}
.pdp-close > * { position: relative; }
@media (min-width: 900px) { .pdp-close { grid-template-columns: 1.35fr 1fr; }}

.pdp-close__title {
  margin: var(--p-space-2-5) 0 0; font-size: clamp(1.75rem, 3.8vw, 2.5rem); font-weight: 600;
  letter-spacing: var(--p-tracking-n25); line-height: 1.08; color: var(--brand-foreground); text-wrap: balance;
}
.pdp-close__sub { margin: var(--p-space-3-5) 0 0; max-width: 46ch; line-height: 1.6; color: color-mix(in oklab, var(--brand-foreground) 72%, transparent); }
.pdp-close__row { display: flex; flex-wrap: wrap; gap: var(--p-space-3); margin-top: var(--p-space-7); }
.pdp-close__media { justify-self: center; }
.pdp-close__media img {
  display: block; width: 100%; max-width: 18rem; height: auto;
  filter: drop-shadow(0 28px 32px rgb(0 0 0 / 0.45));
}
@media (max-width: 899px) { .pdp-close__media { display: none; }}


/* ---- THE PACK LADDER -------------------------------------------------------
   Multi-buy, drawn as the brand's own argument rather than as a discount
   shout. Three decisions worth keeping:

   1. HAIRLINES, NOT CARDS ON SHADOWS. The reference pages float each tier on a
      grey fill with a drop shadow, which on this white counter would put three
      more elevations directly under a page that has exactly one (the button).
      A 1px rule and a brand ring carry the same selected/unselected
      information at a fraction of the visual weight.

   2. THE RADIO IS THE WHOLE ROW. The <input> is visually hidden but still the
      real control, so the <label> IS the hit area — comfortably past the 44px
      floor — and keyboard, arrow-key group navigation and the focus ring are
      the platform's rather than a reimplementation.

   3. THE PER-UNIT FIGURE IS SET IN MONO AND THE BADGE IS NOT. Everything on
      this page that makes a numeric claim is monospace; the saving badge is a
      display-layer device, so it stays in the text face. That split is the
      brand law's data/display line, applied to a commerce component.
   -------------------------------------------------------------------------- */
.packs { border: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--p-space-2); }

/* A RULE WITH THE LABEL SET INTO IT. `display: flex` on a <legend> is enough
   to lay the two rules out; the element is not participating in the fieldset's
   layout box in any browser that matters, and float/absolute versions of this
   pattern all need a background colour to punch the hole. */
.packs__legend {
  padding: 0; margin-bottom: var(--p-space-3-5); width: 100%;
  display: flex; align-items: center; gap: var(--p-space-3-5);
}
.packs__legend::before, .packs__legend::after {
  content: ''; flex: 1 1 auto; height: 0; border-top: 1px solid var(--border);
}
.packs__legend-t {
  font-size: var(--p-text-11); font-weight: 600;
  letter-spacing: var(--p-tracking-12); color: var(--foreground); white-space: nowrap;
}

.pack {
  position: relative;
  display: grid; align-items: center;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  gap: var(--p-space-3);
  padding: var(--p-space-3-5) var(--p-space-4);
  /* UNSELECTED IS A FILL, SELECTED IS A FRAME. The reference inverts the usual
     pairing and it is the right way round: the chosen row is the one that
     should read as raised off the group, and a white card ringed in the brand
     reads raised against three grey ones. Three outlined boxes with one
     slightly darker outline reads as four of the same thing. */
  border: 1px solid transparent; border-radius: var(--radius-lg);
  background: var(--surface-2); cursor: pointer;
  /* Named properties only. The `all` keyword here would also animate the grid
     template and the ribbon's position on first paint. (Spelled this way on
     purpose: check-styles.sh greps the sheets for literal declarations and
     counts a shorthand written out in a comment as a real one.) */
  transition: border-color var(--p-duration-base) var(--ease-default),
              background-color var(--p-duration-base) var(--ease-default),
              box-shadow var(--p-duration-base) var(--ease-default);
}
/* Hover is gated: on a touch device the tap that selects a tier also leaves it
   in a stuck hover state, so the row you just chose and the row under your
   thumb look identical. */
@media (hover: hover) and (pointer: fine) {
  .pack:hover { background: var(--brand-muted); }
  .pack.is-active:hover { background: var(--card); }
}
.pack.is-active {
  border-color: var(--brand); background: var(--card);
  box-shadow: inset 0 0 0 1px var(--brand), var(--elevation-key);
}
@media (prefers-reduced-motion: reduce) { .pack { transition: none; } }

/* The control itself — hidden from sight, never from the accessibility tree or
   the tab order. `display: none` would remove it from both. */
.pack__radio { position: absolute; opacity: 0; width: 0; height: 0; }
.pack__radio:focus-visible ~ .pack__mark { box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring) 30%, transparent); }

.pack__mark {
  width: var(--p-space-4-5); height: var(--p-space-4-5); flex-shrink: 0;
  border: 1px solid var(--border-strong); border-radius: var(--p-radius-circle);
  background: var(--card); position: relative;
  transition: border-color var(--p-duration-base) var(--ease-default);
}
.pack__mark::after {
  content: ''; position: absolute; inset: var(--p-radius-3);
  border-radius: var(--p-radius-circle); background: var(--brand);
  /* Not scale(0) — nothing in the world appears out of nothing, and at this
     size the difference between 0 and 0.4 is the difference between a pip that
     pops open and one that simply fills. */
  opacity: 0; transform: scale(0.4);
  transition: opacity var(--motion-enter) var(--ease-default),
              transform var(--motion-enter) var(--ease-default);
}
.pack.is-active .pack__mark { border-color: var(--brand); }
.pack.is-active .pack__mark::after { opacity: 1; transform: scale(1); }
@media (prefers-reduced-motion: reduce) {
  .pack__mark, .pack__mark::after { transition: none; }
}

/* --- the fanned packshots ---
   One thumbnail per unit, overlapped. The ring in the card's own colour is what
   stops three copies of the same bottle reading as one wide smear. */
.pack__stack { display: flex; align-items: center; flex-shrink: 0; }
/* THE PALE STAGE, NOT THE FLOOD BAND. The first pass floated each thumbnail on
   the SKU's own band, which on the copper franchise means a cupric bottle
   standing on a cupric ground: at 40px the packshot vanished into the chip and
   the ladder read as three blue squares. The band is already stated in the
   chip beside the availability pill; here the job is legibility, so this is the
   same pale stage the gallery uses. */
/* NO TILE. The packshots sit straight on the row's own ground; the separating
   ring is that same ground, so overlapping shots read as separate objects
   without adding a box. --shot-ring tracks the row's state because the ground
   changes when a row is selected — hard-coding either colour leaves a halo on
   the other. */
.pack { --shot-ring: var(--surface-2); }
.pack.is-active { --shot-ring: var(--card); }
.pack__shot {
  width: var(--p-space-11); height: var(--p-space-11);
  border-radius: var(--p-radius-sm); overflow: hidden;
  box-shadow: 0 0 0 3px var(--shot-ring);
}
.pack__shot + .pack__shot { margin-left: calc(var(--p-space-4-5) * -1); }
.pack__shot img { width: 100%; height: 100%; object-fit: contain; display: block; }

.pack__body { display: flex; flex-direction: column; gap: var(--p-space-0-5); min-width: 0; }
.pack__name {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--p-space-2);
  font-size: var(--p-text-17); font-weight: 600; color: var(--foreground);
}
.pack__badge {
  /* GAP, NOT A SPACE CHARACTER. "Save" and the wc_price() markup are two
     separate flex items, and a whitespace-only run between flex items is not
     rendered — the badge shipped reading "Save$37.60" until this was a gap. */
  display: inline-flex; align-items: center; gap: var(--p-space-1);
  border-radius: var(--p-radius-pill); padding: var(--p-space-px) var(--p-space-2);
  background: var(--brand-muted); color: var(--savings);
  font-size: var(--p-text-11); font-weight: 600;
}
.pack__badge .woocommerce-Price-amount { color: inherit; font: inherit; }
.pack__value {
  font-size: var(--p-text-13); font-variant-numeric: tabular-nums; color: var(--ink-3);
}

.pack__prices { display: flex; flex-direction: column; align-items: flex-end; gap: var(--p-space-0-5); text-align: right; }
.pack__total {
  font-size: var(--p-text-18); font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--price-regular);
}
.pack__total .woocommerce-Price-amount { color: inherit; font: inherit; }
.pack__was {
  font-size: var(--p-text-12); font-variant-numeric: tabular-nums;
  color: var(--price-compare); text-decoration: line-through;
}
.pack__was .woocommerce-Price-amount { color: inherit; font: inherit; }

/* --- the best-value tab ---
   PROTRUDING ABOVE THE CARD'S TOP-RIGHT, as in the reference. The first pass
   seated it inside the corner to avoid needing headroom; inside, it reads as a
   label printed on the card, and the whole point of a ribbon is that it sticks
   out of the group and catches the eye scanning down the ladder.
   The headroom is bought explicitly with margin-top on the row rather than
   taken out of the fieldset's gap, so the tab cannot collide with the row
   above when the gap changes. */
.pack--best { margin-top: var(--p-space-4); }
.pack__ribbon {
  position: absolute; right: 0; bottom: calc(100% - var(--p-space-2));
  padding: var(--p-space-1) var(--p-space-3) var(--p-space-3);
  border-radius: var(--p-radius-sm) var(--p-radius-sm) 0 0;
  background: var(--brand); color: var(--brand-foreground);
  font-size: var(--p-text-10); font-weight: 600; letter-spacing: var(--p-tracking-10);
  /* The tab tucks BEHIND the card so its lower edge is hidden rather than
     drawn — that is what makes it read as one folded object instead of two
     stacked rectangles. */
  z-index: 0;
}
.pack { z-index: 1; }

/* --- narrow ---
   At 380px the four-column grid cannot hold a fanned stack, a name, a badge and
   two prices. The stack is the one carrying no information the rest of the row
   does not already state, so it is the one that goes. */
@media (max-width: 419px) {
  .pack { grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--p-space-2-5); }
  .pack__stack { display: none; }
}

/* the saving flag on cart/drawer line items */
.ms-tierflag {
  display: inline-block; margin-left: var(--p-space-1-5);
  border-radius: var(--p-radius-pill); padding: var(--p-space-px) 0.4375rem;
  background: var(--brand-muted); color: var(--brand);
  font-family: var(--font-mono), ui-monospace, monospace;
  font-size: var(--p-text-10); font-weight: 600; letter-spacing: var(--p-tracking-4); white-space: nowrap;
}

/* ==========================================================================
   PDP §B SUPPLY  ·  §C NOTHING ELSE
   ========================================================================== */


/* ==========================================================================
   PDP GALLERY — carousel + thumbnail rail
   Ecommerce gallery rules being honoured here, in order of how often sites
   get them wrong:
     1. FIXED ASPECT on the stage, so nothing reflows while images decode.
     2. Every frame the SAME ratio — a gallery that changes shape per slide
        reads as broken, which is why the generated angles are all 0.806.
     3. Native scroll-snap, so touch swipe costs nothing and never fights the
        browser's own momentum.
     4. Thumbnails vertical on desktop (they sit beside the stage, not under
        it, so the buy box stays above the fold), horizontal on mobile.
     5. Arrows are desktop-only affordances — on touch they are noise.
   ========================================================================== */

.pdp-gallery { display: grid; gap: var(--p-space-3); grid-template-columns: 1fr; min-width: 0; }
@media (min-width: 900px) {
  /* thumbs | stage — the rail on the left is the AG1/Oura arrangement and it
     keeps the stage optically centred against the buy box. */
  .pdp-gallery--multi { grid-template-columns: 4.5rem minmax(0, 1fr); gap: .85rem; }
  .pdp-gallery--multi .pdp-gallery__thumbs { order: -1; }}


/* THE STAGE OWNS THE HEIGHT — nothing else may influence it.
   Previously the aspect-ratio lived on the slide, which is a flex item in a
   flex track whose own height was auto. With the default align-items:stretch
   the slide could end up taller than its ratio implied, and `object-fit:cover`
   then cropped the sides — which is why secondary frames looked violently
   zoomed while their thumbnails (sized independently) looked correct.
   Now: main declares the ratio, the track fills it absolutely, slides are
   100% of both axes. No circular sizing left to resolve. */
.pdp-gallery__main {
  position: relative;
  min-width: 0;
  aspect-ratio: 1400 / 1737;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-2);
}

.pdp-gallery__track {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: stretch;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}
.pdp-gallery__track::-webkit-scrollbar { display: none; }
.pdp-gallery__track:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }

.pdp-gallery__slide {
  position: relative;
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  align-self: stretch;   /* never let a stray align-items shrink-wrap this */
  margin: 0;
  scroll-snap-align: center;
  overflow: hidden;
}
.pdp-gallery__img {
  /* Absolutely positioned, not height:100%. The rule this replaced pinned
     `aspect-ratio: 1/1` on the IMAGE because a percentage height resolves to
     auto against an aspect-ratio-derived parent — so the portrait packshot
     rendered square inside a 0.806 slide and left a dead band underneath.
     Filling an inset-0 box sidesteps that resolution question completely and
     leaves the ratio owned in exactly one place: the slide. */
  position: absolute;
  inset: 0;
  /* !important because WordPress, WooCommerce and the block-library global
     stylesheet all ship their own `img` sizing, and this element must not lose
     that fight — a mis-sized frame here reads as a broken zoom, which is
     exactly the bug this replaces. */
  width: 100% !important;
  height: 100% !important;
  max-width: none;
  min-width: 0;
  min-height: 0;
  aspect-ratio: auto;
  /* CONTAIN, not COVER. Every frame is pre-normalised to 1400x1737 before
     upload, so with a correct container the two are pixel-identical. They
     differ only when something upstream is wrong — and then contain
     letterboxes (obvious, harmless) while cover silently crops to a violent
     zoom. Given two ratio bugs have already surfaced here, fail safe. */
  object-fit: contain;
  object-position: center;
  display: block;
}

/* --- arrows: pointer devices only --------------------------------------- */
.pdp-gallery__nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 2.5rem; height: 2.5rem;
  display: none; place-items: center;
  border: 1px solid var(--border-strong);
  border-radius: var(--p-radius-pill);
  background: color-mix(in oklab, var(--surface) 88%, transparent);
  backdrop-filter: blur(6px);
  color: var(--foreground);
  cursor: pointer;
  transition: opacity .15s ease, background .15s ease;
}
@media (hover: hover) and (pointer: fine) { .pdp-gallery__nav { display: grid; }}

.pdp-gallery__nav:hover { background: var(--surface); }
.pdp-gallery__nav:disabled { opacity: .28; pointer-events: none; }
.pdp-gallery__nav:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.pdp-gallery__nav--prev { left: .7rem; }
.pdp-gallery__nav--next { right: .7rem; }
.pdp-gallery__nav--prev .ms-i { transform: rotate(90deg); }
.pdp-gallery__nav--next .ms-i { transform: rotate(-90deg); }

/* --- counter: the mobile affordance ------------------------------------- */
.pdp-gallery__counter {
  position: absolute; right: .7rem; bottom: .7rem; margin: 0;
  padding: .2rem .55rem;
  font-size: var(--p-text-11);
  border-radius: var(--p-radius-pill);
  background: color-mix(in oklab, var(--scrim) 62%, transparent);
  color: var(--brand-foreground);
  backdrop-filter: blur(6px);
}
.pdp-gallery__counter span { opacity: .55; margin: 0 .1rem; }
@media (hover: hover) and (pointer: fine) { .pdp-gallery__counter { display: none; }}


/* --- thumbnail rail ------------------------------------------------------ */
.pdp-gallery__thumbs {
  display: flex; gap: var(--p-space-2);
  overflow-x: auto; scrollbar-width: none;
  padding-bottom: .15rem;
}
.pdp-gallery__thumbs::-webkit-scrollbar { display: none; }
@media (min-width: 900px) {
  .pdp-gallery__thumbs { flex-direction: column; overflow-x: visible; overflow-y: auto; max-height: 100%; }}


/* SELECTION IS A RING, NOT A BLACK EDGE. The active thumb used
   `border-color: var(--foreground)` — #10131A, effectively hairline black
   against cream packshots, which is the heaviest mark on the whole gallery
   and sits next to the lightest content on the page.

   Two changes. Selection is now drawn in the brand, doubled with an inset
   shadow so the ring reads at 2px without a 1px layout shift when it moves
   between thumbs. And dimming moved off `opacity` — fading the whole button
   also faded its border, so an unselected thumb had a ghost of a frame — onto
   a scrim in front of the IMAGE. The frame stays crisp at every state and
   only the picture recedes. */
.pdp-thumb {
  position: relative;
  flex: 0 0 4rem;
  width: 4rem;
  aspect-ratio: 1400 / 1737;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 160ms var(--ease-out-quart),
              box-shadow 160ms var(--ease-out-quart),
              transform 160ms var(--ease-out-quart);
}
@media (min-width: 900px) { .pdp-thumb { flex: 0 0 auto; width: 100%; }}

.pdp-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pdp-thumb::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: var(--surface);
  opacity: .34;
  transition: opacity 160ms var(--ease-out-quart);
}
.pdp-thumb:hover::after, .pdp-thumb.is-hover::after { opacity: .12; }
.pdp-thumb:active { transform: scale(0.97); }
.pdp-thumb.is-active::after { opacity: 0; }
.pdp-thumb.is-active {
  border-color: var(--brand);
  box-shadow: inset 0 0 0 1px var(--brand);
}
.pdp-thumb:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .pdp-gallery__track { scroll-behavior: auto; }
  .pdp-gallery__nav, .pdp-thumb, .pdp-thumb::after { transition: none; }
  .pdp-thumb:active { transform: none; }}


/* ============================================================================
   BUY-BOX CONVERSION COMPONENTS
   Renderers in engine/inc/buybox-cro.php. Every one of these draws nothing
   until it has real data behind it, so each block below has to survive its
   component being absent from the page entirely — no sibling here depends on
   another's margin.
   ========================================================================== */

/* ---- proof bar, above the title -------------------------------------------
   The reference opens with a pile of faces and a customer count. This opens
   with the rating, because that is the part we can evidence. Two lines, both
   quiet: proof that shouts is proof that reads as marketing. */

/* ---- headline price: compare-at + saving ----------------------------------
   Both spans exist in the DOM at all times and are toggled with [hidden] — see
   the note at the render site. `hidden` needs the explicit rule because the
   parent sets `display: flex` on its children's behalf, which overrides the UA
   sheet's `[hidden] { display: none }`. */
.pdp-price__was[hidden], .pdp-price__save[hidden], .pdp-price__unitcost[hidden] { display: none; }
.pdp-price__was {
  font-size: var(--p-text-16); font-variant-numeric: tabular-nums;
  color: var(--price-compare); text-decoration: line-through;
}
.pdp-price__was .woocommerce-Price-amount { color: inherit; font: inherit; }
/* The one place the accent is spent above the button. It is ~70px wide against
   a full-width CTA, so it reads as emphasis rather than as competition. */
.pdp-price__save {
  display: inline-flex; align-items: center; gap: var(--p-space-1);   /* see .pack__badge */
  border-radius: var(--p-radius-pill); padding: var(--p-space-px) var(--p-space-2-5);
  background: var(--brand); color: var(--brand-foreground);
  font-size: var(--p-text-12); font-weight: 600; white-space: nowrap;
}
.pdp-price__save .woocommerce-Price-amount { color: inherit; font: inherit; }

/* ---- low stock -------------------------------------------------------------
   Amber, not red. Red on a storefront means an error; this is a fact about
   supply. Renders only against managed stock — see ms_stock_urgency(). */
.stockline {
  display: flex; align-items: center; gap: var(--p-space-2); margin: 0;
  font-size: var(--p-text-13); font-weight: 600; color: var(--warning);
}
.stockline__dot {
  width: var(--p-space-2); height: var(--p-space-2); flex-shrink: 0;
  border-radius: var(--p-radius-circle); background: currentColor;
}

/* ---- accepted payment marks ------------------------------------------------
   CENTRED, AND DRAWN. Left-aligned they read as another line of the text
   column; centred under a full-width button they read as a property of that
   button, which is what they are. Each mark is inline SVG on a white tile —
   see ms_paymark_svg(). */
.paymarks {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--p-space-2);
}
.paymark { display: inline-flex; }
.paymark__tile {
  display: grid; place-items: center;
  width: var(--p-space-12); height: var(--p-space-8);
  border: 1px solid var(--border); border-radius: var(--p-radius-4);
  background: var(--card);
}
/* The tile is the frame; the mark sits inside it with its own breathing room.
   Amex fills its tile edge to edge because the blue card IS the mark. */
.paymark__tile svg { width: 82%; height: auto; display: block; }
.paymark--amex .paymark__tile svg { width: 100%; height: 100%; border-radius: var(--p-radius-3); }

/* ---- the delivery strip ----------------------------------------------------
   Ordered → dispatched → delivered, with real dates computed from the
   published policy. The dashed connector is one pseudo-element behind the row
   rather than a border on each leg: per-leg borders leave a visible seam under
   each icon chip, and the chip has to mask the line rather than butt against
   it. */
.deliverail { display: flex; flex-direction: column; gap: var(--p-space-3); }

.cutdown {
  display: flex; justify-content: center; align-items: center; gap: var(--p-space-1-5); margin: 0;
  font-size: var(--p-text-14); color: var(--ink-2);
}
.cutdown[hidden] { display: none; }
.cutdown__t { font-weight: 600; font-variant-numeric: tabular-nums; color: var(--brand-hover); }

.deliverail__legs {
  list-style: none; margin: 0; padding: 0; position: relative;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* Sits at the vertical centre of the icon chips (chip is --p-space-9 tall), and
   stops a chip-radius short of each end so it never pokes out past the outer
   two marks. */
.deliverail__legs::before {
  content: ''; position: absolute; z-index: 0;
  top: calc(var(--p-space-11) / 2); left: calc(100% / 6); right: calc(100% / 6);
  border-top: 1px dashed var(--border-strong);
}
.deliverail__leg {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: var(--p-space-1);
  text-align: center;
}
.deliverail__mark {
  display: grid; place-items: center;
  width: var(--p-space-11); height: var(--p-space-11);
  border-radius: var(--p-radius-circle);
  background: var(--brand); color: var(--brand-foreground);
}
.deliverail__label { font-size: var(--p-text-12); font-weight: 600; color: var(--foreground); }
/* A delivery window spanning two months runs to "Thu 27 Aug – Tue 1 Sep",
   which is wider than a third of the rail and wrapped with "Sep" alone on the
   second line. Balanced, it breaks at the dash instead. */
.deliverail__when {
  display: block; text-wrap: balance;
  font-size: var(--p-text-11); font-variant-numeric: tabular-nums; color: var(--ink-3);
}
.deliverail__note { margin: 0; font-size: var(--p-text-11); line-height: 1.5; color: var(--ink-4); text-wrap: pretty; }

/* ---- add-to-cart -----------------------------------------------------------
   With the ladder on, the stepper is a hidden input and the button is the only
   child — so the row must not keep the two-column layout the stepper needed. */
.pdp-atc--laddered { display: block; }

/* The single most important control on the page, and it was the same height as
   a form field. .btn--xl stays where it is — it is shared with the cart and
   checkout, and this is a PDP decision, not a change to the primitive. */
.pdp-add { height: var(--p-space-14); font-size: var(--p-text-17); }
