@import url('/fonts/fonts.css');

/* ============================================================
   RECLAIM — design tokens
   Ported from Figma "Reclaim · Color" / "Reclaim · Scale"
   (file yFc9QSmqnTSk30F2ke0WpJ). Fluid values are solved so the
   curve passes exactly through the 390 and 1440 comps.
   ============================================================ */
:root {
  /* surfaces */
  --surface-canvas: #F7F5F2;
  --surface-card:   #FFFFFF;
  --surface-sand:   #EFEBE5;
  --surface-deep:   #1000C3;
  --surface-ink:    #111114;

  /* brand */
  --brand-blue:        #2E1BFF;
  --brand-blue-bright: #4800FF;
  --brand-blue-mid:    #4000FF;
  --brand-periwinkle:  #796DFF;
  --brand-tint:        #F1F0FF;   /* the Reclaim column in the pricing table */

  /* text — muted is darkened from the Figma #8A8A93, which measured 3.14:1 on the
     canvas and failed WCAG AA for normal text. #6A6A74 is 4.91:1. The footer sits on
     ink and needs the opposite correction, hence its own token at 8.00:1. */
  --text-primary:      #111114;
  --text-secondary:    #5A5A63;
  --text-muted:        #6A6A74;
  --text-on-ink:       #A8A8B2;
  --text-on-deep:      #FFFFFF;
  --text-on-deep-soft: #C3BCFF;

  /* lines */
  --line-soft:    #E4E2DE;
  --line-on-deep: #3F33CF;

  /* radii */
  --r-img: 16px;
  --r-card: 20px;
  --r-tile: 24px;
  --r-full: 999px;

  /* type ramp — Inter, sentence case, tight tracking */
  --t-display-xl: clamp(38px, calc(28.343px + 2.4762vw), 64px);
  --t-display-l:  clamp(30px, calc(24.800px + 1.3333vw), 44px);
  --t-display-m:  clamp(24px, calc(21.029px + 0.7619vw), 32px);
  --t-subhead:    clamp(19px, calc(17.886px + 0.2857vw), 22px);
  --t-body-l:     clamp(16px, calc(15.257px + 0.1905vw), 18px);
  --t-body:       16px;
  --t-body-s:     14px;
  --t-eyebrow:    clamp(12px, calc(11.629px + 0.0952vw), 13px);
  --t-mono-data:  clamp(14px, calc(13.629px + 0.0952vw), 15px);
  --t-mono-micro: 11px;
  --t-button:     15px;
  --t-cta:        clamp(46px, calc(31.143px + 3.8095vw), 86px);

  /* rhythm — ONE scale, solved through the same 390 / 1440 comps as the type ramp.
     Read it as a boundary system, not a padding system: adjacent block paddings
     STACK, so the gap a reader perceives between two sections is twice whatever
     each of them carries. Hence --section-pad is deliberately HALF a boundary.

                      390    1440
       --space-2xs      8       8
       --space-xs      12      16
       --space-s       16      24
       --space-m       20      32
       --space-l       28      48
       --space-xl      40      64
       --space-2xl     56      80   <- section padding-block

     The only two boundaries the page is allowed to use:
       major  = 2 x --space-2xl           = 112 / 160
                section <-> section, and content <-> band edge <-> content (80 + 80,
                split by the band's own colour edge)
       minor  = --space-m + --space-xl    =  60 /  96
                below the trust strip only — a rule between two sections, not a section

     The old --section-pad ran 64 -> 112px, so the boundary it produced ran 128 -> 225
     while --content-max had already capped at 1296. That is why the page read as
     over-padded at desktop and fine on mobile: 225px of air against a 1296px measure. */
  --space-2xs: 8px;
  --space-xs:  clamp(12px, calc(10.514px + 0.3810vw), 16px);
  --space-s:   clamp(16px, calc(13.029px + 0.7619vw), 24px);
  --space-m:   clamp(20px, calc(15.543px + 1.1429vw), 32px);
  --space-l:   clamp(28px, calc(20.571px + 1.9048vw), 48px);
  --space-xl:  clamp(40px, calc(31.086px + 2.2857vw), 64px);
  --space-2xl: clamp(56px, calc(47.086px + 2.2857vw), 80px);

  --gutter:      clamp(20px, calc(0.686px + 4.9524vw), 72px);
  --section-pad: var(--space-2xl);
  --content-max: 1296px;

  --ease: cubic-bezier(0.4, 0.14, 0.2, 1);
}

/* ============================================================
   scroll reveal
   ------------------------------------------------------------
   Every rule here is scoped to `.js`, which an inline <head> script adds. That is the
   whole safety design: with scripting off, blocked, or erroring before main.js runs,
   none of these selectors match and the page renders normally. A reveal system that
   hides by default and depends on JS to un-hide can blank an entire page, so it is
   built so the failure mode is "no animation" rather than "no content".

   Only opacity and translate are animated — both composited, so revealing a section
   costs no layout and no repaint. Nothing here changes box size, so there is no CLS.
   ============================================================ */
.js [data-reveal],
.js [data-reveal-stagger] > * {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .55s var(--ease), transform .55s var(--ease);
}
.js [data-reveal].is-in,
.js [data-reveal-stagger].is-in > * { opacity: 1; transform: none; }
/* Stagger is CSS, not JS: the observer only ever toggles one class per group. */
.js [data-reveal-stagger].is-in > :nth-child(2) { transition-delay: .07s; }
.js [data-reveal-stagger].is-in > :nth-child(3) { transition-delay: .14s; }
.js [data-reveal-stagger].is-in > :nth-child(4) { transition-delay: .21s; }
.js [data-reveal-stagger].is-in > :nth-child(5) { transition-delay: .28s; }
.js [data-reveal-stagger].is-in > :nth-child(6) { transition-delay: .35s; }
/* MOTION POLICY under an OS reduce-motion setting — softened, not silenced.
   The original build disabled every effect under prefers-reduced-motion, which made
   the site read as completely broken on any machine where Windows "Animation
   effects" is off or iOS "Reduce Motion" is on — a very common power-user setting,
   and exactly how the client first saw their own site.
   The setting exists for vestibular safety, which is about CONTINUOUS or LARGE
   TRAVELLING motion. So: the looping helix stays a poster and reveals lose their
   travel — but one-shot, sub-5s, small-area effects (fade-ins, the glimmer sweep,
   the pill rotation, the count-up) are kept. WCAG 2.2.2 exempts auto-playing motion
   of five seconds or less. */
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal], .js [data-reveal-stagger] > * {
    transform: none;                    /* fade only — no vertical travel */
  }
}

/* ============================================================
   base
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--surface-canvas);
  color: var(--text-primary);
  font-family: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: var(--t-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; }

:focus-visible {
  outline: 2px solid var(--brand-blue);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute; left: 8px; top: -60px; z-index: 100;
  background: var(--brand-blue); color: #fff;
  padding: 12px 20px; border-radius: var(--r-full);
  font-size: var(--t-button); font-weight: 500;
  transition: top .18s var(--ease);
}
.skip-link:focus { top: 8px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ============================================================
   type
   ============================================================ */
.display-xl, .display-l, .display-m, .subhead {
  font-weight: 500; margin: 0; text-wrap: balance;
}
.display-xl { font-size: var(--t-display-xl); line-height: 1.05; letter-spacing: -0.025em; }
.display-l  { font-size: var(--t-display-l);  line-height: 1.10; letter-spacing: -0.02em; }
.display-m  { font-size: var(--t-display-m);  line-height: 1.15; letter-spacing: -0.015em; }
.subhead    { font-size: var(--t-subhead);    line-height: 1.30; letter-spacing: -0.01em; }

.body-l { font-size: var(--t-body-l); line-height: 1.55; margin: 0; text-wrap: pretty; }
.body   { font-size: var(--t-body);   line-height: 1.60; margin: 0; text-wrap: pretty; }
.body-s { font-size: var(--t-body-s); line-height: 1.50; margin: 0; text-wrap: pretty; }

.eyebrow {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: var(--t-mono-micro);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--brand-blue); margin: 0;
}
.eyebrow--on-deep { color: var(--text-on-deep-soft); }
.mono-data {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: var(--t-mono-data); font-weight: 500; letter-spacing: 0.02em;
}
/* margin:0 is not cosmetic — .mono-micro is used on <p>, and without it the UA's
   1em margin (11px) was leaking into the page rhythm: it added 11px to the bottom
   of the "Half the cost" section and turned .compare__foot's 10px grid gap into 21. */
.mono-micro {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: var(--t-mono-micro); letter-spacing: 0.06em; text-transform: uppercase;
  margin: 0;
}
.muted { color: var(--text-secondary); }
.dim   { color: var(--text-muted); }

/* ============================================================
   layout
   ============================================================ */
.section { padding: var(--section-pad) var(--gutter); }
.section--hairline { border-top: 1px solid var(--line-soft); }
.wrap { max-width: var(--content-max); margin-inline: auto; }

/* Stacked, not side-by-side: the note sits UNDER the heading (client call — the
   side-by-side pushed subheads into a right column that read as disconnected).
   Notes are capped at two sentences editorially and 62ch physically. */
.section-head { display: block; margin-bottom: var(--space-l); }
.section-head__note { max-width: 62ch; margin-top: var(--space-s); }
.section-head .eyebrow { margin-bottom: var(--space-xs); }
.section-head--center { text-align: center; }
.tile__icon { width: 64px; height: 64px; object-fit: contain; margin-bottom: var(--space-xs); }
.section-head--center .section-head__note { margin-inline: auto; }

/* 2x2 for the four treatments — bento density rather than a 4-across strip, which
   would shrink each card below the point where the tablet render reads. */
/* One row of four (client call). At --content-max that's ~306px per card, which also
   resolves the earlier too-wide complaint without needing a cap. Collapses 4 -> 2 -> 1
   as the cards approach the point where the dose line can't breathe. */
.grid-cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-s); }
@media (max-width: 1100px) { .grid-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 1048px; margin-inline: auto; } }
@media (max-width: 640px) { .grid-cards { grid-template-columns: 1fr; max-width: 560px; } }

/* One gap token across both grids and both breakpoints — --space-s fluids 16 -> 24,
   which is what the old 24px/20px pair was approximating by hand. */
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-s); }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-s); }
@media (max-width: 1020px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 680px)  { .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* ============================================================
   buttons
   ============================================================ */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  height: 52px; padding: 0 28px;
  border-radius: var(--r-full);
  font-size: var(--t-button); font-weight: 500; letter-spacing: -0.01em;
  cursor: pointer; white-space: nowrap;
  transition: background-color .18s var(--ease), color .18s var(--ease),
              transform .18s var(--ease), box-shadow .18s var(--ease);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn--primary { background: var(--brand-blue); color: #fff; }
.btn--primary:hover { background: var(--brand-blue-mid); box-shadow: 0 8px 24px -8px rgba(46,27,255,.55); }
.btn--secondary { background: var(--surface-card); color: var(--brand-blue); border: 1px solid var(--line-soft); }
.btn--secondary:hover { border-color: var(--brand-blue); }
.btn--on-deep { background: #fff; color: var(--brand-blue); }
.btn--on-deep:hover { background: var(--brand-tint); }
.btn--block { width: 100%; }

/* ============================================================
   announcement + nav
   ============================================================ */
.announce {
  /* Flush full-bleed bar (client call — the inset pill read as detached on mobile).
     margin:0 is load-bearing: this is a <p>, and its UA margin escapes through
     <body> and paints a canvas band above the bar otherwise. */
  margin: 0;
  background: var(--surface-deep); color: var(--text-on-deep);
  text-align: center; padding: 9px var(--gutter);
  font-size: var(--t-body-s);
}
/* eonbio-style behaviour: merged with the page at rest; once scrolled it detaches
   into a floating glassy pill (blur + hairline + shadow, inset from the edges), and
   it HIDES on scroll-down / REVEALS on scroll-up (driven from main.js). All the
   float styles are layout-inert for the page: only the nav's own width and paint
   change, so content never shifts. */
.nav {
  /* Eon structure: links left, logo CENTRED, actions right. 1fr auto 1fr keeps the
     logo optically centred regardless of how the sides differ in width. */
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 24px;
  padding: 0 var(--gutter); height: 84px;
  background: var(--surface-canvas);
  border-bottom: 1px solid var(--line-soft);
  position: sticky; top: 0; z-index: 50;
  transition: transform .35s var(--ease), background-color .3s var(--ease),
              border-radius .3s var(--ease), margin .3s var(--ease),
              box-shadow .3s var(--ease), border-color .3s var(--ease), height .3s var(--ease);
}
.nav.is-float {
  margin-inline: clamp(10px, 2vw, 24px);
  height: 68px;
  transform: translateY(10px);
  background: rgba(247, 245, 242, .78);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-full);
  box-shadow: 0 12px 32px -18px rgba(17, 17, 20, .28);
  padding: 0 clamp(18px, 3vw, 34px);
}
.nav.is-hidden { transform: translateY(-120%); }
/* Same shadow-tree rule as .footer__logo — this one only ever LOOKED right because the
   default fill is black and the nav sits on canvas. Made explicit so a theme change
   can't silently break it. */
.nav__logo { height: 26px; width: auto; fill: var(--text-primary); }
.nav__home { justify-self: center; }
.nav__links { display: flex; gap: 28px; order: -1; }
.nav__links a { font-size: var(--t-button); font-weight: 500; }
.nav__links a:hover { color: var(--brand-blue); }
.nav__actions { display: flex; align-items: center; gap: 14px; justify-self: end; }
.nav__cta { font-size: var(--t-button); font-weight: 500; }
.nav__cta:hover { color: var(--brand-blue); }
/* outlined rounded-square icon buttons, per the Eon reference */
.nav__iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; flex: 0 0 auto;
  border: 1px solid var(--line-soft); border-radius: 12px;
  background: var(--surface-card);
  transition: border-color .2s var(--ease), color .2s var(--ease);
  color: var(--text-primary);
}
.nav__iconbtn:hover { border-color: var(--brand-periwinkle); color: var(--brand-blue); }
.nav__icon { width: 20px; height: 20px; }
.nav__toggle { display: none; }
.nav__icon--close { display: none; }
.nav__toggle[aria-expanded="true"] .nav__icon--menu { display: none; }
.nav__toggle[aria-expanded="true"] .nav__icon--close { display: block; }
@media (max-width: 900px) {
  .nav { height: 64px; gap: 10px; grid-template-columns: auto 1fr auto; }
  .nav__logo { height: 22px; }
  .nav__cta { display: none; }
  .nav__toggle { display: inline-flex; order: -2; }
  .nav__links {
    display: none;
    position: absolute; left: 0; right: 0; top: 100%;
    flex-direction: column; gap: 0; margin: 0;
    background: var(--surface-canvas);
    border-bottom: 1px solid var(--line-soft);
    padding: 8px var(--gutter) 20px;
  }
  .nav.is-float .nav__links {
    border: 1px solid var(--line-soft); border-radius: var(--r-card);
    margin-top: 8px;
  }
  .nav__links.is-open { display: flex; }
  .nav__links a { padding: 14px 0; border-top: 1px solid var(--line-soft); }
}

/* ============================================================
   hero
   ============================================================ */
/* Deliberately NOT inside .wrap: a max-width wrapper can't put anything against the
   viewport's left edge, and the helix has to bleed off it. The copy column
   re-establishes the page grid with its own margin instead.

   Fold budget: the announce bar and the sticky nav are both on screen at scrollTop 0,
   so both come out of the height. --hero-reveal deliberately leaves the top of the
   trust strip visible below the fold — that hairline IS the scroll cue, which is why
   there's no chevron (a bouncing arrow is a loud-DTC tell; this brand isn't that). */
/* --hero-reveal was 76px, sized when the strip below was a single line of text. The
   strip is now 203px of glass-icon cards, so 76 put the fold through the middle of
   them and cropped four icons. 46 lands it on the cards' rounded tops instead — still
   unmistakably "the page continues", without slicing artwork in half. */
:root { --nav-h: 88px; --announce-h: 43px; --hero-reveal: 46px; }
@media (max-width: 900px) { :root { --nav-h: 64px; } }

.hero {
  display: grid;
  grid-template-columns: clamp(320px, 44vw, 660px) minmax(0, 1fr);
  /* The cap lives INSIDE min-height via min(), not in a separate max-height.
     CSS applies max-height first and min-height second, so min-height always wins a
     conflict — `min-height: 100svh; max-height: 900px` on a tall window silently
     ignores the cap and the hero grows without limit. That is what made the header
     run to ~1600px on some browsers.
     No max-height at all now: if the copy itself ever exceeds the cap (large text
     settings, long translations) the section SHOULD grow rather than clip. */
  min-height: min(
    calc(100svh - var(--nav-h) - var(--announce-h) - var(--hero-reveal)),
    720px);
  /* Full-bleed on purpose: the render's ground carries a soft vignette (measured
     227,226,222 vs the page's 247,245,242), invisible only while the video runs off
     the viewport edge — capping the section exposed it as a grey rectangle. So the
     hero stays full-width and the CENTRING is done inside: the copy column is
     capped to half the content width and pushed against the centreline, so on wide
     screens copy + helix read as one centred pair while the helix keeps bleeding. */
  max-width: none; margin-inline: 0;
  padding: 0;
  overflow: clip;                 /* the helix runs past its track */
}

.hero__helix { position: relative; min-width: 0; }
/* The video carries a BAKED --surface-canvas ground (see tools/reclaim/encode-helix.sh
   for why it isn't an alpha format), so `cover` is safe: whatever it crops, the
   remaining frame still paints the exact page colour edge to edge. Centre-crop rather
   than edge-anchored because the column's aspect flips either side of the asset's
   0.826 — at 1440 it crops top/bottom, on a narrow tall panel it crops left/right, and
   the helix reads correctly cropped both ways since it already bleeds off its own
   frame edges. */
.hero__helix-media {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  max-width: none;                /* <video> isn't covered by the global img,svg reset */
  display: block;
  object-fit: cover;
  object-position: 50% 50%;
  pointer-events: none;
}

/* The two hero scenes stack in the same box; the vial one fades in on the
   "sex drive." phase using the identical 320ms/--ease as the headline word. */
.hero__vial-media { opacity: 0; transition: opacity .32s var(--ease); z-index: 1; }
.hero.is-ed .hero__vial-media { opacity: 1; }

.hero__copy {
  align-self: center; justify-self: start;
  display: grid; gap: 28px; justify-items: start;
  width: min(100%, 560px);
  margin-inline: clamp(28px, 4.4vw, 80px) var(--gutter);
  padding-block: 56px;
}
/* On wide screens the copy column starts at the viewport centre (the grid's first
   track is 44vw), so the copy is anchored just right of centre with the helix
   filling the left half — the balanced composition from the design comps. Cap the
   helix track at 50vw so the pair stays symmetric however wide the window goes. */
@media (min-width: 1500px) {
  .hero { grid-template-columns: minmax(0, 50vw) minmax(0, 1fr); }
  .hero__copy { margin-inline: clamp(48px, 5vw, 96px) var(--gutter); }
}
/* The eyebrow is load-bearing, not decoration: a vertically-centred stack with nothing
   above the headline floats, and in a regulated category it puts "prescription" on
   screen before the promise. */
.hero__copy .eyebrow { margin-bottom: -10px; }

/* ── rotating headline word ─────────────────────────────────
   The word crossfades between "hairline." and "ED."; the vial render slides in
   beside the word only during the ED phase. Driven from main.js (which also parks
   it on "hairline." under reduced motion — a forever-cycling headline is looping
   motion). The h1's aria-label carries the stable sentence. */
.hero__swap { display: inline-flex; align-items: center; gap: 0.14em; white-space: nowrap; }
.hero__word { display: inline-block; transition: opacity .32s var(--ease), transform .32s var(--ease); }
.hero__word.is-out { opacity: 0; transform: translateY(0.35em); }
/* Each product is a tiny sprite window (11x11 mini sheet) that SPINS for the whole
   time its word is on screen — pill with "hairline.", vial with "ED." Both stack in
   one box and FADE with exactly the word's transition (same duration, same ease,
   same vertical travel), so the swap reads as one gesture. */
.hero__objbox { position: relative; width: 0.92em; height: 0.92em; flex: 0 0 auto; }
.hero__obj {
  position: absolute; inset: 0;
  overflow: hidden; display: block;
  opacity: 0; transform: translateY(0.35em);
  transition: opacity .32s var(--ease), transform .32s var(--ease);
}
.hero__obj.is-on { opacity: 1; transform: none; }
.hero__obj-sheet {
  position: absolute; left: 0; top: 0;
  width: 1100%; height: 1100%;
  max-width: none; display: block;
}
.hero__ctas { display: flex; gap: 12px; flex-wrap: wrap; }

/* Mobile hero: a full-width helix BAND above the copy — restyled by design after
   both side-by-side variants disappointed (a `contain` ribbon floats in gutters, a
   `cover` ribbon crops most of the frame's width). In a WIDE box the asset gives up
   nothing: it already bleeds off its own top and bottom, so `cover` trims only
   vertically and every tablet stays whole. Copy keeps its full type scale below. */
@media (max-width: 1020px) {
  .hero {
    display: block;
    min-height: 0; max-width: none;
  }
  /* Height derived from WIDTH, not viewport units: svh resolves differently across
     browsers, and whenever the band's aspect dipped below the video's 0.826, cover
     flipped from width-constrained to height-constrained and cropped the spiral's
     sides (the Safari bug). aspect-ratio 1238/1200 keeps the band comfortably wider
     than the video's aspect at every size, so cover ALWAYS shows the full frame
     width and trims only the vertical bleed — identical composition everywhere. */
  .hero__helix { height: auto; aspect-ratio: 1238 / 1200; max-height: 540px; }
  .hero__helix-media { object-fit: cover; object-position: 50% 50%; }
  .hero__copy {
    width: auto; margin-inline: var(--gutter);
    padding-block: var(--space-l) var(--space-s); gap: var(--space-s);
  }
  .hero__ctas .btn { flex: 1 1 auto; }
}
@media (max-width: 680px) {
  /* same width-derived rule as above — no separate phone height */
  .hero__copy { padding-block: var(--space-m) var(--space-2xs); }
  .hero__copy .eyebrow { margin-bottom: -6px; }
  /* two-line lede on phones: the tail sentence is desktop-only colour */
  .hero__lede-more { display: none; }
}

.media-card {
  position: relative; overflow: hidden;
  border-radius: var(--r-tile);
  background: var(--surface-sand);
  min-height: 460px;
  display: flex; align-items: flex-end;
}
.media-card img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.media-card__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,.72) 0%, rgba(0,0,0,.05) 45%, rgba(0,0,0,0) 100%);
}
.media-card__body {
  position: relative; display: flex; align-items: center; gap: var(--space-s);
  padding: var(--space-l); width: 100%; color: #fff;
}
.media-card__body .eyebrow { color: var(--text-on-deep-soft); margin-bottom: 6px; }
.media-card__body > div { flex: 1 1 auto; min-width: 0; }
.media-card__arrow {
  flex: 0 0 auto; width: 52px; height: 52px; border-radius: var(--r-full);
  background: #fff; color: var(--brand-blue);
  display: grid; place-items: center; font-size: 20px;
  transition: transform .2s var(--ease);
}
.media-card:hover .media-card__arrow { transform: translateX(4px); }
@media (max-width: 680px) {
  .media-card { min-height: 380px; }
  .media-card__body { padding: var(--space-m); }
  .media-card__arrow { display: none; }
}

/* Leads the #how section since moving out of the hero — full width there, and taller
   than the old 456px-wide tile allowed. */
/* One step below the section boundary on purpose: the card leads INTO this section,
   so it has to read as part of it rather than as its own band. (Was a bespoke
   clamp(40px, 4.4vw, 72px) that resolved to 63.36px — --space-xl is 64.) */
/* ── two-panel: copy left, image right ─────────────────────────
   Replaces the full-width overlaid photo card. The old crop cut the top of his head
   off, which on a hair-loss site is the subject of the photograph. The media panel
   therefore carries the source's native 4:3 rather than a designed height, so `cover`
   is a no-op and NOTHING is cropped — no object-position guesswork to get wrong. */
/* ONE container, not two floating columns (client call — the open version read as
   disconnected). Copy padded on the left, image flush to the right edge; the card's
   own radius clips the image so no inner rounding is needed. The image keeps its
   native 4:3, which sets the card height, and the copy centres against it. */
.split {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0; align-items: stretch;
  background: var(--surface-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-tile);
  overflow: hidden;
  margin-bottom: var(--space-xl);
}
.split__copy {
  display: grid; gap: var(--space-s); justify-items: start; align-content: center;
  padding: var(--space-l) var(--space-xl);
}
.split__copy .btn { margin-top: var(--space-2xs); }
.split__media {
  margin: 0;
  aspect-ratio: 4 / 3;                 /* == the source, so cover crops nothing */
  background: var(--surface-sand);
}
.split__media img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; }
  /* Image first on narrow: it is the emotional hook and the copy reads better under
     it than above it once they stack. */
  .split__media { order: -1; }
  .split__copy { padding: var(--space-m); }
}

/* ============================================================
   trust strip
   ============================================================ */
/* NOT a moving marquee, deliberately:
   1. Nothing overflows — four items fit one row to 1240 and a 2x2 below it, so there
      is no hidden content for scrolling to reveal, which is a marquee's only job.
   2. WCAG 2.2.2 Pause/Stop/Hide is Level A and covers anything moving automatically
      for >5s alongside other content. prefers-reduced-motion does NOT satisfy it —
      2.2.2 wants an in-page control, so motion here costs a pause button living in
      the strip forever.
   3. Register — the same call already made against a scroll chevron on the hero.
   The presence asked for is bought with containers, glass renders and a 3x contrast
   jump instead. */
.trust {
  padding: var(--space-m) var(--gutter);
  border-block: 1px solid var(--line-soft);
}
/* .wrap sits on the LIST, not the band, so the hairlines stay full-bleed while the
   cards cap at --content-max and line up with the section heads below. */
/* Centred flex, not a 4-column grid: equal columns each left-aligned their content,
   so the SET sat left of centre (client: "my OCD"). Flex with a centred main axis and
   an even gap centres the group as a whole. */
.trust__list {
  /* margin-inline:auto is what actually centres the 1296px list on WIDE viewports —
     the earlier `margin: 0` (a <ul> reset) beat .wrap's auto margins and pinned the
     list to the left gutter, so at 1970px the whole row sat 265px left of centre. */
  list-style: none; margin: 0 auto; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: var(--space-s) clamp(24px, 4vw, 64px);
}
/* Containers OFF (client call via Loom 2026-08-13): icon + label pairs floating in
   one clean centred row, nothing boxed. */
.trust__item {
  display: flex; align-items: center; justify-content: center; gap: var(--space-xs);
  padding: 0;
}
/* Fixed 88px, never fluid: the glass renders lose their volumetric read below ~88px
   (same floor as .step__art), so the ICON is the constant and the GRID absorbs the
   responsive load — the column count drops before the icon ever does. 88 also sits
   just under .step__art's 98px minimum, so a step icon stays the larger of the two at
   every width and the hierarchy can't invert. */
.trust__icon { flex: 0 0 auto; width: 88px; height: 88px; object-fit: contain; }
/* --text-primary on --surface-card = 18.85:1, against 6.27:1 for the old
   --text-secondary on canvas. The complaint was perceptual weight, not compliance. */
.trust__label {
  color: var(--text-primary);
  font-size: var(--t-body); font-weight: 500;
  line-height: 1.3; letter-spacing: -0.01em;
  text-wrap: balance;
}
/* 4-up needs ~1040px of content width (18+88+12+109+18 per card, widest unbreakable
   word "FDA-approved" at 109px in the shipped Inter); that runs out near 1155px of
   viewport, so flip at 1240 with slack for the fallback face. */
@media (max-width: 1240px) { .trust__item { flex: 0 1 calc(50% - 32px); justify-content: center; } }
@media (max-width: 560px) {
  .trust__item { flex-direction: column; align-items: center; text-align: center; gap: 8px; }
}
/* The client's note: "too much padding from the usp marquee and this section".
   The strip is a RULE, not a section — one line of text between two hairlines — so a
   full section boundary under it reads as a hole rather than as breathing room. It
   drops one rung on the ladder: minor boundary = 2 x --space-l = 56 / 96, made of the
   strip's own --space-m below its text plus --space-xl above the next section's first
   element. Was 26 + 112 = 139 at 1440, against a 224 boundary everywhere else. */
.trust + .section { padding-top: var(--space-xl); }
/* Dropping to 14px takes the widest word to 95px, which is what lets the 2-up stay
   horizontal all the way down to 560 instead of needing a third column count. */
@media (max-width: 780px) { .trust__label { font-size: var(--t-body-s); } }

/* ============================================================
   how it works
   ============================================================ */
/* Bento: each step in the same container recipe as .trust__item / .tile / .mech,
   content centred. Gap drops from --space-xl to --space-s — bento cells sit close;
   the breathing room the old open layout needed now lives INSIDE the cards. */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-s); }
.step {
  position: relative;             /* anchors the mobile timeline dot */
  background: var(--surface-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-card);
  padding: var(--space-l) var(--space-m);
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
/* Glass renders, not line icons, so they run far larger than the 42px a stroke icon
   would take — the optical detail (volumetric absorption, Fresnel edges) is invisible
   below ~88px.
   No tile: these are the blue-throughout renders, whose body measures 5.7-6.2:1 against
   the canvas, so they float unaided. The earlier near-white renders needed a dark tile
   because 81-92% of their area sat below the 3:1 floor. If the art is ever re-generated
   pale, the tile has to come back — check with tools/reclaim/measure-blue.mjs first. */
.step__art {
  position: relative; display: block;
  width: clamp(98px, calc(83.143px + 3.8095vw), 138px);
  aspect-ratio: 1;
  margin-bottom: var(--space-s);  /* moved off .step__icon */
}
.step__icon { width: 100%; height: 100%; object-fit: contain; }

/* ── the cascading glimmer ────────────────────────────────────
   A white duplicate of the icon, revealed through a narrow gradient band that
   travels across it, so the highlight follows the render's own alpha silhouette.
   That matters more than it sounds: these icons sit on the bare canvas with no
   tile, so a box-shaped sweep would show its own corners against #F7F5F2 and read
   as a rectangle sliding over the art.

   `filter` and `mask` are on SEPARATE boxes on purpose — WebKit has long-standing
   bugs when -webkit-mask and filter land on the same element (dropped masks, black
   fills). One extra <span> buys immunity from a whole class of Safari failure.

   The duplicate <img> carries an identical srcset/sizes, so it resolves to the same
   candidate the real icon already fetched and comes straight off the memory cache:
   one network request, zero extra bytes. */
.step__glimmer {
  position: absolute; inset: 0;
  opacity: 0;
  pointer-events: none;
  /* Narrow band, soft shoulders. mask-size is 300%, so each 1% of the gradient is 3%
     of the icon — the 44-56% span here is a ~50px sweep on a 138px icon. Widening
     these stops reads as a wash over the whole icon rather than a specular glint. */
  -webkit-mask-image: linear-gradient(76deg, rgba(0,0,0,0) 44%, rgba(0,0,0,.28) 48%, rgba(0,0,0,1) 50%, rgba(0,0,0,.28) 52%, rgba(0,0,0,0) 56%);
          mask-image: linear-gradient(76deg, rgba(0,0,0,0) 44%, rgba(0,0,0,.28) 48%, rgba(0,0,0,1) 50%, rgba(0,0,0,.28) 52%, rgba(0,0,0,0) 56%);
  -webkit-mask-size: 300% 300%;    mask-size: 300% 300%;
  -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
  -webkit-mask-position: 118% 50%; mask-position: 118% 50%;
}
.step__glimmer img {
  display: block; width: 100%; height: 100%; object-fit: contain;
  filter: brightness(0) invert(1);   /* every opaque pixel -> white; alpha untouched */
}

/* Without mask support the gradient is ignored and the opacity keyframes alone would
   flash a solid white silhouette — considerably worse than no effect. Drop the layer
   rather than degrade to that. The glimmer carries no information, so a static icon
   is the correct fallback. */
@supports not ((-webkit-mask-image: linear-gradient(#000,#000)) or (mask-image: linear-gradient(#000,#000))) {
  .step__glimmer { display: none; }
}

/* Linear, deliberately not var(--ease): a strong ease-in-out loiters at both ends —
   which are off the icon and invisible — and races through the middle, which is the
   entire point of the effect. Real speculars travel at constant speed. */
.steps.is-lit .step__glimmer { animation: step-glimmer 900ms linear both; }
.steps.is-lit .step:nth-child(2) .step__glimmer { animation-delay: 200ms; }
.steps.is-lit .step:nth-child(3) .step__glimmer { animation-delay: 400ms; }

/* Replaying on hover needs a SEPARATE keyframes name. After the scroll cascade runs,
   `animation: step-glimmer ... both` stays applied to the element, so re-declaring the
   same animation on :hover is a no-op and nothing replays. A distinct name is a distinct
   animation, so it starts fresh on every pointer entry.
   Hover-capable pointers only — on touch, :hover latches after a tap and the glimmer
   would fire at a random moment and then stick. */
@media (hover: hover) and (pointer: fine) {
  /* Whole .step is the target, not just .step__art — a 138px icon is a mean target, and
     "point at the step, the icon catches the light" is the readable interaction. */
  .step:hover .step__glimmer { animation: step-glimmer-hover 900ms linear both; }
}

@keyframes step-glimmer {
  0%   { opacity: 0;   -webkit-mask-position: 118% 50%; mask-position: 118% 50%; }
  12%  { opacity: .75; }
  82%  { opacity: .75; }
  100% { opacity: 0;   -webkit-mask-position: -18% 50%; mask-position: -18% 50%; }
}
/* Same curve as the cascade so the two triggers read as one effect. */
@keyframes step-glimmer-hover {
  0%   { opacity: 0;   -webkit-mask-position: 118% 50%; mask-position: 118% 50%; }
  12%  { opacity: .75; }
  82%  { opacity: .75; }
  100% { opacity: 0;   -webkit-mask-position: -18% 50%; mask-position: -18% 50%; }
}

.step h3 { margin: 0 0 var(--space-xs); }
/* gap is inherited, not re-declared: --space-xl is already 40px at 390. */
@media (max-width: 900px) { .steps { grid-template-columns: 1fr; } }

/* ── mobile timeline ──────────────────────────────────────────
   Stacked steps get a rail on the left: a hairline with one dot per card. main.js
   keeps .is-active on whichever step is crossing the middle of the viewport; the
   active dot fills brand blue, the card's stroke lifts, and its glimmer re-runs.
   The glimmer rule is AFTER the .is-lit rules in the cascade order, so while a card
   is active it wins the `animation` property; class churn between cards restarts it. */
@media (max-width: 900px) {
  .steps { position: relative; padding-left: 30px; }
  .steps::before {
    content: ''; position: absolute; left: 5px; top: 14px; bottom: 14px;
    width: 2px; border-radius: 1px; background: var(--line-soft);
  }
  .step { transition: border-color .3s var(--ease); }
  .step::before {
    content: ''; position: absolute; left: -30px; top: 50%; translate: 0 -50%;
    width: 12px; height: 12px; border-radius: 50%;
    background: var(--surface-canvas); border: 2px solid var(--line-soft);
    transition: background-color .3s var(--ease), border-color .3s var(--ease), box-shadow .3s var(--ease);
  }
  .step.is-active { border-color: var(--brand-periwinkle); }
  .step.is-active::before {
    background: var(--brand-blue); border-color: var(--brand-blue);
    box-shadow: 0 0 0 4px var(--brand-tint);
  }
  .step.is-active .step__glimmer { animation: step-glimmer 900ms linear both; }
}

/* ============================================================
   treatment cards
   ============================================================ */
/* One card, one surface: the gradient is painted on the CARD, starting saturated at
   the top and dissolving to white before the copy begins — per the client's comp.
   This replaces the old two-part construction (sand media panel over white body),
   whose junction read as "a line in the middle". The dome image is gone entirely. */
.card {
  display: flex; flex-direction: column;
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--brand-periwinkle) 58%, #fff) 0%,
      color-mix(in srgb, var(--brand-periwinkle) 20%, #fff) 34%,
      var(--surface-card) 56%);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-tile);
  overflow: hidden;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease), transform .2s var(--ease);
}
.card:hover { border-color: #d5d2ff; box-shadow: 0 18px 40px -24px rgba(17,17,20,.25); transform: translateY(-2px); }

.card__media { position: relative; aspect-ratio: 416 / 300; }
.card__pill {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  width: 55%; aspect-ratio: 1;
  /* A clipping WINDOW. The sheet inside is an <img> moved by transform — NOT a
     background-image. The background-size/position percentage path mis-rendered on
     iOS Safari (huge zoomed fragments) while identical geometry was perfect in
     Chromium; composited transforms on an img are the one sprite mechanism WebKit
     has never got wrong. main.js injects the img and drives it. */
  overflow: hidden;
}
.card__pill-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.card__pill-sheet {
  position: absolute; left: 0; top: 0;
  /* 800% = the 8x8 HD sheets: two per product (frames 0-63 / 64-120), because a
     single 121-frame sheet caps cells at ~370px under iOS's ~16.7MP image-decode
     ceiling — the split lifts cells to 448px, each sheet staying under the limit. */
  width: 800%; height: 800%;
  max-width: none; display: block;
  will-change: transform;
}
.card__body { display: flex; flex-direction: column; gap: var(--space-xs); padding: var(--space-m); flex: 1 1 auto; }
.card__body h3 { margin: 0; }
/* margin:0 is load-bearing. This is a bare <p> and its parent is a FLEX column —
   flex items do not collapse margins, so the UA's 1em sat on top of the container's
   16px gap and produced 32px above and below the dose line where the card design
   asks for 16. Same leak class as .mono-micro and .stat__value. */
.card__dose { margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2xs); }
.card__dose .sep { color: var(--text-muted); }
.card__desc { flex: 1 1 auto; }
.card__rule { height: 1px; background: var(--line-soft); }
.card__foot { display: flex; align-items: center; gap: var(--space-xs); }
.card__foot .price { flex: 1 1 auto; }
/* A glass render, matching the step/USP/trust icon family — not a disc with a glyph. */
.card__arrow {
  width: 44px; height: 44px; object-fit: contain;
  transition: transform .2s var(--ease);
}
.card:hover .card__arrow { transform: translateX(4px); }

/* The rotation is driven by JS (a requestAnimationFrame frame-stepper in main.js),
   not CSS keyframes. The two-axis steps() approach was rebuilt after it broke twice:
   once when a fill-mode parked the sheet on a blank cell, and once when the sheet
   geometry changed under immutable-cached files. The stepper walks the 11x11 sheet
   cell by cell from JS, so there is exactly one source of truth for frame order,
   timing, replay and rest state. */
@media (prefers-reduced-motion: reduce) {
  .card:hover { transform: none; }
  .btn:hover { transform: none; }
  .card:hover .card__arrow { transform: none; }
}

/* ============================================================
   half the cost — comparison table
   ============================================================ */
/* Bento'd: the table lives inside a max-width card, centred. Full content-width gave
   the four rows acres of empty tracking between columns; 880px is enough for the
   longest row plus the two price columns without the dead air. */
.compare-shell {
  max-width: 880px; margin-inline: auto;
  background: var(--surface-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-tile);
  padding: var(--space-l) var(--space-l) var(--space-m);
  overflow: hidden;
}
.compare {
  /* separate, NOT collapse: border-collapse:collapse discards border-radius on cells,
     and the tinted columns need rounded ends. border-spacing puts an even gutter
     between the three columns (the tinted pills were touching); the negative inline
     margins cancel the outer spacing so the table still aligns with the shell. */
  border-collapse: separate; border-spacing: var(--space-s) 0;
  width: calc(100% + 2 * var(--space-s));
  margin-inline: calc(-1 * var(--space-s));
}
/* The tinted column is one visual pill inside the card: round its first and last
   cells with the nested radius (--r-img, per the radii scale). */
.compare tbody tr:first-child .col-us { border-top-left-radius: var(--r-img); border-top-right-radius: var(--r-img); }
.compare tbody tr:last-child .col-us { border-bottom-left-radius: var(--r-img); border-bottom-right-radius: var(--r-img); }
.compare th, .compare td { text-align: left; vertical-align: middle; }
.compare thead th {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: var(--t-mono-micro); font-weight: 400;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-muted); padding: 0 var(--space-m) var(--space-s) 0;
}
.compare tbody th, .compare tbody td { padding: var(--space-s) var(--space-m) var(--space-s) 0; border-top: 1px solid var(--line-soft); }
.compare tbody th { font-weight: 400; padding-left: 0; }
.compare .t-name { font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: var(--t-mono-data); font-weight: 500; }
.compare .t-sub { color: var(--text-muted); font-size: var(--t-body-s); }
.compare .col-them {
  background: var(--surface-sand);
  padding-left: var(--space-m);
  font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: var(--t-mono-data);
}
/* The value columns are self-contained pills, but they are still TABLE columns — the
   row rules stay so the grid reads, drawn in a neutral ink-alpha that works on both
   the sand and the tint ground. Only the FIRST row's rule is dropped, so no line cuts
   across the rounded top corners. The vertical hairline dividers go — the containers
   do the column separation. */
.compare tbody .col-them, .compare tbody .col-us {
  border-top: 1px solid rgba(17, 17, 20, .08);
  border-left: 0;
}
.compare tbody tr:first-child .col-them,
.compare tbody tr:first-child .col-us { border-top: 0; }
.compare tbody tr:first-child .col-them { border-top-left-radius: var(--r-img); border-top-right-radius: var(--r-img); }
.compare tbody tr:last-child .col-them { border-bottom-left-radius: var(--r-img); border-bottom-right-radius: var(--r-img); }
.compare .col-us {
  padding-left: var(--space-m);
  background: var(--brand-tint); color: var(--brand-blue);
  font-family: 'IBM Plex Mono', ui-monospace, monospace; font-size: var(--t-mono-data); font-weight: 500;
}
.compare .col-us .t-sub { color: var(--brand-blue); opacity: .8; font-family: Inter, sans-serif; }
.compare thead .col-them, .compare thead .col-us { background: none; border-left: 0; color: var(--text-muted); }
.compare__foot { text-align: center; margin-top: var(--space-l); display: grid; gap: var(--space-2xs); justify-items: center; }

/* stacked cards on narrow screens — a 3-col table is unreadable at 390 */
@media (max-width: 760px) {
  /* The desktop machinery — border-spacing gutters, negative-margin compensation,
     per-corner radii on the tinted columns — must all reset here: on display:block
     elements it turns into stray margins and clipped corners, which is exactly the
     broken phone layout reported 2026-08-13. */
  .compare, .compare tbody, .compare tr, .compare th, .compare td { display: block; width: 100%; }
  .compare { margin-inline: 0; border-spacing: 0; }
  .compare thead { display: none; }
  .compare tbody tr {
    border: 1px solid var(--line-soft); border-radius: var(--r-img);
    overflow: hidden; margin-bottom: var(--space-s);
    padding: var(--space-2xs);
    background: var(--surface-card);
  }
  .compare tbody th { border-top: 0; padding: var(--space-s); }
  /* value cells become inset rounded chips with an even gap; all four corners round,
     overriding the desktop first/last-row radius split */
  .compare tbody td {
    border-top: 0; padding: var(--space-s);
    border-radius: var(--r-img) !important;
    margin-top: var(--space-2xs);
  }
  .compare tbody td::before {
    content: attr(data-label); display: block;
    font-family: 'IBM Plex Mono', ui-monospace, monospace;
    font-size: var(--t-mono-micro); letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-muted); margin-bottom: 4px;
  }
  .compare .col-them, .compare .col-us { border-left: 0; padding-left: var(--space-s); }
}

/* ============================================================
   science
   ============================================================ */
/* stretch, not start: the photo and the three boxes are one bento row and must share
   a bottom edge. The image DROPS its fixed aspect-ratio — the mech column is the
   height authority and the photo cover-fills whatever that is; with a fixed ratio the
   two columns could never agree. */
.science__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-s); align-items: stretch; }
/* The img is ABSOLUTE inside the figure so it contributes zero intrinsic height to
   the grid track — otherwise the photo's natural height competes with the mech column
   and the taller of the two wins, which is exactly the mismatch being fixed. */
.science__img { position: relative; border-radius: var(--r-tile); overflow: hidden; min-height: 320px; }
.science__img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Boxed rather than hairline-separated. Same recipe as .trust__item and .tile — white
   on canvas, 1px --line-soft, --r-card — so the page keeps ONE container vocabulary
   instead of inventing a treatment per section.
   1fr rows: the three boxes split the column height equally, so the row's height is
   set by the column as a whole and every box bottom lines up. */
.mech-list { display: grid; gap: var(--space-s); grid-template-rows: repeat(3, 1fr); }
.mech {
  background: var(--surface-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-card);
  padding: var(--space-m);
}
.mech__head { display: flex; align-items: center; gap: var(--space-xs); margin-bottom: var(--space-2xs); }
.mech__head .idx { color: var(--brand-blue); }
.mech h3 { margin: 0; }
@media (max-width: 900px) { .science__row { grid-template-columns: 1fr; } }

/* ============================================================
   full-bleed image bands
   ============================================================ */
.band { position: relative; overflow: hidden; padding: var(--section-pad) var(--gutter); color: #fff; }
.band > img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0;
}
.band__scrim { position: absolute; inset: 0; z-index: 1; }
.band .wrap { position: relative; z-index: 2; }
.band--evidence .band__scrim { background: rgba(0,0,0,.58); }
.band--cta .band__scrim {
  background: color-mix(in srgb, var(--surface-deep) 70%, transparent);
  mix-blend-mode: multiply;
}
.band--cta .band__scrim2 { position: absolute; inset: 0; z-index: 1; background: rgba(5,0,46,.16); }

/* Bento tiles on the dark band: translucent ink cards with hairline borders, so the
   photograph still reads through and the tiles feel cut from the band rather than
   pasted onto it. Same radius family as the light bento cells. */
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-s); margin: var(--space-l) 0; }
.stat__label { text-align: left; }
/* Outlined, not filled (client call via Loom follow-up): a white stroke on the
   photo band, no background. */
.stat {
  background: none;
  border: 1px solid rgba(255,255,255,.38);
  border-radius: var(--r-card);
  padding: var(--space-m);
  text-align: left;
}
/* tabular-nums is load-bearing, not typographic taste: the value counts up from 0, and
   proportional digits change width as it climbs, which would shove the label under it
   around for the whole animation. */
/* margin:0 is the biggest single margin leak on the page: this is a bare <p>, so the
   UA's 1em resolves against --t-display-xl and lands at 64px top AND bottom, silently
   inflating every gap in the evidence band. */
.stat__value {
  margin: 0;
  font-size: var(--t-display-xl); font-weight: 500; line-height: 1.05; letter-spacing: -.025em;
  /* tabular-nums is load-bearing, not typographic taste: the value counts up from 0,
     and proportional digits change width as it climbs, which would shove the label
     under it around for the whole animation. */
  font-variant-numeric: tabular-nums;
}
.stat__label { opacity: .82; margin-top: var(--space-xs); }
@media (max-width: 860px) {
  .stats { grid-template-columns: 1fr; gap: var(--space-s); }
  /* Same outlined rounded container as desktop (client call, superseding an earlier
     "bare on mobile" note) — the white stroke/no-fill treatment inherits; only the
     alignment centres. */
  .stat { text-align: center; }
  .stat__label { text-align: center; max-width: 28ch; margin-inline: auto; }
}

/* ── CTA split: one seamless black container ──────────────────
   Same construction as the assessment .split — copy beside media, media on top on
   mobile. The copy half is the video's exact ground (#000, measured from the render
   corners), so there is no visible seam between the halves and the timelapse gets
   its spotlight rather than carrying a headline. */
.cta-split {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  background: #000; color: var(--text-on-deep);
  border-radius: var(--r-tile); overflow: hidden;
}
.cta-split__copy {
  display: grid; gap: var(--space-m); align-content: center; justify-items: start;
  padding: var(--space-xl);
}
.cta-split__copy p { opacity: .88; }
.cta-split__media { margin: 0; position: relative; min-height: 380px; }
.band__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 900px) {
  .cta-split { grid-template-columns: 1fr; }
  /* media first — the video is the hook. A 4:3 panel with `contain`: the video
     letterboxes inside it, but the bars are #000 on #000 — invisible — so the panel
     gets real presence while the full frame always shows, uncropped. */
  .cta-split__media { order: -1; min-height: 0; aspect-ratio: 4 / 3; }
  .cta-split__media .band__video { object-fit: contain; }
  .cta-split__copy { padding: var(--space-l) var(--space-m) var(--space-l); }
}

/* ============================================================
   included / faq / isi / footer
   ============================================================ */
.tile {
  background: var(--surface-card); border: 1px solid var(--line-soft);
  border-radius: var(--r-card); padding: var(--space-m); display: grid; gap: var(--space-xs); align-content: start;
}
.tile h3 { margin: 0; }

/* Capped like .compare-shell — full content width left the Q&A rows stretched thin. */
.faq { max-width: 880px; margin-inline: auto; }
.faq__item { border-top: 1px solid var(--line-soft); }
.faq__q {
  width: 100%; display: flex; align-items: center; gap: var(--space-s);
  padding: var(--space-m) 0; cursor: pointer; text-align: left;
}
.faq__q > span:first-child { flex: 1 1 auto; }
.faq__mark { color: var(--brand-blue); font-size: var(--t-subhead); line-height: 1; }
.faq__a { padding: 0 0 var(--space-m); max-width: 860px; }
.faq__item[data-open='false'] .faq__a { display: none; }

/* .isi and .footer sit outside <main> and never had .section, so they carried their
   own hand-set padding (72 / 88) and produced the page's two odd boundaries — 184 and
   160 against 224 everywhere else. They now take --space-2xl like every section. */
.isi { background: var(--surface-sand); padding: var(--space-2xl) var(--gutter); }
/* Bento'd: each drug's safety copy in its own card. White-on-sand keeps the cards in
   the page's one container recipe while the band still reads as a distinct zone. */
.isi__cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-s); margin: var(--space-s) 0; }
.isi__cols > div {
  background: var(--surface-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-card);
  padding: var(--space-m);
}
@media (max-width: 860px) { .isi__cols { grid-template-columns: 1fr; } }

.footer { background: var(--surface-ink); color: var(--text-on-ink); padding: var(--space-2xl) var(--gutter) var(--space-xl); }
.footer__top { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--space-l); }
/* fill goes on the ELEMENT, not on a descendant selector. <use> clones the symbol into
   a shadow tree that descendant selectors cannot cross, so `.footer__logo path` matched
   zero nodes and the whole lockup rendered default black — invisible on the ink footer.
   `fill` is an inherited SVG property, so setting it here does reach the shadow content. */
.footer__logo { height: 34px; width: auto; margin-bottom: var(--space-s); fill: #fff; }
.footer .footer-h {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: var(--t-mono-micro); font-weight: 400; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-on-deep-soft); margin: 0 0 var(--space-xs);
}
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-xs); }
.footer a:hover { color: #fff; }
.footer__legal {
  display: flex; flex-wrap: wrap; gap: var(--space-xs) var(--space-m); align-items: center;
  border-top: 1px solid #2a2a30; margin-top: var(--space-xl); padding-top: var(--space-m);
}
.footer__legal p { margin: 0; flex: 1 1 420px; }
@media (max-width: 900px) { .footer__top { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .footer__top { grid-template-columns: 1fr; } }
