@import url("fonts.css");

/* ===========================================================================
   AlphaPepsOfficial — design system

   Modelled on the supplied apple.com "Explore the lineup." reference: a light
   #f5f5f7 ground, very large tightly-tracked display type, generously rounded
   product tiles on soft gradient grounds, colour dots beneath each tile, and
   the product name centred below.

   Type is Inter / Inter Tight. The reference is set in SF Pro Display, which
   Apple licenses for Apple-platform development only and cannot ship on a
   commercial storefront; Inter is the closest freely-licensable match and the
   scale below is tuned against the reference.

   Motion: entrance reveals, tile hover/press, gate and modal transitions,
   count-ups and slider feedback. No scroll hijacking, pinning or parallax.
   =========================================================================== */

:root {
  /* Surfaces --------------------------------------------------------------- */
  --white: #ffffff;
  --paper: #f5f5f7;      /* the reference page ground */
  --paper-2: #ebecf0;
  --ink: #1d1d1f;        /* the reference near-black */
  --ink-2: #2a2a2e;

  /* Brand ------------------------------------------------------------------ */
  --blue: #0b6ee5;
  --blue-deep: #0a5cc0;
  --blue-hi: #63a9ff;
  --blue-wash: #eef5ff;

  /* Text ------------------------------------------------------------------- */
  --fg: var(--ink);
  --fg-dim: #4a4a4f;
  --fg-mute: #6e6e73;    /* the reference secondary grey */
  --on-dark: #f5f5f7;
  --on-dark-dim: #a1a1a6;

  --ok: #1a8a4a;
  --ok-wash: #e8f6ee;
  --warn: #a8620a;
  --warn-wash: #fdf3e6;
  --off: #86868b;

  --line: rgba(29, 29, 31, 0.10);
  --line-2: rgba(29, 29, 31, 0.06);
  --line-dark: rgba(255, 255, 255, 0.14);

  /* Type ------------------------------------------------------------------- */
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --display: "Inter Tight", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* Not a second family: the reference uses one face throughout. Technical
     labels read as technical through tracking, weight and tabular figures. */
  --mono: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  --t-micro: 0.6875rem;
  --t-xs: 0.8125rem;
  --t-sm: 0.9375rem;
  --t-base: 1.0625rem;
  --t-lede: clamp(1.15rem, 1.02rem + 0.58vw, 1.5rem);
  --t-h3: clamp(1.15rem, 1.06rem + 0.4vw, 1.4rem);
  --t-tile: clamp(1.35rem, 1.1rem + 1.1vw, 1.9rem);   /* product name under a tile */
  --t-d3: clamp(1.5rem, 1.28rem + 0.95vw, 2.1rem);
  --t-d2: clamp(2.3rem, 1.5rem + 3.4vw, 4.5rem);
  --t-d1: clamp(2.9rem, 1.7rem + 5.2vw, 6.5rem);

  /* Geometry --------------------------------------------------------------- */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 18px;
  --r-xl: 28px;
  --r-tile: clamp(18px, 2vw, 28px);

  --gut: clamp(1.15rem, 4vw, 3.5rem);
  --maxw: 1320px;
  --band-y: clamp(4.5rem, 9vw, 9rem);

  --shadow-sm: 0 1px 2px rgba(29, 29, 31, 0.05), 0 4px 12px rgba(29, 29, 31, 0.04);
  --shadow-lift: 0 20px 48px -20px rgba(29, 29, 31, 0.22), 0 2px 8px rgba(29, 29, 31, 0.05);
  --shadow-modal: 0 40px 100px -20px rgba(0, 0, 0, 0.35), 0 12px 32px rgba(0, 0, 0, 0.14);

  /* Apple-ish easing: quick out, long settle. */
  --ease: cubic-bezier(0.28, 0.11, 0.32, 1);
  --ease-io: cubic-bezier(0.65, 0, 0.35, 1);
  --spring: cubic-bezier(0.34, 1.4, 0.5, 1);

  --nav-h: 48px;
}

/* Reset ---------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 24px);
  /* Clip at the root, not just on body. An overflow value on <body> is
     propagated to the viewport, which means body stops clipping its own
     children — so the drifting glow layer escaped and widened the document. */
  overflow-x: clip;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  /* clip, never hidden: overflow-x:hidden silently forces overflow-y:auto,
     which moves the scroll container onto <body> and breaks sticky. */
  overflow-x: clip;
  background: var(--white);
  color: var(--fg);
  font-family: var(--sans);
  font-size: var(--t-base);
  line-height: 1.55;
  letter-spacing: -0.011em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
/* While the gate is up the page behind it must not scroll. */
body.gated { overflow-y: hidden; }   /* y only: overflow:hidden would reset the x-axis clip */
/* Applied by markup, not script, so there is no scrollable frame before JS runs. */
html:not(.gate-done) body:has(.gate) { overflow-y: hidden; }

h1, h2, h3, h4 { font-family: var(--display); font-weight: 640; line-height: 1.06; text-wrap: balance; }
p { text-wrap: pretty; }
ul, ol { list-style: none; }
dl, dd { margin: 0; }
/* The hidden attribute has to beat every component's own display.

   `[hidden] { display: none }` lives in the user-agent stylesheet, so any
   author rule outranks it — and `.pc { display: flex }` did exactly that,
   which meant the catalogue filters set the attribute on 35 of 38 cards and
   all 38 stayed on screen. The product count was right, so it read as
   working. Patching it per component is what produced .gate[hidden] and
   .tabpanel[hidden] below; this rule is here so the next component with a
   display value does not quietly break filtering all over again. */
[hidden] { display: none !important; }

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }

:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; border-radius: 5px; }

.skip {
  position: fixed; top: -100px; left: 1rem; z-index: 300;
  padding: 0.7rem 1.1rem; border-radius: var(--r-sm);
  background: var(--ink); color: var(--white); font-size: var(--t-sm);
}
.skip:focus { top: 1rem; }

.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

/* Layout ---------------------------------------------------------------------- */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gut); }
.wrap--tight { max-width: 920px; }

.band { position: relative; padding-block: var(--band-y); }
.band--paper { background: var(--paper); }

.band--dark { background: var(--ink); color: var(--on-dark); }
.band--dark .lede, .band--dark .copy { color: var(--on-dark-dim); }
.band--dark .copy strong { color: var(--white); }
.band--dark .blue { color: var(--blue-hi); }

/* Type ------------------------------------------------------------------------ */
.d1 { font-size: var(--t-d1); font-weight: 680; letter-spacing: -0.028em; line-height: 1.02; }
/* --- Slogan tracking -------------------------------------------------------
   "Performance" broke at -0.028em: at 102px that is -2.86px a gap, and the
   r's arm ran straight into the f, reading as one letter. The slogan is the
   largest type on the site so it is the first place tracking that tight will
   show. Loosened here rather than on .d1, so the other display headings keep
   the tighter setting they were drawn with. */
.hero__title { letter-spacing: -0.012em; }
.d2 { font-size: var(--t-d2); font-weight: 680; letter-spacing: -0.026em; line-height: 1.05; }
.d3 { font-size: var(--t-d3); font-weight: 660; letter-spacing: -0.022em; line-height: 1.12; }

.kicker {
  display: inline-flex; align-items: center; gap: 0.55rem;
  font-family: var(--mono); font-size: var(--t-micro); font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--blue);
  font-variant-numeric: tabular-nums;
}
.kicker::before { content: ""; width: 20px; height: 1px; background: currentColor; }
.band--dark .kicker { color: var(--blue-hi); }

.lede { font-size: var(--t-lede); line-height: 1.42; color: var(--fg-mute); max-width: 46ch; letter-spacing: -0.016em; }
.copy { color: var(--fg-mute); max-width: 66ch; line-height: 1.62; }
.copy + .copy { margin-top: 1rem; }
.copy strong { color: var(--fg); font-weight: 600; }
.blue { color: var(--blue); }

.brk { display: none; }
@media (min-width: 760px) { .brk { display: inline; } }

/* Buttons ---------------------------------------------------------------------- */
/* Pill buttons at the reference's proportions: compact, high radius, quiet. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  min-height: 46px; padding: 0 1.35rem;
  border: 1px solid transparent; border-radius: 999px;
  font-size: var(--t-sm); font-weight: 560; letter-spacing: -0.012em;
  white-space: nowrap; cursor: pointer;
  transition: background 0.3s var(--ease), color 0.3s var(--ease),
              border-color 0.3s var(--ease), transform 0.25s var(--ease),
              box-shadow 0.3s var(--ease), opacity 0.25s var(--ease);
}
.btn svg { transition: transform 0.35s var(--ease); }
.btn:hover svg { transform: translateX(3px); }
.btn:active { transform: scale(0.975); }
.btn[disabled] { opacity: 0.38; cursor: not-allowed; pointer-events: none; }

.btn--blue { background: var(--blue); color: var(--white); }
.btn--blue:hover { background: var(--blue-deep); }

.btn--dark { background: var(--ink); color: var(--white); }
.btn--dark:hover { background: var(--ink-2); }

.btn--wire { border-color: var(--line); color: var(--fg); background: var(--white); }
.btn--wire:hover { border-color: rgba(29,29,31,0.28); background: var(--paper); }
.band--dark .btn--wire { background: transparent; border-color: var(--line-dark); color: var(--white); }
.band--dark .btn--wire:hover { border-color: rgba(255,255,255,0.45); background: rgba(255,255,255,0.07); }

.btn--sm { min-height: 36px; padding: 0 0.95rem; font-size: var(--t-xs); }
.btn--danger { background: #c2341d; color: #fff; }
.btn--danger:hover { background: #a52c18; }

/* The reference's inline link style: blue, chevron, underline on hover only. */
.tlink {
  display: inline-flex; align-items: center; gap: 0.35rem;
  min-height: 44px;
  font-size: var(--t-base); font-weight: 450; color: var(--blue); letter-spacing: -0.014em;
}
.band--dark .tlink { color: var(--blue-hi); }
.tlink u { text-decoration: none; }
.tlink:hover u { text-decoration: underline; text-underline-offset: 3px; }
.tlink svg { transition: transform 0.35s var(--ease); }
.tlink:hover svg { transform: translateX(3px); }

.acts { display: flex; flex-wrap: wrap; gap: 0.7rem; }

/* Research-use ribbon ----------------------------------------------------------- */
.ruo { background: var(--ink); color: var(--on-dark-dim); }
.ruo p {
  padding-block: 0.5rem; text-align: center;
  font-family: var(--mono); font-size: 0.625rem;
  letter-spacing: 0.16em; text-transform: uppercase;
}

/* Header ------------------------------------------------------------------------ */
/* Slim and translucent, like the reference's global bar. */
.nav {
  position: sticky; top: 0; z-index: 100;
  border-bottom: 1px solid transparent;
  background: rgba(245, 245, 247, 0.72);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  transition: border-color 0.35s var(--ease), background 0.35s var(--ease);
}
.nav.solid { border-bottom-color: var(--line); background: rgba(245, 245, 247, 0.9); }

.nav__in { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-height: var(--nav-h); }

.brand { display: inline-flex; align-items: center; gap: 0.5rem; }
.brand img { width: 26px; height: 26px; border-radius: 50%; flex: none; }
.brand span { font-family: var(--display); font-size: 1rem; font-weight: 660; letter-spacing: -0.03em; }
.brand em { font-style: normal; font-weight: 430; color: var(--fg-mute); }
.band--dark .brand span, .foot .brand span { color: var(--white); }
.foot .brand em { color: var(--on-dark-dim); }

.nav__links { display: none; gap: 0.15rem; }
.nav__links a {
  padding: 0.35rem 0.7rem; border-radius: 999px;
  font-size: var(--t-xs); font-weight: 450; color: var(--fg);
  transition: color 0.25s var(--ease), background 0.25s var(--ease), opacity 0.25s var(--ease);
}
.nav__links a:hover { background: rgba(29,29,31,0.06); }
.nav__links a[aria-current="page"] { font-weight: 560; background: rgba(29,29,31,0.07); }

.nav__end { display: flex; align-items: center; gap: 0.15rem; }
.ico {
  display: grid; place-items: center; width: 38px; height: 38px;
  border-radius: 50%; color: var(--fg);
  transition: background 0.25s var(--ease);
}
.ico:hover { background: rgba(29,29,31,0.06); }

.burger span, .burger span::before, .burger span::after {
  display: block; width: 16px; height: 1.5px; border-radius: 2px; background: currentColor;
  transition: transform 0.35s var(--ease), opacity 0.2s linear;
}
.burger span { position: relative; }
.burger span::before { content: ""; position: absolute; top: -5px; }
.burger span::after { content: ""; position: absolute; top: 5px; }
.burger[aria-expanded="true"] span { background: transparent; }
.burger[aria-expanded="true"] span::before { transform: translateY(5px) rotate(45deg); }
.burger[aria-expanded="true"] span::after { transform: translateY(-5px) rotate(-45deg); }

.sheet {
  display: grid; grid-template-rows: 0fr;
  overflow: hidden; border-top: 1px solid transparent;
  background: rgba(245, 245, 247, 0.98);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  transition: grid-template-rows 0.45s var(--ease), border-color 0.45s var(--ease);
}
.sheet[data-open="true"] { grid-template-rows: 1fr; border-top-color: var(--line); }
/* A closed sheet is still 1px tall, and it sits directly under the header.

   Clearing its background was not enough: backdrop-filter kept running, and
   saturate(180%) applied to a 1px strip saturates whatever is behind it. On
   the blue field that painted a vivid 1px line under the logo and icons —
   0,44,144 against a surrounding 7,41,97. The filter has to go too, and the
   border with it, or the same hairline returns in a different colour. */
.sheet:not([data-open="true"]) {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-top-color: transparent;
}
/* No vertical padding here: the 0fr row clamps this box to zero height, but
   padding sits outside that clamp and would leak below the header. */
.sheet__in { min-height: 0; overflow: hidden; }
.sheet ul { margin: 0; padding-top: 0.5rem; }
.sheet a {
  display: flex; align-items: center; min-height: 52px;
  border-bottom: 1px solid var(--line-2);
  font-family: var(--display); font-size: 1.35rem; font-weight: 600; letter-spacing: -0.026em;
  /* Each row eases in behind the sheet as it opens. */
  opacity: 0; transform: translateY(-6px);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}
.sheet[data-open="true"] a { opacity: 1; transform: none; }
.sheet[data-open="true"] li:nth-child(1) a { transition-delay: 0.05s; }
.sheet[data-open="true"] li:nth-child(2) a { transition-delay: 0.09s; }
.sheet[data-open="true"] li:nth-child(3) a { transition-delay: 0.13s; }
.sheet[data-open="true"] li:nth-child(4) a { transition-delay: 0.17s; }
.sheet[data-open="true"] li:nth-child(5) a { transition-delay: 0.21s; }
.sheet[data-open="true"] li:nth-child(6) a { transition-delay: 0.25s; }
.sheet .btn { width: 100%; margin-block: 1.2rem 1.4rem; }

/* Hero --------------------------------------------------------------------------- */
.hero { position: relative; padding-block: clamp(2.5rem, 6vw, 5rem) clamp(3rem, 7vw, 6rem); overflow: clip; }
.hero--center { text-align: center; }
.hero--center .lede { margin-inline: auto; }
.hero--center .acts { justify-content: center; }
.hero__title { margin-block: 1rem 1.1rem; }
.hero__lede { margin-bottom: 1.9rem; }

/* Light product stage. The plates are lit on a white sweep, so the frame is a
   pale gradient and the vial keeps its own soft contact shadow. */
.hero__fig {
  position: relative; display: grid; place-items: center;
  padding: clamp(1.5rem, 3vw, 2.5rem);
  border-radius: var(--r-tile);
  background: linear-gradient(168deg, #fbfdff 0%, #dfeaf8 55%, #cfe0f4 100%);
  overflow: hidden;
}
.hero__vial { position: relative; z-index: 1; width: auto; max-height: min(58vh, 460px); max-width: 100%; }

.spec {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.1rem 1.5rem;
  margin-top: 2.2rem; padding-top: 1.5rem; border-top: 1px solid var(--line);
  text-align: left;
}
.spec dt { font-family: var(--mono); font-size: var(--t-micro); font-weight: 560; letter-spacing: 0.11em; text-transform: uppercase; color: var(--fg-mute); }
.spec dd { margin-top: 0.3rem; font-size: var(--t-sm); font-weight: 560; }

/* Fact strip ---------------------------------------------------------------------- */
.strip { border-block: 1px solid var(--line); background: var(--paper); }
.strip__in { display: grid; }
.strip__i { padding-block: clamp(1.6rem, 3vw, 2.3rem); }
.strip__i + .strip__i { border-top: 1px solid var(--line); }
.strip__v {
  font-family: var(--display); font-weight: 680;
  font-size: clamp(1.7rem, 1.15rem + 2vw, 2.6rem);
  letter-spacing: -0.035em; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.strip__l { margin-top: 0.6rem; font-size: var(--t-sm); color: var(--fg-mute); max-width: 34ch; }

/* Section head ---------------------------------------------------------------------- */
.shead { display: grid; gap: 1rem; margin-bottom: clamp(2.2rem, 4.5vw, 3.4rem); }
.shead .d2 { max-width: 24ch; }
.shead .copy { max-width: 50ch; }
.shead--center { justify-items: center; text-align: center; }
.shead--center .d2 { max-width: 20ch; }

/* Product tiles ----------------------------------------------------------------------- */
/* The reference layout: a rounded gradient tile, colour dots beneath, then the
   name centred under that. The whole thing is the link. */
.grid { display: grid; gap: clamp(1rem, 2vw, 1.6rem); }

.pc { display: flex; flex-direction: column; }

.pc__fig {
  position: relative; aspect-ratio: 4 / 5; overflow: hidden;
  border-radius: var(--r-tile);
  background: linear-gradient(168deg, #fbfdff 0%, #dfeaf8 55%, #cfe0f4 100%);
  transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease);
}
/* Absolutely positioned against the padding box: a percentage max-height on a
   grid item whose row is sized by aspect-ratio resolves against an indefinite
   height in Chrome, and the vial gets clipped. */
.pc__fig img {
  position: absolute; inset: 6%;
  width: 88%; height: 88%;
  object-fit: contain;
  transition: transform 0.65s var(--ease);
}
.pc:hover .pc__fig { box-shadow: var(--shadow-lift); }
.pc:hover .pc__fig img { transform: scale(1.045); }
.pc:active .pc__fig { transform: scale(0.99); }

.pc__code {
  position: absolute; top: 0.85rem; left: 0.95rem; z-index: 2;
  font-family: var(--mono); font-size: var(--t-micro); font-weight: 560;
  letter-spacing: 0.09em; color: var(--fg-mute);
  font-variant-numeric: tabular-nums;
}

/* Colour dots, straight from the reference. */
.pc__dots { display: flex; justify-content: center; gap: 0.45rem; margin-top: 1.1rem; }
.pc__dots i {
  width: 8px; height: 8px; border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(29,29,31,0.14);
  transition: transform 0.3s var(--spring);
}
.pc:hover .pc__dots i { transform: scale(1.22); }
.pc:hover .pc__dots i:nth-child(2) { transition-delay: 0.04s; }
.pc:hover .pc__dots i:nth-child(3) { transition-delay: 0.08s; }

.pc__b { display: flex; flex-direction: column; align-items: center; gap: 0.35rem; padding-top: 0.85rem; text-align: center; }
.pc__n { font-family: var(--display); font-size: var(--t-tile); font-weight: 660; letter-spacing: -0.026em; line-height: 1.14; }
.pc__m { font-size: var(--t-xs); color: var(--fg-mute); }
/* Documentation status. The content package requires it on the card, and it
   is deliberately quiet: it is a provenance note, not a selling point, and it
   must not compete with the compound name or the price. */
.pc__doc {
  margin-top: 0.28rem;
  font-size: var(--t-micro);
  letter-spacing: 0.01em;
  color: var(--fg-mute);
}
.pc__price { margin-top: 0.15rem; font-size: var(--t-sm); font-weight: 560; font-variant-numeric: tabular-nums; }

/* Out of stock ------------------------------------------------------------------
   The badge sits under the class name rather than over the image: a corner
   ribbon on the vial would fight the product photography the whole catalogue is
   built around, and this line reads in the same scan as the price.

   The tile is dimmed rather than hidden. The catalogue number, size and price
   are still worth showing for a compound someone is planning to order later. */
.pc__stock {
  align-self: start;
  margin-top: 0.3rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--paper-2);
  color: var(--off);
  font-size: var(--t-micro);
  font-weight: 560;
  letter-spacing: 0.02em;
}
.pc[data-stock="Out of stock"] .pc__fig { opacity: 0.55; }
.pc[data-stock="Out of stock"] .pc__n,
.pc[data-stock="Out of stock"] .pc__price { color: var(--fg-mute); }
.pc[data-stock="Out of stock"]:hover .pc__fig img { transform: none; }
.pc__note { margin-top: 0.6rem; font-size: var(--t-micro); line-height: 1.55; color: var(--fg-mute); max-width: 30ch; }

.chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  margin-top: 0.5rem; padding: 0.26rem 0.62rem;
  border-radius: 999px; background: var(--paper-2);
  font-family: var(--mono); font-size: var(--t-micro); color: var(--fg-mute); white-space: nowrap;
}
.chip::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--fg-mute); flex: none; }
.chip--review { background: var(--blue-wash); color: var(--blue-deep); }
.chip--review::before { background: var(--blue); }
.chip--pending::before { background: var(--fg-mute); }
.chip--live { background: var(--ok-wash); color: var(--ok); }
.chip--live::before { background: var(--ok); }
.chip--off { background: var(--paper-2); color: var(--off); }
.chip--off::before { background: var(--off); }
.chip--wait { background: var(--warn-wash); color: var(--warn); }
.chip--wait::before { background: var(--warn); }

/* Steps ------------------------------------------------------------------------------- */
.steps { display: grid; gap: clamp(1.6rem, 3vw, 2.2rem); }
.step { padding-top: 1.4rem; border-top: 2px solid var(--ink); }
.band--dark .step { border-top-color: var(--white); }
.step__n { font-family: var(--mono); font-size: var(--t-micro); letter-spacing: 0.1em; color: var(--blue); }
.band--dark .step__n { color: var(--blue-hi); }
.step h3 { margin-block: 0.7rem 0.5rem; }
.step p { color: var(--fg-mute); font-size: var(--t-sm); max-width: 44ch; }
.band--dark .step p { color: var(--on-dark-dim); }

/* Catalog toolbar ----------------------------------------------------------------------- */
.filters {
  position: sticky; top: var(--nav-h); z-index: 20;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 0.6rem 0.9rem; margin-block: 2.2rem 1.8rem; padding: 0.7rem 0.9rem;
  /* A rounded rectangle rather than a full pill. With four filters the bar
     wraps to two rows on a laptop, and at 999px the corners then cut into the
     first and last controls. */
  border: 1px solid var(--line); border-radius: 26px;
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  box-shadow: var(--shadow-sm);
}
.filters__g { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 0.9rem; flex: 1 1 auto; }
/* Each label travels with its own control, so wrapping cannot strand a label
   at the end of one row with its select on the next. */
.filters__f { display: inline-flex; align-items: center; gap: 0.5rem; }
.filters label { font-family: var(--mono); font-size: var(--t-micro); font-weight: 560; letter-spacing: 0.11em; text-transform: uppercase; color: var(--fg-mute); }
.filters input, .filters select {
  min-height: 40px; padding: 0 0.9rem;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--white); font-size: var(--t-sm);
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.filters input { min-width: min(230px, 58vw); }
.filters select { padding-right: 2rem; appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%236e6e73' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 0.9rem center; }
.filters input:focus, .filters select:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px rgba(11,110,229,0.16); }
.filters__n { font-family: var(--mono); font-size: var(--t-micro); letter-spacing: 0.07em; text-transform: uppercase; color: var(--fg-mute); padding-right: 0.6rem; }

/* Narrow screens: the toolbar stacks.

   As a wrapping flex row it worked with two controls and fell apart at four —
   each label wrapped onto the line of the *previous* control instead of its
   own, and the 999px pill radius made a stacked block look like a lozenge
   with the corners eating the first and last rows. A two-column grid pairs
   each label with its own control and cannot come apart however many filters
   are added later. */
@media (max-width: 760px) {
  .filters {
    display: grid;
    gap: 0.55rem 0.8rem;
    padding: 0.9rem;
    border-radius: 20px;
  }
  .filters__g { display: grid; gap: 0.55rem; width: 100%; }
  .filters__g > input[type="search"] { width: 100%; min-width: 0; }
  /* Label left, control right, on one row each. */
  .filters__f { display: grid; grid-template-columns: auto 1fr; gap: 0.8rem; align-items: center; }
  .filters__f select { width: 100%; min-width: 0; }
  .filters__n { padding-right: 0; text-align: right; }
}

/* Ledger ---------------------------------------------------------------------------------- */
.ledger { border-top: 1px solid var(--line); }
.band--dark .ledger { border-top-color: var(--line-dark); }
.ledger > div { display: grid; gap: 0.3rem 2.5rem; padding-block: 1.3rem; border-bottom: 1px solid var(--line); }
.band--dark .ledger > div { border-bottom-color: var(--line-dark); }
.ledger dt { font-family: var(--mono); font-size: var(--t-micro); letter-spacing: 0.09em; text-transform: uppercase; color: var(--blue); }
.band--dark .ledger dt { color: var(--blue-hi); }
.ledger dd { color: var(--fg-mute); max-width: 62ch; }
.band--dark .ledger dd { color: var(--on-dark-dim); }

/* Checks ------------------------------------------------------------------------------------ */
.checks { display: grid; gap: 0.75rem; }
.checks li { display: flex; gap: 0.65rem; color: var(--fg-mute); font-size: var(--t-sm); line-height: 1.5; }
.checks svg { flex: none; margin-top: 0.32em; color: var(--blue); }
.band--dark .checks li { color: var(--on-dark-dim); }
.band--dark .checks svg { color: var(--blue-hi); }

/* Tables -------------------------------------------------------------------------------------- */
.tablewrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--white); color: var(--fg); }
table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); min-width: 560px; }
thead th {
  padding: 0.85rem 1.1rem; text-align: left; white-space: nowrap;
  border-bottom: 1px solid var(--line); background: var(--paper);
  font-family: var(--mono); font-size: var(--t-micro); font-weight: 500;
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--fg-mute);
}
tbody td { padding: 0.9rem 1.1rem; border-bottom: 1px solid var(--line-2); color: var(--fg-mute); vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody td:first-child { color: var(--fg); font-weight: 550; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* Accordion ------------------------------------------------------------------------------------- */
.acc { border-top: 1px solid var(--line); }
.acc details { border-bottom: 1px solid var(--line); }
.acc summary {
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  padding-block: 1.2rem; cursor: pointer; list-style: none;
  font-family: var(--display); font-size: var(--t-h3); font-weight: 600; letter-spacing: -0.02em;
  transition: color 0.25s var(--ease);
}
.acc summary:hover { color: var(--blue); }
.acc summary::-webkit-details-marker { display: none; }
.acc summary::after {
  content: ""; flex: none; width: 9px; height: 9px;
  border-right: 1.6px solid currentColor; border-bottom: 1.6px solid currentColor;
  transform: rotate(45deg) translateY(-3px);
  transition: transform 0.35s var(--ease);
}
.acc details[open] summary::after { transform: rotate(-135deg) translateY(-2px); }
.acc p { max-width: 66ch; padding-bottom: 1.25rem; color: var(--fg-mute); font-size: var(--t-sm); }

/* Forms -------------------------------------------------------------------------------------------- */
.form { display: grid; gap: 1.05rem; max-width: 780px; }
.row { display: grid; gap: 1.05rem; }
.field { display: grid; gap: 0.4rem; }
.field label { font-family: var(--mono); font-size: var(--t-micro); font-weight: 560; letter-spacing: 0.11em; text-transform: uppercase; color: var(--fg-mute); }
.field input, .field select, .field textarea {
  width: 100%; min-height: 48px; padding: 0.75rem 1rem;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--white); font-size: var(--t-sm);
  transition: border-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.field textarea { min-height: 148px; resize: vertical; line-height: 1.6; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px rgba(11,110,229,0.16);
}
.hint { font-size: var(--t-micro); color: var(--fg-mute); }
/* The whole label is the click target, so it carries the 44px floor. */
.check {
  display: flex; gap: 0.7rem; align-items: flex-start; min-height: 44px; padding-block: 0.4rem;
  font-size: var(--t-sm); color: var(--fg-mute); cursor: pointer;
}
.check input { flex: none; width: 18px; height: 18px; margin-top: 0.2em; accent-color: var(--blue); }
.formnote {
  margin-top: 0.3rem; padding: 0.9rem 1.1rem;
  border-radius: var(--r-md); background: var(--blue-wash);
  font-size: var(--t-sm); color: var(--blue-deep);
}
.formnote--ok { background: var(--ok-wash); color: var(--ok); }
.formnote--bad { background: #fdecea; color: #a52c18; }

/* Notice ---------------------------------------------------------------------------------------------- */
.notice {
  display: flex; gap: 0.75rem; padding: 1.05rem 1.25rem;
  border-radius: var(--r-lg); background: var(--paper);
  font-size: var(--t-sm); line-height: 1.58; color: var(--fg-mute);
}
.notice svg { flex: none; margin-top: 0.16em; color: var(--blue); }
.band--paper .notice { background: var(--white); }
.band--dark .notice { background: rgba(255,255,255,0.05); color: var(--on-dark-dim); }
.band--dark .notice svg { color: var(--blue-hi); }

/* Page head ------------------------------------------------------------------------------------------- */
.phead { padding-block: clamp(2.4rem, 5vw, 4.2rem) clamp(2.6rem, 5.5vw, 4.6rem); }

/* On the field, the title band has no ground of its own and its type inverts. */
.has-blue .phead { background: transparent; border-bottom: 0; color: #fff; }
.has-blue .phead .d1 { color: #fff; text-shadow: 0 2px 40px rgba(3, 18, 48, 0.32); }
.has-blue .phead .lede { color: rgba(255, 255, 255, 0.84); }
.has-blue .phead .kicker { color: rgba(180, 220, 255, 0.95); }
.has-blue .phead .stamp { color: rgba(255, 255, 255, 0.62); }
.has-blue .phead .crumb { color: rgba(255, 255, 255, 0.66); }
.has-blue .phead .crumb a { color: rgba(255, 255, 255, 0.78); }
.has-blue .phead .crumb a:hover { color: #fff; }
.crumb { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.4rem; font-family: var(--mono); font-size: var(--t-micro); letter-spacing: 0.06em; color: var(--fg-mute); }
.crumb a {
  display: inline-block;
  padding-block: 0.25rem;
  color: var(--fg-mute);
  transition: color 0.25s var(--ease);
}
.crumb a:hover { color: var(--blue); }
.phead .lede { margin-top: 1.2rem; }
.stamp { margin-top: 1.2rem; font-family: var(--mono); font-size: var(--t-micro); letter-spacing: 0.05em; color: var(--fg-mute); }

/* Long-form documents ---------------------------------------------------------------------------------- */
.doc { display: grid; gap: clamp(2.2rem, 5vw, 3.2rem); }
.doc section { scroll-margin-top: calc(var(--nav-h) + 24px); }
.doc h2 { max-width: 24ch; }

/* Closer -------------------------------------------------------------------------------------------------- */
.closer { text-align: center; }
.closer .kicker { justify-content: center; }
.closer .d2 { max-width: 18ch; margin-inline: auto; }
.closer .lede { margin-inline: auto; margin-top: 1.1rem; }
.closer .acts { justify-content: center; margin-top: 2rem; }

/* Footer ---------------------------------------------------------------------------------------------------- */
.foot { padding-block: clamp(3.2rem, 6vw, 5rem) 2.4rem; background: var(--ink); color: var(--on-dark-dim); }
.foot__grid { display: grid; gap: 2.4rem; padding-bottom: 2.6rem; border-bottom: 1px solid var(--line-dark); }
.foot h3 { font-family: var(--mono); margin-bottom: 1rem; font-size: var(--t-micro); font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--white); }
.foot ul { display: grid; gap: 0.65rem; }
/* WCAG 2.5.8 asks for a 24px target, and it measures the target box, not the
   glyphs. These links sat at 19px tall, so the box is grown with padding
   rather than by enlarging the type — the list keeps its rhythm and the hit
   area clears the minimum on a phone. */
.foot li a {
  display: inline-block;
  padding-block: 0.2rem;
  font-size: var(--t-sm);
  color: var(--on-dark-dim);
  transition: color 0.25s var(--ease);
}
.foot li a:hover { color: var(--white); }
.foot__blurb { margin-top: 1rem; max-width: 34ch; font-size: var(--t-sm); line-height: 1.58; }
.foot__base { display: grid; gap: 1.1rem; padding-top: 1.9rem; }
.foot__base p { font-size: var(--t-micro); line-height: 1.7; color: var(--off); max-width: 92ch; }

/* =====================  AGE / RESEARCH-USE GATE  ============================ */
/* A legal gate, so it is modal in the strict sense: focus-trapped, not
   dismissible by Escape or backdrop click, and no route through without both
   confirmations. */
.gate {
  position: fixed; inset: 0; z-index: 400;
  display: grid; place-items: center;
  padding: clamp(1rem, 4vw, 2rem);
  background: rgba(20, 20, 22, 0.55);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  opacity: 0;
  transition: opacity 0.5s var(--ease);
}
/* Visible by default and opaque immediately: the gate must be the first thing
   painted, not something a deferred script reveals. The inline head guard adds
   .gate-done for a returning visitor, which removes it before first paint. */
.gate { opacity: 1; }
.gate[hidden], .gate-done .gate { display: none; }
/* The card still animates in. */
.gate[data-open="true"] { opacity: 1; }
.gate[data-closing="true"] { opacity: 0; }

.gate__card {
  width: min(560px, 100%);
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  padding: clamp(1.6rem, 4vw, 2.6rem);
  border-radius: var(--r-xl);
  background: var(--white);
  box-shadow: var(--shadow-modal);
  transform: translateY(22px) scale(0.965);
  opacity: 0;
  transition: transform 0.62s var(--spring), opacity 0.42s var(--ease);
}
.gate[data-open="true"] .gate__card { transform: none; opacity: 1; }
.gate[data-closing="true"] .gate__card { transform: translateY(-8px) scale(0.985); opacity: 0; transition-duration: 0.3s, 0.3s; }

.gate__mark { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 1.5rem; }
.gate__mark img { width: 34px; height: 34px; border-radius: 50%; }
.gate__mark b { font-family: var(--display); font-size: 1.05rem; font-weight: 660; letter-spacing: -0.03em; }
.gate__mark b i { font-style: normal; font-weight: 430; color: var(--fg-mute); }

.gate h2 { font-size: clamp(1.6rem, 1.2rem + 1.7vw, 2.1rem); letter-spacing: -0.028em; }
.gate__lede { margin-top: 0.8rem; font-size: var(--t-sm); line-height: 1.6; color: var(--fg-mute); }

.gate__checks { display: grid; gap: 0.4rem; margin-top: 1.6rem; }
/* Each confirmation is a full tappable card, not a bare checkbox. */
.gate__check {
  display: flex; gap: 0.85rem; align-items: flex-start;
  padding: 0.95rem 1.05rem;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--white);
  font-size: var(--t-sm); line-height: 1.5; color: var(--fg-dim);
  cursor: pointer;
  transition: border-color 0.25s var(--ease), background 0.25s var(--ease), transform 0.2s var(--ease);
}
.gate__check:hover { background: var(--paper); }
.gate__check:active { transform: scale(0.993); }
.gate__check:has(input:checked) { border-color: var(--blue); background: var(--blue-wash); color: var(--fg); }
.gate__check input { flex: none; width: 20px; height: 20px; margin-top: 0.08em; accent-color: var(--blue); }

.gate__acts { display: grid; gap: 0.6rem; margin-top: 1.6rem; }
.gate__acts .btn { width: 100%; }
.gate__deny {
  min-height: 44px; font-size: var(--t-sm); color: var(--fg-mute);
  transition: color 0.25s var(--ease);
}
.gate__deny:hover { color: var(--fg); text-decoration: underline; text-underline-offset: 3px; }
.gate__fine { margin-top: 1.2rem; font-size: var(--t-micro); line-height: 1.6; color: var(--fg-mute); }

/* Shake when someone tries to enter without both boxes ticked. */
@keyframes gate-nudge {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-7px); }
  30% { transform: translateX(6px); }
  45% { transform: translateX(-4px); }
  60% { transform: translateX(3px); }
  80% { transform: translateX(-1px); }
}
.gate__checks[data-nudge="true"] { animation: gate-nudge 0.5s var(--ease-io); }

/* =====================  AFFILIATE PORTAL  =================================== */
.dash { display: grid; gap: clamp(1.4rem, 3vw, 2rem); }

.kpis { display: grid; gap: clamp(0.8rem, 1.6vw, 1.1rem); }
.kpi {
  padding: 1.25rem 1.35rem;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--white);
}
.kpi dt { font-family: var(--mono); font-size: var(--t-micro); font-weight: 560; letter-spacing: 0.11em; text-transform: uppercase; color: var(--fg-mute); }
.kpi dd {
  margin-top: 0.5rem;
  font-family: var(--display); font-size: clamp(1.6rem, 1.2rem + 1.4vw, 2.2rem);
  font-weight: 680; letter-spacing: -0.032em; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.kpi small { display: block; margin-top: 0.45rem; font-size: var(--t-micro); color: var(--fg-mute); }

.panel {
  padding: clamp(1.2rem, 2.5vw, 1.75rem);
  border: 1px solid var(--line); border-radius: var(--r-xl);
  background: var(--white);
}
.panel__h { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.7rem; margin-bottom: 1.2rem; }
.panel__h h3 { font-size: var(--t-h3); font-weight: 640; letter-spacing: -0.022em; }
.panel__h p { font-size: var(--t-xs); color: var(--fg-mute); }

/* The affiliate's code, presented as the artefact it is. */
.codebox {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem;
  padding: 1rem 1.15rem; border-radius: var(--r-lg);
  background: var(--paper); border: 1px dashed var(--line);
}
.codebox code {
  font-family: var(--mono); font-size: clamp(1.1rem, 0.9rem + 0.7vw, 1.45rem);
  font-weight: 500; letter-spacing: 0.06em; color: var(--fg);
}
.codebox .btn { margin-left: auto; }

/* Tactile commission slider ------------------------------------------------- */
.slider { display: grid; gap: 0.55rem; }
.slider__top { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.slider__lbl { font-family: var(--mono); font-size: var(--t-micro); font-weight: 560; letter-spacing: 0.11em; text-transform: uppercase; color: var(--fg-mute); }
.slider__val {
  font-family: var(--display); font-size: 1.5rem; font-weight: 680;
  letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
  transition: transform 0.18s var(--spring), color 0.18s var(--ease);
}
.slider[data-dragging="true"] .slider__val { transform: scale(1.14); color: var(--blue); }

.slider input[type="range"] {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 34px; background: transparent; cursor: grab;
}
.slider input[type="range"]:active { cursor: grabbing; }
/* Track is painted with a gradient stop at --pct so the fill follows the thumb. */
.slider input[type="range"]::-webkit-slider-runnable-track {
  height: 8px; border-radius: 999px;
  background: linear-gradient(to right, var(--blue) 0 var(--pct, 50%), var(--paper-2) var(--pct, 50%) 100%);
}
.slider input[type="range"]::-moz-range-track {
  height: 8px; border-radius: 999px;
  background: linear-gradient(to right, var(--blue) 0 var(--pct, 50%), var(--paper-2) var(--pct, 50%) 100%);
}
.slider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 26px; height: 26px; margin-top: -9px;
  border-radius: 50%; background: var(--white);
  border: 1px solid rgba(29,29,31,0.12);
  box-shadow: 0 1px 3px rgba(0,0,0,0.18), 0 4px 12px rgba(0,0,0,0.10);
  transition: transform 0.18s var(--spring), box-shadow 0.18s var(--ease);
}
.slider input[type="range"]::-moz-range-thumb {
  width: 26px; height: 26px; border-radius: 50%; background: var(--white);
  border: 1px solid rgba(29,29,31,0.12);
  box-shadow: 0 1px 3px rgba(0,0,0,0.18), 0 4px 12px rgba(0,0,0,0.10);
}
.slider input[type="range"]:hover::-webkit-slider-thumb { transform: scale(1.06); }
.slider input[type="range"]:active::-webkit-slider-thumb { transform: scale(1.16); box-shadow: 0 2px 6px rgba(0,0,0,0.22), 0 8px 20px rgba(0,0,0,0.14); }
.slider input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px rgba(11,110,229,0.28); }

.slider__scale { display: flex; justify-content: space-between; font-family: var(--mono); font-size: var(--t-micro); color: var(--fg-mute); }
.slider__sum {
  margin-top: 0.2rem; padding: 0.7rem 0.9rem; border-radius: var(--r-md);
  background: var(--paper); font-size: var(--t-xs); color: var(--fg-mute);
}
.slider__sum b { color: var(--fg); font-weight: 600; font-variant-numeric: tabular-nums; }

/* Live / deactivated switch --------------------------------------------------- */
.switch { display: inline-flex; align-items: center; gap: 0.7rem; cursor: pointer; min-height: 44px; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch__track {
  position: relative; width: 52px; height: 31px; flex: none;
  border-radius: 999px; background: var(--paper-2);
  transition: background 0.3s var(--ease);
}
.switch__track::after {
  content: ""; position: absolute; top: 3px; left: 3px;
  width: 25px; height: 25px; border-radius: 50%; background: var(--white);
  box-shadow: 0 1px 3px rgba(0,0,0,0.2);
  transition: transform 0.32s var(--spring), width 0.2s var(--ease);
}
.switch input:checked + .switch__track { background: var(--ok); }
.switch input:checked + .switch__track::after { transform: translateX(21px); }
.switch:active .switch__track::after { width: 30px; }
.switch input:checked:active + .switch__track::after { transform: translateX(16px); }
.switch input:focus-visible + .switch__track { box-shadow: 0 0 0 4px rgba(11,110,229,0.28); }
.switch__lbl { font-size: var(--t-sm); font-weight: 550; }

/* Affiliate rows ---------------------------------------------------------------- */
.aff { display: grid; gap: 0.9rem; }
.aff__row {
  display: grid; gap: 1rem;
  padding: 1.15rem 1.25rem;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--white);
  transition: border-color 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.aff__row:hover { box-shadow: var(--shadow-sm); }
.aff__row[data-live="false"] { background: var(--paper); }
.aff__id { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }
.aff__id strong { font-size: var(--t-base); font-weight: 620; letter-spacing: -0.018em; }
.aff__id span { font-size: var(--t-xs); color: var(--fg-mute); }
.aff__code { font-family: var(--mono); font-size: var(--t-sm); letter-spacing: 0.05em; }
.aff__meta { display: flex; flex-wrap: wrap; gap: 1.4rem; font-size: var(--t-xs); color: var(--fg-mute); }
.aff__meta b { display: block; margin-top: 0.15rem; font-family: var(--display); font-size: 1.05rem; font-weight: 660; color: var(--fg); letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.aff__ctl { display: grid; gap: 1.1rem; padding-top: 1.1rem; border-top: 1px solid var(--line-2); }

/* Tabs ---------------------------------------------------------------------------- */
.tabs { display: inline-flex; gap: 0.2rem; padding: 0.25rem; border-radius: 999px; background: var(--paper-2); }
.tabs button {
  padding: 0.5rem 1rem; border-radius: 999px;
  font-size: var(--t-sm); font-weight: 550; color: var(--fg-mute);
  transition: background 0.3s var(--ease), color 0.3s var(--ease);
}
.tabs button[aria-selected="true"] { background: var(--white); color: var(--fg); box-shadow: var(--shadow-sm); }
.tabpanel[hidden] { display: none; }

/* Empty state ---------------------------------------------------------------------- */
.empty { padding: 2.4rem 1.2rem; text-align: center; color: var(--fg-mute); font-size: var(--t-sm); }

/* Reveals ------------------------------------------------------------------------------ */
/* Gated on .js so nothing is ever stranded invisible with scripting off. */
.js .rv { opacity: 0; transform: translate3d(0, 22px, 0); transition: opacity 0.8s var(--ease), transform 0.8s var(--ease); }
.js .rv.on { opacity: 1; transform: none; }
.js .rv[data-d="1"].on { transition-delay: 60ms; }
.js .rv[data-d="2"].on { transition-delay: 120ms; }
.js .rv[data-d="3"].on { transition-delay: 180ms; }
.js .rv[data-d="4"].on { transition-delay: 240ms; }
/* Tiles in a grid stagger along the row; --i is set from the child index. */
.js .rv[style*="--i"].on { transition-delay: calc(var(--i) * 55ms); }

@media (prefers-reduced-motion: reduce) {
  .js .rv { opacity: 1 !important; transform: none !important; transition: none !important; }
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

/* A coarse pointer has no hover, so lift effects would stick after a tap. */
@media (hover: none) {
  .pc:hover .pc__fig { box-shadow: none; }
  .pc:hover .pc__fig img { transform: none; }
  .pc:hover .pc__dots i { transform: none; }
  .pc:active .pc__fig { transform: scale(0.985); }
  .pc:active .pc__fig img { transform: scale(1.02); }
}

/* Breakpoints ------------------------------------------------------------------------------ */
@media (min-width: 560px) {
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 620px) {
  .row--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .foot__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .foot__base { grid-template-columns: 1fr auto; align-items: end; }
  .strip__in { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .strip__i + .strip__i { border-top: 0; border-left: 1px solid var(--line); padding-left: clamp(1.2rem, 3vw, 2.2rem); }
  .strip__i { padding-right: 1.2rem; }
  .aff__row { grid-template-columns: 1fr auto; align-items: center; }
  /* start, not end: the slider column is taller, and bottom-aligning the
     code field left a dead gap above it. */
  .aff__ctl { grid-column: 1 / -1; grid-template-columns: 1fr 1fr; gap: 1.6rem; align-items: start; }
}

@media (min-width: 900px) {
  .nav__links { display: flex; }
  .burger { display: none; }
  .sheet { display: none; }
  .steps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .shead { grid-template-columns: 1.15fr 1fr; align-items: end; gap: 2.5rem; }
  .shead--center { grid-template-columns: 1fr; }
  .duo { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.6rem, 4vw, 3rem); align-items: start; }
  .ledger > div { grid-template-columns: 15rem 1fr; align-items: baseline; }
  .foot__grid { grid-template-columns: 1.4fr repeat(3, 1fr); gap: 3rem 2rem; }
  .hero__grid { display: grid; grid-template-columns: 1.06fr 0.94fr; gap: clamp(2.5rem, 5vw, 4rem); align-items: center; }
  .spec { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .gate__acts { grid-template-columns: auto 1fr; align-items: center; }
  .gate__acts .gate__deny { justify-self: start; }
}

@media (min-width: 1120px) { .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1380px) { .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }


/* =====================  QUICK VIEW  =========================================
   A pop-up that cannot disturb the page: it is a fixed layer with its own
   scroll, and the body is locked while it is open, so nothing behind it
   reflows or shifts on either desktop or mobile.
   ========================================================================== */
.qv {
  position: fixed; inset: 0; z-index: 380;
  display: grid; place-items: center;
  padding: clamp(0.75rem, 3vw, 2rem);
  background: rgba(20, 20, 22, 0.42);
  backdrop-filter: blur(12px) saturate(140%);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  opacity: 0; visibility: hidden;
  transition: opacity 0.32s var(--ease), visibility 0.32s var(--ease);
}
.qv[data-open="true"] { opacity: 1; visibility: visible; }

.qv__card {
  position: relative;
  width: min(940px, 100%);
  max-height: calc(100dvh - 1.5rem);
  overflow-y: auto; overscroll-behavior: contain;
  border-radius: var(--r-xl);
  background: var(--white);
  box-shadow: var(--shadow-modal);
  transform: translateY(18px) scale(0.972); opacity: 0;
  transition: transform 0.5s var(--spring), opacity 0.3s var(--ease);
}
.qv[data-open="true"] .qv__card { transform: none; opacity: 1; }

.qv__x {
  position: absolute; top: 0.9rem; right: 0.9rem; z-index: 3;
  display: grid; place-items: center; width: 38px; height: 38px;
  border-radius: 50%; background: rgba(29,29,31,0.06); color: var(--fg);
  transition: background 0.25s var(--ease), transform 0.25s var(--ease);
}
.qv__x:hover { background: rgba(29,29,31,0.12); }
.qv__x:active { transform: scale(0.92); }

.qv__grid { display: grid; }
.qv__fig {
  position: relative; aspect-ratio: 1 / 1;
  background: linear-gradient(168deg, #fbfdff 0%, #dfeaf8 55%, #cfe0f4 100%);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  overflow: hidden;
}
.qv__fig img { position: absolute; inset: 5%; width: 90%; height: 90%; object-fit: contain; }
.qv__code {
  position: absolute; top: 1rem; left: 1.1rem;
  font-size: var(--t-micro); font-weight: 560; letter-spacing: 0.09em;
  color: var(--fg-mute); font-variant-numeric: tabular-nums;
}

.qv__body { padding: clamp(1.3rem, 3vw, 2rem); display: flex; flex-direction: column; }
.qv__cat { font-size: var(--t-micro); font-weight: 560; letter-spacing: 0.11em; text-transform: uppercase; color: var(--fg-mute); }
.qv__n { margin-top: 0.5rem; font-family: var(--display); font-size: clamp(1.6rem, 1.2rem + 1.6vw, 2.3rem); font-weight: 680; letter-spacing: -0.03em; line-height: 1.06; }
.qv__price { margin-top: 0.7rem; font-family: var(--display); font-size: clamp(1.5rem, 1.2rem + 1.1vw, 1.95rem); font-weight: 680; letter-spacing: -0.028em; font-variant-numeric: tabular-nums; }
.qv__spec { display: grid; gap: 0.55rem; margin-top: 1.1rem; padding-top: 1rem; border-top: 1px solid var(--line); }
.qv__spec > div { display: flex; justify-content: space-between; gap: 1.5rem; font-size: var(--t-sm); }
.qv__spec dt { color: var(--fg-mute); }
.qv__spec dd { font-weight: 560; text-align: right; }
.qv__buy { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.3rem; }
.qv__buy .btn { flex: 1 1 12rem; }
.qv__note { margin-top: 1rem; font-size: var(--t-micro); line-height: 1.6; color: var(--fg-mute); }

/* Quantity stepper ------------------------------------------------------------ */
.qty { display: inline-flex; align-items: center; border: 1px solid var(--line); border-radius: 999px; overflow: hidden; flex: none; }
.qty button {
  display: grid; place-items: center; width: 44px; height: 46px;
  font-size: 1.15rem; color: var(--fg);
  transition: background 0.2s var(--ease);
}
.qty button:hover { background: var(--paper); }
.qty button:disabled { opacity: 0.3; cursor: not-allowed; }
.qty input {
  width: 46px; height: 46px; border: 0; text-align: center; background: transparent;
  font-size: var(--t-sm); font-weight: 560; font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}
.qty input::-webkit-outer-spin-button, .qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* =====================  CART  =============================================== */
.ico { position: relative; }
.cart-count {
  position: absolute; top: 1px; right: 1px;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 999px; background: var(--blue); color: #fff;
  font-size: 0.625rem; font-weight: 620; line-height: 17px; text-align: center;
  font-variant-numeric: tabular-nums;
  transform: scale(0); transition: transform 0.32s var(--spring);
}
.cart-count[data-n]:not([data-n="0"]) { transform: scale(1); }
@keyframes cart-bump { 0%, 100% { transform: scale(1); } 40% { transform: scale(1.3); } }
.cart-count.bump { animation: cart-bump 0.45s var(--spring); }

.drawer { position: fixed; inset: 0; z-index: 390; visibility: hidden; transition: visibility 0.4s var(--ease); }
.drawer[data-open="true"] { visibility: visible; }
.drawer__scrim { position: absolute; inset: 0; background: rgba(20,20,22,0.4); opacity: 0; transition: opacity 0.4s var(--ease); }
.drawer[data-open="true"] .drawer__scrim { opacity: 1; }

.drawer__panel {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(420px, 100%);
  display: flex; flex-direction: column;
  background: var(--white);
  box-shadow: -20px 0 60px -20px rgba(0,0,0,0.28);
  transform: translateX(100%);
  transition: transform 0.45s var(--ease);
}
.drawer[data-open="true"] .drawer__panel { transform: none; }

.drawer__h { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1.1rem 1.25rem; border-bottom: 1px solid var(--line); }
.drawer__h h2 { font-size: var(--t-h3); font-weight: 640; letter-spacing: -0.022em; }
.drawer__list { flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: 1rem 1.25rem; display: grid; gap: 1rem; align-content: start; }

.ci { display: grid; grid-template-columns: 62px 1fr auto; gap: 0.85rem; align-items: center; }
.ci__fig { position: relative; aspect-ratio: 1/1; border-radius: var(--r-md); background: linear-gradient(168deg,#fbfdff,#d9e6f6); overflow: hidden; }
.ci__fig img { position: absolute; inset: 6%; width: 88%; height: 88%; object-fit: contain; }
/* Spans inside a span, so they need to be told to stack. */
.ci > span:nth-child(2) { display: flex; flex-direction: column; align-items: flex-start; }
.ci__n { display: block; font-size: var(--t-sm); font-weight: 580; letter-spacing: -0.014em; line-height: 1.25; }
.ci__m { display: block; margin-top: 0.15rem; font-size: var(--t-micro); color: var(--fg-mute); }
.ci__price { font-size: var(--t-sm); font-weight: 580; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ci__x { margin-top: 0.3rem; font-size: var(--t-micro); color: var(--fg-mute); text-decoration: underline; text-underline-offset: 2px; }
.ci__x:hover { color: #a52c18; }
.ci .qty { margin-top: 0.4rem; transform: scale(0.8); transform-origin: left center; }

.drawer__f { padding: 1.15rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom)); border-top: 1px solid var(--line); display: grid; gap: 0.85rem; }
.drawer__sum { display: flex; align-items: baseline; justify-content: space-between; }
.drawer__sum span { font-size: var(--t-sm); color: var(--fg-mute); }
.drawer__sum b { font-family: var(--display); font-size: 1.5rem; font-weight: 680; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }

/* =====================  TOAST  ==============================================
   Fixed and pointer-transparent, so it can never push layout on any viewport.
   ========================================================================== */
.toasts {
  position: fixed; z-index: 395; pointer-events: none;
  left: 50%; bottom: calc(1rem + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  display: grid; gap: 0.5rem; width: min(420px, calc(100vw - 2rem));
}
.toast {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0.8rem 1rem; border-radius: 999px;
  background: var(--ink); color: var(--white);
  font-size: var(--t-sm); box-shadow: var(--shadow-lift);
  opacity: 0; transform: translateY(14px) scale(0.96);
  transition: opacity 0.32s var(--ease), transform 0.4s var(--spring);
}
.toast[data-on="true"] { opacity: 1; transform: none; }
.toast svg { flex: none; color: var(--blue-hi); }

@media (min-width: 720px) {
  .qv__grid { grid-template-columns: 1fr 1fr; }
  .qv__fig { aspect-ratio: auto; min-height: 440px; border-radius: var(--r-xl) 0 0 var(--r-xl); }
  .toasts { left: auto; right: 1.25rem; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .qv, .qv__card, .drawer, .drawer__panel, .drawer__scrim, .toast, .cart-count {
    transition: none !important; animation: none !important;
  }
}


/* =====================  PEPTIDE BACKDROP  ===================================
   A hairline amino-acid chain behind the hero text. It twists in once on load
   and then rests — no loop, nothing that keeps drawing attention away from the
   headline.

   It is absolutely positioned inside the hero and pointer-transparent, so it
   cannot affect layout, scrolling or hit-testing. The hero clips it, so the
   chain running past the viewport edge never creates a scrollbar.
   ========================================================================== */
.hero { isolation: isolate; }        /* keeps the backdrop under the text only */

.hero__pep {
  position: absolute; inset: 0; z-index: 0;
  display: grid; place-items: center;
  pointer-events: none;
  /* Fades out at the edges so the chain reads as passing through the frame
     rather than stopping dead at it. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 14%, #000 86%, transparent);
}
.hero .wrap { position: relative; z-index: 1; }

.hero__pep {
  /* Deep enough to read as a rotation rather than a squash. Perspective lives
     here, on the HTML parent — Chrome flattens 3D transforms applied to an SVG
     <g>, so the rotation is done on the <svg> element itself. */
  perspective: 1400px;
}
.hero__pep svg {
  /* max-width: none, because the global `img, svg { max-width: 100% }` reset
     was clamping this to the hero's width. The chain is meant to run past the
     frame and be clipped — that is what the edge mask is for — so the reset
     had been quietly holding it at 100% all along. */
  width: min(1500px, 132%);
  max-width: none;
  height: auto;
  transform-origin: 50% 50%;
  will-change: transform;
}

.pep__g {
  stroke: var(--ink);
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  /* Very low contrast on white: a backdrop, not a graphic. The blue field
     raises it — see --pep-op under .is-front. */
  --pep-op: 0.085;
  transform-origin: 50% 50%;
  /* No opacity on the group itself: SVG group opacity caps its children, so a
     dimmed group would make it impossible for the signal to out-shine the
     backbone. Each structural element carries the dimming instead. */
}
.pep__o, .pep__r, .pep__a { fill: var(--white); }
.pep__a { stroke-width: 2.4; }
.pep__o, .pep__r { stroke-width: 2.2; }

/* The entrance twist: rotate in from an oblique angle and settle flat. */
@keyframes pep-twist {
  from { transform: rotateY(-74deg) rotateX(14deg) rotateZ(-3deg) scale(0.9); }
  to   { transform: none; }
}
/* ...and then it keeps turning, forever. Every keyframe at 0/50/100% is the
   resting orientation, so this can pick up from the entrance without a snap
   and the chain is never parked flat for long. Kept inside +/-13deg: past
   that the chain approaches edge-on and thins out to nothing. */
@keyframes pep-sway {
  0%   { transform: none; }
  25%  { transform: rotateY(13deg) rotateX(-5deg) rotateZ(0.7deg); }
  50%  { transform: none; }
  75%  { transform: rotateY(-13deg) rotateX(5deg) rotateZ(-0.7deg); }
  100% { transform: none; }
}
/* The wave. Scaling the group vertically makes the zig-zag breathe, which
   reads as the chain undulating along its length once the sway is turning it
   in depth. It is done on the group rather than per atom deliberately: an
   affine transform moves bonds and nodes together, so nothing can come
   unstuck from anything else, and the signal keeps riding the backbone. */
@keyframes pep-undulate {
  0%   { transform: scaleY(1); }
  50%  { transform: scaleY(1.075); }
  100% { transform: scaleY(1); }
}
@keyframes pep-draw {
  from { stroke-dashoffset: 5200; }
  to   { stroke-dashoffset: 0; }
}
@keyframes pep-fade {
  from { opacity: 0; }
  to   { opacity: var(--pep-op); }
}
/* Structural parts, dimmed individually. */
.pep__bb, .pep__g line, .pep__a, .pep__o, .pep__r { opacity: var(--pep-op); }

.js .hero__pep svg {
  /* The sway starts exactly where the entrance ends (0.12s + 1.5s) and is
     listed second, so it takes the transform over cleanly from there. */
  animation:
    pep-twist 1.5s cubic-bezier(0.22, 0.9, 0.24, 1) 0.12s both,
    pep-sway 26s cubic-bezier(0.42, 0, 0.58, 1) 1.62s infinite;
}
.js .pep__g {
  animation: pep-undulate 9.5s cubic-bezier(0.42, 0, 0.58, 1) 1.62s infinite;
}

/* The backbone draws itself on as the chain rotates into place. */
.js .pep__bb {
  stroke-dasharray: 5200;
  animation:
    pep-draw 1.7s cubic-bezier(0.3, 0.8, 0.25, 1) 0.1s both,
    pep-in 1.1s var(--ease) 0.12s both;
}
/* Nodes and branches arrive just behind the line that connects them.
   These fade to full opacity, not to 0.085 — the group already carries that,
   and SVG group opacity multiplies with child opacity, so reusing pep-fade
   here rendered them at 0.085 x 0.085 and they vanished. */
@keyframes pep-in { from { opacity: 0; } to { opacity: var(--pep-op); } }
.js .pep__a, .js .pep__o, .js .pep__r,
.js .pep__g line {
  animation: pep-in 0.9s var(--ease) 0.55s both;
}

@media (max-width: 700px) {
  /* Fewer, larger strokes read better at small sizes than a dense chain. */
  /* The chain is off on phones. On a tall narrow screen a wide, short graphic
     either letterboxes across the middle or has to be blown up far past the
     viewport, and behind a headline this size it was competing with the type
     rather than sitting under it. It stays on desktop, where there is room
     for it to read as a field. */
  .hero__pep { display: none; }

  /* A compact centred group rather than three blocks spread down the screen.
     The supporting line sits close under the headline; the buttons sit a
     little further below it than that. */
  .hero .wrap {
    display: flex;
    flex-direction: column;
    justify-content: center;
    height: 100%;
    gap: 0;
    /* Sits above centre, not on it. Padding the foot shrinks the box the group
       centres in, lifting it by half the padding — 185px of clearance above the
       headline becomes about 115px, and the extra room collects at the bottom
       where the gradient is resolving to white. */
    padding-bottom: 17svh;
  }
  .hero__title { margin: 0; }
  .hero__lede { margin: 0.9rem 0 0; }
  .hero .acts { margin-top: 2.1rem; }

  /* --- The entrance -------------------------------------------------------
     Three different motions, in sequence. Driven by the .on class the reveal
     observer already sets, so the .js gate and the reduced-motion rule above
     keep covering this without being restated. */

  /* 1. The headline slides up. Further than the 22px default, because it is
        the largest thing on the screen and a short travel on type this size
        reads as a twitch rather than a move. */
  .js .is-front .hero__title.rv {
    opacity: 0;
    transform: translate3d(0, 44px, 0);
    transition: opacity 0.7s var(--ease), transform 0.85s var(--ease);
  }
  .js .is-front .hero__title.rv.on {
    opacity: 1;
    transform: none;
    transition-delay: 90ms;
  }

  /* 2. The lede pops: a short rise with a slight overshoot, starting as the
        headline settles. */
  .js .is-front .hero__lede.rv {
    opacity: 0;
    transform: translate3d(0, 16px, 0) scale(0.94);
    transition: opacity 0.4s var(--ease), transform 0.55s var(--spring);
  }
  .js .is-front .hero__lede.rv.on {
    opacity: 1;
    transform: none;
    transition-delay: 620ms;
  }

  /* 3. The buttons fade only. Something has to stop moving, or the sequence
        never resolves. */
  .js .is-front .hero .acts.rv {
    opacity: 0;
    transform: none;
    transition: opacity 0.75s var(--ease);
  }
  .js .is-front .hero .acts.rv.on {
    opacity: 1;
    transition-delay: 1000ms;
  }
  .pep__g { stroke-width: 3.4; --pep-op: 0.07; }
}

@media (prefers-reduced-motion: reduce) {
  /* Straight to the resting state — no twist, no draw-on, no fade. */
  .js .hero__pep svg,
  .js .pep__g,
  .js .pep__bb,
  .js .pep__a, .js .pep__o, .js .pep__r, .js .pep__g line {
    animation: none !important;
    stroke-dasharray: none !important;
    stroke-dashoffset: 0 !important;
    transform: none !important;
  }
  /* Children opaque, the group carrying the backdrop level. Opacity is set on
     the two tiers separately and without !important, because SVG group opacity
     multiplies with child opacity — forcing both to 1 put a stark black chain
     behind the headline. */
  .pep__bb, .pep__g line, .pep__a, .pep__o, .pep__r { opacity: var(--pep-op); }
}


/* =====================  BLUE TOP  ===========================================
   The front page opens on a deep blue field that runs behind the research-use
   ribbon, the header and the hero, then dissolves into the paper section below
   so there is no seam between them.

   The gradient is painted by body::before rather than by the hero, because the
   hero cannot reach up behind the header — <main> opens above it. Both layers
   are absolutely positioned at the top of the document and pointer-transparent,
   so nothing here participates in layout.
   ========================================================================== */
.has-blue { position: relative; }
/* Inner pages open on a shorter, lighter field; the home page goes deeper. */
.is-front { --blue-top: #04173a; }

.has-blue::before,
.has-blue::after {
  content: "";
  position: absolute; top: 0; left: 0; right: 0;
  /* Script pins this to the hero's exact bottom; the clamp is the fallback. */
  height: var(--blue-h, clamp(620px, 86vh, 900px));
  z-index: 0;
  pointer-events: none;
}

/* Base field.
   The gradient lands on the paper colour itself — not on transparent — and gets
   there by 86%, comfortably above where the next section starts. The lineup
   band is opaque, so it covers the tail of this layer; if the tail were still
   blue at that point you would see a hard line. Landing on #f5f5f7 means there
   is nothing to see wherever the cut happens to fall. */
.has-blue::before {
  background:
    linear-gradient(
      180deg,
      var(--blue-top, #0a2f6f) 0%,
      #0a3a86 22%,
      #0a51bd 48%,
      #0b6ee5 68%,
      #63a6e8 84%,
      #cbdcee 93%,
      /* Script sets --blue-land from the section that actually follows, so the
         final pixel is exactly the colour it meets. Document pages end on white
         and catalog pages on paper; one fixed colour left a step on whichever
         it did not match. The fallback covers the no-JS case. */
      var(--blue-land, #ffffff) 100%
    );
}

/* Drifting glow. Three soft lobes on a very slow, offset loop — the movement
   should be felt rather than watched. */
.has-blue::after {
  background:
    radial-gradient(46% 40% at 22% 24%, rgba(90, 190, 255, 0.42) 0%, transparent 68%),
    radial-gradient(40% 36% at 78% 34%, rgba(128, 96, 255, 0.34) 0%, transparent 70%),
    radial-gradient(52% 44% at 52% 78%, rgba(0, 224, 255, 0.26) 0%, transparent 72%);
  filter: blur(14px);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 52%, transparent 84%);
  mask-image: linear-gradient(180deg, #000 0%, #000 52%, transparent 84%);
  will-change: transform;
}

@keyframes aurora {
  0%   { transform: translate3d(-3%, -2%, 0) scale(1.04); }
  33%  { transform: translate3d(4%, 3%, 0)  scale(1.16); }
  66%  { transform: translate3d(-2%, 4%, 0) scale(1.06); }
  100% { transform: translate3d(-3%, -2%, 0) scale(1.04); }
}
.js .has-blue::after { animation: aurora 26s ease-in-out infinite; }

/* Ribbon, header and hero all sit on the field ------------------------------ */
.has-blue .ruo,
.has-blue .nav,
.has-blue main { position: relative; z-index: 1; }

.has-blue .ruo { background: transparent; color: rgba(255, 255, 255, 0.72); }

/* Header is invisible over the field, and becomes itself once you scroll past. */
.has-blue .nav:not(.solid) {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.has-blue .nav:not(.solid) .brand span,
.has-blue .nav:not(.solid) .nav__links a,
.has-blue .nav:not(.solid) .ico { color: #fff; }
.has-blue .nav:not(.solid) .brand em { color: rgba(255, 255, 255, 0.66); }
.has-blue .nav:not(.solid) .nav__links a:hover,
.has-blue .nav:not(.solid) .nav__links a[aria-current="page"],
.has-blue .nav:not(.solid) .ico:hover { background: rgba(255, 255, 255, 0.16); }
.has-blue .nav:not(.solid) .burger span,
.has-blue .nav:not(.solid) .burger span::before,
.has-blue .nav:not(.solid) .burger span::after { background: #fff; }

/* Hero type on the field ----------------------------------------------------- */
.is-front .hero { color: #fff; }
.is-front .hero .d1 {
  color: #fff;
  /* A whisper of shadow keeps the headline crisp where a glow lobe passes
     behind it, without reading as a drop shadow. */
  text-shadow: 0 2px 40px rgba(3, 18, 48, 0.35);
}
.is-front .hero .lede { color: rgba(255, 255, 255, 0.82); }

.is-front .hero .btn--blue {
  background: #fff; color: #0a2a66;
}
.is-front .hero .btn--blue:hover { background: rgba(255, 255, 255, 0.9); }
.is-front .hero .btn--wire {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.4);
  color: #fff;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.is-front .hero .btn--wire:hover {
  background: rgba(255, 255, 255, 0.18);
  border-color: rgba(255, 255, 255, 0.65);
}

/* Peptide on blue ------------------------------------------------------------
   Luminous rather than faint: white strokes with a soft bloom, nodes that
   pulse along the chain, and a bright signal running the backbone.
   -------------------------------------------------------------------------- */
.is-front .hero__pep {
  /* Fades on all four edges over the blue, so the chain passes through the
     frame rather than stopping at it. */
  -webkit-mask-image:
    linear-gradient(to right, transparent, #000 12%, #000 88%, transparent),
    linear-gradient(to bottom, #000 0%, #000 62%, transparent 96%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to right, transparent, #000 12%, #000 88%, transparent),
    linear-gradient(to bottom, #000 0%, #000 62%, transparent 96%);
  mask-composite: intersect;
}
.is-front .pep__g {
  stroke: rgba(255, 255, 255, 0.9);
  --pep-op: 0.3;
  filter: drop-shadow(0 0 10px rgba(140, 205, 255, 0.75))
          drop-shadow(0 0 26px rgba(60, 150, 255, 0.35));
}
.is-front .pep__o,
.is-front .pep__r,
.is-front .pep__a { fill: #0a3f8f; }

/* The signal. A short bright dash chasing the backbone, forever. */
.pep__flow { display: none; }
.is-front .pep__flow {
  display: block;
  opacity: 0;
  /* Its own level, far above the structure it runs along. */
  --pep-op: 0.62;
  stroke: #dff0ff;
  stroke-width: 3.6;
  stroke-linecap: round;
  fill: none;
  /* Two dashes on the ~5200-unit path, so a glint is almost always somewhere
     on screen rather than appearing once every seven seconds. */
  stroke-dasharray: 110 2490;
  filter: drop-shadow(0 0 5px rgba(235, 248, 255, 0.85))
          drop-shadow(0 0 14px rgba(170, 220, 255, 0.6))
          drop-shadow(0 0 30px rgba(100, 185, 255, 0.4));
}
@keyframes pep-signal { from { stroke-dashoffset: 2600; } to { stroke-dashoffset: 0; } }
.js .is-front .pep__flow {
  animation:
    pep-signal 6s linear 1.2s infinite backwards,
    pep-in 0.8s var(--ease) 1.2s both;
}

/* Nodes brighten in sequence, so the chain reads as active rather than static. */
@keyframes pep-pulse {
  0%, 72%, 100% { opacity: 0.55; r: 5; }
  80%           { opacity: 1;    r: 7; }
}
.js .is-front .pep__a {
  animation:
    pep-in 0.9s var(--ease) 0.55s both,
    pep-pulse 7s ease-in-out calc(1.7s + var(--n) * 0.28s) infinite;
}
/* The carbonyl oxygens and side-chain nodes ripple on the same travelling
   stagger. They only brighten - no radius change - because they are drawn at
   two different radii and a shared r keyframe would resize one of them. */
@keyframes pep-glim {
  0%, 76%, 100% { opacity: var(--pep-op); }
  84%           { opacity: calc(var(--pep-op) * 2.2); }
}
.js .is-front .pep__o,
.js .is-front .pep__r {
  animation:
    pep-in 0.9s var(--ease) 0.55s both,
    pep-glim 7s ease-in-out calc(1.9s + var(--n) * 0.28s) infinite;
}

/* Below 700px the field is shorter, so the chain sits higher in it. */
@media (max-width: 700px) {
  /* No --blue-h override here any more.

     This used to force clamp(560px, 78vh, 720px). The script measures the
     hero and sets --blue-h from it, and the ::before that paints the gradient
     is sized from the same value, so overriding one and not the other pulled
     them apart: on an 844px screen the gradient was painted 658px tall under
     a hero ending at 671px, and the blue stopped dead 13px early instead of
     fading out. Desktop had no override, which is why the fade was smooth
     there and abrupt on a phone. */
  .is-front .pep__g { --pep-op: 0.26; }
}

@media (prefers-reduced-motion: reduce) {
  .js .is-front::after,
  .js .is-front .pep__flow,
  .js .is-front .pep__o,
  .js .is-front .pep__r,
  .js .is-front .pep__a { animation: none !important; }
  /* The signal is motion by definition; without it, hide it rather than leave
     a stray dash sitting on the backbone. */
  .is-front .pep__flow { display: none; }

}

/* =====================  WOOCOMMERCE COMPATIBILITY  =========================
   WooCommerce ships its own stylesheet and it is enqueued alongside this one.
   Its image rules are written for square catalogue photography and assume a
   product image should fill its container. These vials are 742x1100 portrait
   cutouts, so left alone they get stretched to the column width.

   Everything here is a geometry correction. No colour, no type - those come
   from the design system above.
   ========================================================================== */

/* --- Catalogue tiles ----------------------------------------------------- */
/* Restated with .woocommerce in front so it outranks the plugin stylesheet
   regardless of enqueue order. object-fit is the load-bearing property: it
   keeps the vial's aspect inside a box whose shape is set by the tile. */
.woocommerce .pc__fig img,
.woocommerce-page .pc__fig img,
.pc__fig img {
  position: absolute;
  inset: 6%;
  width: 88%;
  height: 88%;
  object-fit: contain;
  max-width: none;
}

/* WooCommerce renders the loop as ul.products > li.product. The theme's card
   is the product element itself, so the list wrapper has to stop looking like
   a list and start behaving like the design's grid. */
.woocommerce ul.products,
ul.products {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: clamp(1.1rem, 2.2vw, 1.9rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
.woocommerce ul.products::before,
.woocommerce ul.products::after { content: none; }
.woocommerce ul.products li.product,
ul.products li.product {
  width: auto;
  margin: 0;
  float: none;
  clear: none;
}

/* --- Single product ------------------------------------------------------ */
/* .product__media had no rules at all, which is what let the vial stretch. */
.duo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.6rem, 4vw, 3.4rem);
  align-items: start;
}
.product__media {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--r-tile);
  overflow: hidden;
  background: linear-gradient(168deg, #fbfdff 0%, #dfeaf8 55%, #cfe0f4 100%);
}
.product__media img {
  position: absolute;
  inset: 5%;
  width: 90%;
  height: 90%;
  object-fit: contain;
  max-width: none;
}
/* WooCommerce wraps the gallery in its own divs and gives them inline widths. */
.product__media .woocommerce-product-gallery,
.product__media .woocommerce-product-gallery__wrapper,
.product__media figure {
  position: static;
  width: 100% !important;
  margin: 0;
}
.product__media .flex-viewport { position: static; }
/* The thumbnail strip is noise for a single-image product. */
.product__media .flex-control-thumbs { display: none; }

@media (max-width: 860px) {
  .duo { grid-template-columns: minmax(0, 1fr); }
}

/* --- Notices and buttons ------------------------------------------------- */
/* WooCommerce's default button is a grey pill that ignores the design. */
.woocommerce .summary button.button,
.woocommerce .summary a.button,
.woocommerce .summary .single_add_to_cart_button {
  border-radius: 999px;
  padding: 0.85rem 1.6rem;
  font-weight: 560;
  background: var(--blue);
  color: var(--white);
}
.woocommerce .summary .price {
  font-family: var(--display);
  font-size: var(--t-h3);
  font-weight: 620;
  color: var(--fg);
}

/* --- --blue-h fallback -----------------------------------------------------
   --blue-h is measured by the script from the page's .hero or .phead. A page
   with neither used to inherit whatever height the gradient defaulted to, and
   painted a blue field over content styled for a light ground: the cart and
   account pages rendered their text black on blue.

   Defaulting to zero makes the failure mode safe. No anchor, no field, and
   the page simply renders on paper as its content expects. */
:root { --blue-h: 0px; }

/* =====================  WOOCOMMERCE PAGES  =================================
   Cart, checkout and account keep WooCommerce's markup and logic — that is
   where the money is handled and it should stay their tested code. These
   rules only bring the typography, colour and spacing in line so the pages
   do not read as a different website.
   ========================================================================== */

.woocommerce-page .band .wrap { max-width: var(--w-tight, 62rem); }

/* Tables: the cart and order summary. */
.woocommerce table.shop_table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  font-size: var(--t-sm);
}
.woocommerce table.shop_table th,
.woocommerce table.shop_table td {
  padding: 0.9rem 1rem;
  border-bottom: 1px solid var(--line);
  color: var(--fg);
  text-align: left;
}
.woocommerce table.shop_table th {
  font-family: var(--mono);
  font-size: var(--t-micro);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fg-mute);
  background: var(--sunk, #f6f8fa);
}
.woocommerce table.shop_table img { width: 56px; height: auto; border-radius: 8px; }

/* Form fields, matching the contact form. */
.woocommerce form .form-row input.input-text,
.woocommerce form .form-row textarea,
.woocommerce form .form-row select,
.woocommerce-page input.input-text {
  width: 100%;
  min-height: 44px;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--white);
  color: var(--fg);
  font: inherit;
}
.woocommerce form .form-row label,
.woocommerce-page label { color: var(--fg); font-size: var(--t-sm); }
.woocommerce form .form-row input.input-text:focus,
.woocommerce-page input.input-text:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(11, 110, 229, 0.16);
}

/* Buttons. */
.woocommerce a.button,
.woocommerce button.button,
.woocommerce input.button,
.woocommerce #respond input#submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0.7rem 1.5rem;
  border: 0;
  border-radius: 999px;
  background: var(--blue);
  color: var(--white);
  font-weight: 560;
  font-size: var(--t-sm);
}
.woocommerce a.button:hover,
.woocommerce button.button:hover,
.woocommerce input.button:hover { background: var(--blue-dark, #0a51bd); color: var(--white); }
.woocommerce .button.alt { background: var(--blue); }

/* Notices. */
.woocommerce-message,
.woocommerce-info,
.woocommerce-error {
  margin: 0 0 1.4rem;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--blue);
  border-radius: 10px;
  background: var(--sunk, #f6f8fa);
  color: var(--fg);
  font-size: var(--t-sm);
  list-style: none;
}
.woocommerce-error { border-left-color: #b3261e; }

/* Account navigation. */
.woocommerce-MyAccount-navigation ul { list-style: none; margin: 0 0 1.6rem; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.woocommerce-MyAccount-navigation a {
  display: inline-block;
  padding: 0.5rem 0.95rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--fg);
  font-size: var(--t-sm);
}
.woocommerce-MyAccount-navigation .is-active a { background: var(--blue); border-color: var(--blue); color: var(--white); }

@media (max-width: 700px) {
  .woocommerce table.shop_table th { display: none; }
  .woocommerce table.shop_table td { display: block; border-bottom: 0; padding: 0.45rem 1rem; }
  .woocommerce table.shop_table tr { display: block; border-bottom: 1px solid var(--line); padding-block: 0.6rem; }
}

/* =====================  MOBILE PROPORTIONS  ================================
   The phone layout was correct but not proportional to the desktop one:

                        desktop         mobile (before)
     hero / viewport    632/900  70%    444/844  53%
     headline / hero    102/632  16%     47/444  11%

   The hero sat lower on the screen and its headline was smaller within it,
   so the page read as a narrow version of the design rather than the same
   design on a phone.

   The limit is the longest word. "Documented." is eleven characters, and at
   353px of usable width it stops fitting at around 58px, so the desktop's
   16% ratio cannot be reached on a 390px screen without hyphenating. This
   takes it as far as the type allows.
   ========================================================================== */
@media (max-width: 700px) {
  /* svh, not vh: on iOS vh counts the area behind the browser chrome, so a
     vh-sized hero is taller than the visible screen and the buttons start
     below the fold. */
  .hero {
    display: grid;
    align-content: center;
    /* The hero takes the whole first screen, so nothing of the section below
       shows before you scroll. 68svh left 128-217px of the catalogue peeking
       depending on the phone.

       --hdr-h is measured rather than assumed: the ribbon above the header
       wraps to two lines at 390px and one at 430px, so the space to subtract
       is 97px on one and 82px on the other. The fallback matches the taller
       case, so the hero is never short if the script has not run yet. */
    min-height: calc(100svh - var(--hdr-h, 97px));
    /* Even padding. The three blocks inside distribute themselves down the
       hero, so there is no pooled space to push around from out here. */
    padding-block: clamp(1.6rem, 5vw, 2.4rem);
    align-content: stretch;
  }

  /* Up from 47px. Held just under the point where the longest word wraps. */
  /* "Performance." is the longest word and it sets the ceiling. With
     text-wrap: balance the heading shrink-wraps, so the measurement that
     matters is the widest line against the container, not against the
     heading's own box:

       430px   widest 331 of 393 available   62px spare
       390px   widest 314 of 353            39px spare
       360px   widest 302 of 323            21px spare
       320px   widest 285 of 285             0px spare

     Only the smallest phones are actually tight, so they are handled
     separately below rather than by shrinking every size. */
  .hero__title { font-size: clamp(3rem, 1.9rem + 6vw, 3.6rem); }

  /* The supporting line and the buttons grow with it, or the headline starts
     to look detached from what follows it. */
  .hero__lede { font-size: 1.12rem; line-height: 1.5; }
  .hero .acts { gap: 0.7rem; }
  .hero .btn { min-height: 50px; padding-inline: 1.35rem; font-size: 1rem; }

  /* Section headings carry the same relationship to the hero as on desktop. */
  .band .d2 { font-size: clamp(2.35rem, 1.6rem + 3.4vw, 2.9rem); }
}

/* =====================  MOBILE CATALOGUE AND QUICK VIEW  ===================
   Two columns on a phone, and a quick view that fits on the screen.
   ========================================================================== */
@media (max-width: 700px) {
  /* One tile per screen means three quarters of the viewport per product and
     38 screens to reach the end of the catalogue. Two columns is the normal
     phone storefront pattern and puts about three times as many in view. */
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.85rem; }

  /* The tile is half as wide now, so its text has to come down with it or
     every product name wraps to three lines. */
  .pc__n { font-size: 1rem; line-height: 1.2; }
  .pc__m, .pc__doc { font-size: 0.72rem; }
  .pc__price { font-size: 0.95rem; }
  /* The long disclaimer does not survive a 160px column, and it is repeated
     verbatim in the quick view and on the product itself. */
  .pc__note { display: none; }
  .pc__code { top: 0.55rem; left: 0.6rem; font-size: 0.62rem; }
  .pc__dots { transform: scale(0.85); transform-origin: left center; }

  /* Quick view.
     The figure was 1:1 — a full 390px of image before a word of text, which
     pushed the price and Add to cart below the fold. Sizing it against the
     screen instead of its own width leaves room for the detail. */
  .qv { align-items: flex-end; }
  .qv__card {
    max-height: 94dvh;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
  }
  .qv__fig {
    aspect-ratio: auto;
    height: 30dvh;   /* 34dvh left the card 27px taller than the screen */
    min-height: 190px;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
  }
  .qv__body { padding: 1.1rem 1.15rem 1.4rem; }
  .qv__n { font-size: 1.18rem; }
  .qv__price { margin-top: 0.35rem; font-size: 1.15rem; }
  .qv__spec { margin-top: 0.7rem; font-size: 0.8rem; }
  .qv__spec > div { padding-block: 0.36rem; }
  .qv__buy { margin-top: 0.9rem; }
  .qv__note { margin-top: 0.7rem; font-size: 0.7rem; line-height: 1.45; }
  .qv__cat { font-size: 0.65rem; }
  .qv__code { font-size: 0.62rem; top: 0.7rem; left: 0.8rem; }
  .qv__x { width: 34px; height: 34px; top: 0.7rem; right: 0.7rem; }
}

/* Very narrow phones cannot carry two columns without the vial becoming a
   thumbnail, so they keep the single column. */
@media (max-width: 359px) {
  /* At 320px the headline had no room left at all. This is the only width
     that needs it, so the rest of the range keeps its size. */
  .hero__title { font-size: clamp(2.6rem, 1.5rem + 6.2vw, 2.95rem); }
  .grid { grid-template-columns: minmax(0, 1fr); }
  .pc__note { display: block; }
}

/* =====================  MEMBERSHIP CALL TO ACTION  =========================
   The one place on the site with a deliberately loud control. It closes the
   home page on a dark band, so the glow is a halo behind the button rather
   than a shadow under it — a shadow would disappear against the dark ground.

   The halo is a separate ::before that is blurred and scaled, so the button's
   own edge stays crisp; blurring the element itself would soften the label.
   ========================================================================== */
.acts--solo { margin-bottom: 1.6rem; }

.btn--glow {
  position: relative;
  isolation: isolate;
  padding: 1rem 2rem;
  border: 0;
  border-radius: 999px;
  background: linear-gradient(120deg, #0b6ee5 0%, #3f8ff0 52%, #0a51bd 100%);
  color: var(--white);
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: -0.005em;
}
.btn--glow::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: inherit;
  filter: blur(17px);
  opacity: 0.55;
  transform: scale(1.04);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}
.btn--glow:hover { color: var(--white); }
.btn--glow:hover::before { opacity: 0.85; transform: scale(1.1); }
.btn--glow:active { transform: scale(0.985); }
.btn--glow:focus-visible { outline: 2px solid var(--white); outline-offset: 3px; }

/* The single loudest control on the site, closing the home page. */
.btn--xl {
  padding: 1.35rem 3rem;
  font-size: clamp(1.05rem, 0.9rem + 0.6vw, 1.35rem);
  font-weight: 620;
  letter-spacing: -0.012em;
}
.btn--xl svg { width: 18px; height: 18px; }

/* A slow breath, so it reads as lit rather than as a flashing advert. */
@keyframes glow-breathe {
  0%, 100% { opacity: 0.45; transform: scale(1.03); }
  50%      { opacity: 0.8;  transform: scale(1.09); }
}
.js .btn--glow::before { animation: glow-breathe 4.5s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  .js .btn--glow::before { animation: none; }
  .btn--glow::before, .btn--glow:hover::before { transition: none; }
}

@media (max-width: 700px) {
  .btn--glow { width: 100%; justify-content: center; padding-inline: 1.2rem; }
  .acts--solo { margin-bottom: 1.1rem; }
}

/* ==========================  SITEWIDE SALE POP-UP  ==========================
   A real modal: fixed layer, dimmed backdrop, its own scroll if the card is
   taller than a short window.

   It is hidden by default and shown by script rather than the other way
   round, because the age gate is also a modal and gets to go first. Two
   stacked dialogs is the failure mode this ordering exists to avoid.

   The glow is a blurred copy of the card behind itself, not a shadow — on a
   dark blue card a shadow is invisible, where a halo reads as lit.
   ========================================================================= */
.promo {
  position: fixed; inset: 0; z-index: 220;
  display: grid; justify-items: center;
  /* safe: a card taller than the window would otherwise centre itself with
     its top above the scroll origin, where it cannot be reached. */
  align-content: safe center;
  padding: clamp(1rem, 4vw, 2rem);
  /* The overlay scrolls, never the card. Scrolling the card made a scroll
     container of it, which forced the other axis to auto as well, and the
     halo — a ::before scaled past the card's box on purpose — tripped the
     horizontal bar. Two bars then drew inside the rounded rectangle and
     squared off two corners. */
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  background: rgba(6, 20, 44, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0; visibility: hidden;
  transition: opacity 0.4s var(--ease), visibility 0s linear 0.4s;
}
.promo[data-open="true"] {
  opacity: 1; visibility: visible;
  transition: opacity 0.4s var(--ease), visibility 0s;
}

.promo__card {
  position: relative;
  isolation: isolate;
  width: min(420px, 100%);
  /* No max-height and no overflow: the overlay above handles a window too
     short for the card, and any overflow context here clips the halo and
     grows scrollbars inside the rounded corners. */
  padding: clamp(1.9rem, 5vw, 2.6rem) clamp(1.5rem, 5vw, 2.2rem) clamp(1.5rem, 4vw, 1.9rem);
  border-radius: var(--r-xl);
  background: linear-gradient(157deg, #0a51bd 0%, #0b6ee5 42%, #063a8f 100%);
  color: var(--white);
  text-align: center;
  transform: translateY(14px) scale(0.97);
  transition: transform 0.45s var(--spring);
}
.promo[data-open="true"] .promo__card { transform: none; }
.promo__card::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background: inherit;
  filter: blur(26px);
  opacity: 0.5;
  transform: scale(1.04);
}

.promo__pct {
  font-family: var(--display);
  font-size: clamp(3.4rem, 2.4rem + 4vw, 4.6rem);
  font-weight: 700; letter-spacing: -0.045em; line-height: 0.94;
  font-variant-numeric: tabular-nums;
}
.promo__pct span {
  font-size: 0.34em; font-weight: 560; letter-spacing: -0.01em;
  margin-left: 0.15em;
}
.promo__h {
  margin-top: 0.7rem;
  font-size: var(--t-h3); font-weight: 620; letter-spacing: -0.022em;
  line-height: 1.24; text-wrap: balance;
}
.promo__sub {
  margin-top: 0.5rem;
  font-size: var(--t-sm); color: rgba(255, 255, 255, 0.78);
}

.promo__code {
  display: inline-flex; align-items: baseline; gap: 0.6rem;
  margin-top: 1.3rem;
  padding: 0.6rem 1.15rem;
  border: 1px dashed rgba(255, 255, 255, 0.6);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.13);
  color: var(--white);
  font-family: var(--mono);
  font-size: var(--t-base); font-weight: 640; letter-spacing: 0.1em;
  cursor: pointer;
  transition: background 0.25s var(--ease), border-color 0.25s var(--ease);
}
.promo__code:hover { background: rgba(255, 255, 255, 0.22); border-color: var(--white); }
.promo__code small {
  font-family: var(--sans); font-size: var(--t-micro); font-weight: 450;
  letter-spacing: 0.02em; opacity: 0.72;
}
.promo__code[data-copied] small { opacity: 1; }

.promo__go { margin-top: 1.3rem; width: 100%; justify-content: center; }
.promo__ruo {
  margin-top: 1.1rem;
  font-size: var(--t-micro); letter-spacing: 0.05em; text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}

.promo__x {
  position: absolute; top: 0.75rem; right: 0.75rem;
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 50%;
  color: rgba(255, 255, 255, 0.72);
  transition: background 0.25s var(--ease), color 0.25s var(--ease);
}
.promo__x:hover { background: rgba(255, 255, 255, 0.18); color: var(--white); }
.promo__x:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }

/* A slow breath, so it reads as lit rather than as a flashing advert. */
@keyframes promo-breathe {
  0%, 100% { opacity: 0.4;  transform: scale(1.03); }
  50%      { opacity: 0.72; transform: scale(1.07); }
}
.js .promo[data-open="true"] .promo__card::before { animation: promo-breathe 4.5s ease-in-out infinite; }

@media (prefers-reduced-motion: reduce) {
  .js .promo .promo__card::before { animation: none; }
  .promo, .promo__card { transition: none; }
}
