/* ==========================================================================
   Berkeley Life — Blue Pill landing page
   Brand tokens pulled from berkeleylife.com (Shopify theme + Typekit kit):
   type    tt-commons-pro (headings/body), cormorant-garamond (serif accent)
   color   #ffffff page, #f6f4f0 header band, #080b2b ink, #d54f2f brand,
           #e2e0d8 hairline
   ========================================================================== */

:root {
  --bg: #ffffff;
  --header-bg: #f6f4f0;
  --ink: #080b2b;
  --muted: rgba(8, 11, 43, 0.62);
  --brand: #d54f2f;
  --brand-deep: #b8432a;
  --hairline: #e2e0d8;
  --pill-blue: #2f3c96;

  --sans: "tt-commons-pro", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  --serif: "cormorant-garamond", "Cormorant Garamond", Georgia, serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* Vertical slideshow: native scroll with each full-screen slide
   snapping into place. No scroll-jacking.

   The scroller is .deck, a fixed-height inner container — NOT the
   document (v2, Jul 9). Document scroll let mobile Safari collapse
   its URL bar mid-swipe, growing the visual viewport past 100svh:
   every svh-sized slide then sat short of the screen and the next
   slide bled in at the foot, and the height change re-snapped the
   last slide late ("snaps and extends"). With the document pinned
   and only the deck scrolling, the browser chrome never collapses,
   so the viewport — and every snap point — holds still on any
   phone. */
html, body {
  height: 100%;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

.deck {
  height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  scroll-behavior: smooth;
  scroll-snap-type: y mandatory;
}

@media (prefers-reduced-motion: reduce) {
  .deck { scroll-behavior: auto; }
}

/* --------------------------------------------------------------------------
   Fold: one full-viewport slide (slide 1 additionally carries the
   site header at its top).
   -------------------------------------------------------------------------- */

.fold {
  /* 100% of the deck — the stable, chrome-independent viewport. */
  min-height: 100%;
  /* Each fold clips its own horizontal overflow (the old body-level
     overflow-x: clip, relocated). The deck can't carry it — clip on
     one axis of a scroller computes to hidden, and hidden still
     allows PROGRAMMATIC x-scroll (a hash jump or a Tab focus could
     shove the whole page sideways). With every fold clipped, the
     deck simply never has horizontal overflow to scroll to. */
  overflow-x: clip;
  display: flex;
  flex-direction: column;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  /* Positioning context for the keep-scrolling cue at each slide's foot. */
  position: relative;
}

/* The page's one dark slide: the problem lives in shadow between
   two bright rooms. */
.fold--dark {
  background: var(--ink);
  color: var(--header-bg);
}

.fold--dark .hero__lede,
.fold--dark .hero__sub {
  color: rgba(246, 244, 240, 0.62);
}

/* No orange type in the dark room (Ewan, Jul 8) — brand orange is
   muddy on ink and the lifted tone read as off-brand. The accent
   word keeps only its serif italic voice, in the room's cream. */
.fold--dark .hl-accent {
  color: var(--header-bg);
}

/* --------------------------------------------------------------------------
   Slide 2 choreography: "the bond". app.js flips .is-visible on the
   fold itself once the slide has mostly settled (a per-element trigger
   would start the beats mid-snap, half off screen). One clock, beats
   as transition delays:
     0s     headline words rise in one after another, echoing slide 1's
            opening word-rise instead of landing as one block;
     0.45s  the atoms drift in from opposite sides and dock, over-
            shooting into the overlap and settling back: a bond forming;
     1.15s  the N and O surface on the docked atoms;
     1.35s  the tick draws down, the caption names the molecule;
     1.8s   the lede and the Nobel line land together, one beat; the
            medal + citation get the headline's blur-rise, the lede
            itself stays a plain fade-rise (see note below).
   Reduced motion: everything below sits inside the no-preference query,
   so the slide is simply visible in its settled state. */
@media (prefers-reduced-motion: no-preference) {
  #the-molecule .hero__headline .w {
    display: inline-block;
    opacity: 0;
    transform: translateY(0.3em);
    filter: blur(4px);
    transition: opacity 0.45s ease, transform 0.45s ease, filter 0.45s ease;
  }

  #the-molecule .hero__headline .w.is-on {
    opacity: 1;
    transform: none;
    filter: none;
  }

  #the-molecule .atom {
    opacity: 0;
    transition:
      transform 0.8s cubic-bezier(0.34, 1.45, 0.5, 1) 0.45s,
      opacity 0.4s ease 0.45s;
  }
  #the-molecule .atom--n { transform: translateX(-38px); }
  #the-molecule .atom--o { transform: translateX(38px); }

  #the-molecule .mol-symbol {
    opacity: 0;
    transition: opacity 0.4s ease 1.15s;
  }

  #the-molecule .molecule__note::before {
    transform: scaleY(0);
    transform-origin: top;
    transition: transform 0.35s ease 1.35s;
  }

  #the-molecule .molecule__note span {
    display: inline-block;
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 0.5s ease 1.5s, transform 0.5s ease 1.5s;
  }

  /* Lede and Nobel line land together, one beat. The lede stays a
     plain fade-rise: it's a multi-line, text-wrap:pretty paragraph,
     and animating filter:blur() on it fights the browser's line-
     breaking pass, visibly re-wrapping a line right as the blur
     clears. The single-line medal + citation have no line to
     renegotiate, so they keep the fuller blur-rise, matching the
     headline's word-in. */
  #the-molecule .hero__lede,
  #the-molecule .nobel-lockup {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.55s ease 1.8s, transform 0.55s ease 1.8s;
  }

  #the-molecule .nobel-lockup {
    filter: blur(4px);
    transition: opacity 0.55s ease 1.8s, transform 0.55s ease 1.8s, filter 0.55s ease 1.8s;
  }

  #the-molecule .mol-shadow {
    opacity: 0;
    transition: opacity 0.5s ease 0.9s;
  }

  #the-molecule.is-visible .atom,
  #the-molecule.is-visible .mol-symbol,
  #the-molecule.is-visible .mol-shadow,
  #the-molecule.is-visible .molecule__note::before,
  #the-molecule.is-visible .molecule__note span,
  #the-molecule.is-visible .hero__action > * {
    opacity: 1;
    transform: none;
    filter: none;
  }

  /* Off screen, app.js resets the slide through this class so a quick
     return never catches the beats mid-fade. */
  #the-molecule.is-resetting *,
  #the-molecule.is-resetting *::before {
    transition: none !important;
  }
}

/* --------------------------------------------------------------------------
   Slide 3 choreography: "the demonstration". The poster assembles:
   copy accumulates with the scene's events and nothing informative
   ever dissolves, so the resting slide keeps its whole argument.
   Walls, cells, molecules and signal rings all move on app.js's one
   rAF clock (one motion language — see drawFlight); CSS keeps only
   the copy beats:
     0s     headline words rise in one after another, like slides 1
            and 2, over the constricted vessel and its trickle;
     0.65s  (app.js) two NO pairs ride in with the blood, mid-stream
            among the cells, and settle onto the inner walls of the
            constriction — NO works from inside the vessel, carried
            in the blood — each marking delivery with a quiet ring;
     1.8s   is-releasing: walls ease open, flow surges, the pairs ride
            the widening walls out and are carried off downstream —
            their work done; the lede follows (+0.45s), one paragraph
            in slide 2's voice.
   The beat class accumulates (not removed until reset). */
@media (prefers-reduced-motion: no-preference) {
  #the-vessel .vessel-scene__headline .w {
    display: inline-block;
    opacity: 0;
    transform: translateY(0.3em);
    filter: blur(4px);
    transition: opacity 0.45s ease, transform 0.45s ease, filter 0.45s ease;
  }

  #the-vessel .vessel-scene__headline .w.is-on {
    opacity: 1;
    transform: none;
    filter: none;
  }

  #the-vessel .vessel__tag {
    opacity: 0;
    transition: opacity 0.5s ease 0.3s;
  }

  #the-vessel.is-visible .vessel__tag {
    opacity: 1;
  }

  #the-vessel .hero__lede {
    opacity: 0;
    transform: translateY(16px);
  }

  #the-vessel.is-releasing .hero__lede {
    opacity: 1;
    transform: none;
    transition: opacity 0.6s ease 0.45s, transform 0.6s ease 0.45s;
  }

  #the-vessel.is-resetting *,
  #the-vessel.is-resetting *::before {
    transition: none !important;
  }
}

/* --------------------------------------------------------------------------
   Slide 4 choreography: "the supply" (v5, the lifespan chart). Same
   conductor as slides 2-3 (app.js flips .is-visible once the slide
   settles); the two data series and the leading marker are drawn
   per-frame by app.js, so the only CSS beats here are the headline
   word-rise (like slides 1-3), the chart's entrance, and the copy,
   keyed to the is-pill class the clock adds when the pill's spikes
   begin — the words land on a scene that has already shown them.
     0s      headline words rise in one after another;
     0.2s    the chart surfaces;
     0.4s    the draw starts: age sweeps 20 -> 70, the cream supply
             line falls 100 -> ~25;
     ~mid    is-pill: from age 40 the blue spikes appear and fall back
             to the line, shrinking; the lede follows. */
@media (prefers-reduced-motion: no-preference) {
  #the-supply .hero__headline .w {
    display: inline-block;
    opacity: 0;
    transform: translateY(0.3em);
    filter: blur(4px);
    transition: opacity 0.45s ease, transform 0.45s ease, filter 0.45s ease;
  }

  #the-supply .hero__headline .w.is-on {
    opacity: 1;
    transform: none;
    filter: none;
  }

  /* The stat line rises with the chart it summarises. */
  #the-supply .hero__sub,
  #the-supply .chart-hero {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.55s ease 0.2s, transform 0.55s ease 0.2s;
  }

  #the-supply .hero__action > * {
    opacity: 0;
    transform: translateY(18px);
  }

  /* The lede waits for the pill's spikes to enter — it lands on the
     "it runs on what's left" picture already on screen. */
  #the-supply.is-pill .hero__lede {
    opacity: 1;
    transform: none;
    transition: opacity 0.55s ease 0.3s, transform 0.55s ease 0.3s;
  }

  #the-supply.is-visible .hero__sub,
  #the-supply.is-visible .chart-hero {
    opacity: 1;
    transform: none;
  }

  #the-supply.is-resetting *,
  #the-supply.is-resetting *::before {
    transition: none !important;
  }
}

/* --------------------------------------------------------------------------
   Slide 5 choreography: "the source". Same conductor as slides 2-3.
   The molecule transforms in place on CSS transitions; app.js adds the
   step classes (nitrate -> nitrite -> nitric oxide) and, at the last
   step, is-made (which blooms the halo and lands the lede). Base rules
   OUTSIDE this query hold the finished NO pair (the no-JS / reduced-
   motion poster); the no-preference query rewinds the atoms to nitrate
   and gives them their transitions, then the step classes drive them
   home.
     0s      headline words rise in one after another;
     0s      the figure surfaces as a nitrate molecule;
     0.8s    step-nitrite: one oxygen peels away and fades;
     1.7s    step-no + is-made: the second oxygen peels, the N and O
             settle into the pair, the halo blooms, the lede lands. */
@media (prefers-reduced-motion: no-preference) {
  #the-source .hero__headline .w {
    display: inline-block;
    opacity: 0;
    transform: translateY(0.3em);
    filter: blur(4px);
    transition: opacity 0.45s ease, transform 0.45s ease, filter 0.45s ease;
  }

  #the-source .hero__headline .w.is-on {
    opacity: 1;
    transform: none;
    filter: none;
  }

  #the-source .conv {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.55s ease, transform 0.55s ease;
  }

  #the-source.is-visible .conv {
    opacity: 1;
    transform: none;
  }

  /* Rewind to the nitrate molecule: three oxygens around one nitrogen,
     the nitrogen centered. The transitions here are what the step
     classes below animate along. */
  #the-source .conv__n {
    transform: translate(120px, 96px);
    transition: transform 0.7s cubic-bezier(0.34, 1.2, 0.5, 1);
  }
  #the-source .conv__o {
    transition: transform 0.7s cubic-bezier(0.34, 1.2, 0.5, 1), opacity 0.4s ease;
  }
  #the-source .conv__o1 { transform: translate(120px, 62px); opacity: 1; }
  #the-source .conv__o2 { transform: translate(149px, 113px); }
  #the-source .conv__o3 { transform: translate(90px, 113px); opacity: 1; }

  #the-source .conv__glow { opacity: 0; transition: opacity 0.7s ease; }

  #the-source .conv__label {
    opacity: 0;
    transition: opacity 0.4s ease;
  }
  #the-source.is-visible .conv__label { opacity: 1; }

  #the-source .hero__lede {
    opacity: 0;
    transform: translateY(18px);
  }

  /* step-nitrite: the top oxygen lifts off and fades. */
  #the-source.step-nitrite .conv__o1 {
    transform: translate(120px, 6px);
    opacity: 0;
  }

  /* step-no: the second oxygen drifts off, the survivors close into
     the familiar N-left / O-right pair. */
  #the-source.step-no .conv__n  { transform: translate(104px, 96px); }
  #the-source.step-no .conv__o2 { transform: translate(148px, 96px); }
  #the-source.step-no .conv__o3 {
    transform: translate(64px, 170px);
    opacity: 0;
  }

  /* The halo rides step-no (not is-made) so it blooms and rewinds with
     every pass of the loop; is-made persists across passes and only
     holds the lede. */
  #the-source.step-no .conv__glow { opacity: 1; }

  /* is-made: the supply comes back — the lede lands, and stays put
     while the conversion loops behind it. */
  #the-source.is-made .hero__lede {
    opacity: 1;
    transform: none;
    transition: opacity 0.55s ease 0.2s, transform 0.55s ease 0.2s;
  }

  /* The loop's seam. step-exit: the finished pair drifts up and off,
     sent into circulation. step-enter then holds the silently rewound
     nitrate just below the stage (declared after is-visible, so it
     wins) so the next pass rides the same entry transition as the
     first. */
  #the-source.step-exit .conv {
    opacity: 0;
    transform: translateY(-14px);
  }
  #the-source.step-enter .conv {
    opacity: 0;
    transform: translateY(18px);
  }

  #the-source.is-resetting *,
  #the-source.is-resetting *::before {
    transition: none !important;
  }
}

/* --------------------------------------------------------------------------
   Slide 6 choreography: "the reveal". Same conductor as slide 2 — one
   .is-visible class from app.js, beats as transition delays. The cut
   from the dark room is itself the first beat (the room floods with
   light); everything here just assembles the poster:
     0s     headline words rise in one after another, like slides 1-4;
     0.3s   the bottle rises in and settles — the museum-object entrance
            slide 2's molecule made, now for the product — and the stamp
            ring surfaces WITH it, already turning;
     1.25s  the lede lands;
     1.65s  the review ticker rises in under the bottle, and its
            marquee drift is already running — the strip arrives
            mid-motion, like a band of the page that was always
            turning. The ring STAYS this time (Ewan, Jul 13: with
            nothing tucked behind the bottle anymore it's no longer
            clutter half-hidden behind widgets — the spinning stamp
            is part of the resting poster again).
   (A caption under the bottle — "This is Nitric Oxide Support." in
   slide 2's tick idiom — shipped in the first cut; Ewan cut the echo.
   The label names the product itself.) */
@media (prefers-reduced-motion: no-preference) {
  #the-capsule .hero__headline .w {
    display: inline-block;
    opacity: 0;
    transform: translateY(0.3em);
    filter: blur(4px);
    transition: opacity 0.45s ease, transform 0.45s ease, filter 0.45s ease;
  }

  #the-capsule .hero__headline .w.is-on {
    opacity: 1;
    transform: none;
    filter: none;
  }

  #the-capsule .reveal__bottle {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 0.7s ease 0.3s, transform 0.7s ease 0.3s;
  }

  /* Surfaces with the bottle and stays — only opacity animates (the
     element's transform is the centering translate, never touch it;
     the turn lives on the inner group). */
  #the-capsule .reveal__ring {
    opacity: 0;
    transition: opacity 0.7s ease 0.4s;
  }

  /* The award takes the old lede beat — with the "Each capsule..."
     line cut (Jul 9), this is the slide's only copy below the
     bottle: the award landing under the object it credentials.
     (Jul 13: the award is now FrontrowMD's live badge iframe; the
     beat rides its wrapper, whose reserved height keeps the rise
     steady even though the badge's own paint is network-timed.) */
  #the-capsule .frontrow-award {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 0.55s ease 1.25s, transform 0.55s ease 1.25s;
  }

  /* The receipts band follows the award, one beat later — the same
     rise, woven in just before the review pill slides out. */
  #the-capsule .stats {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 0.55s ease 1.45s, transform 0.55s ease 1.45s;
  }

  /* The review ticker rises in as the last beat, on the same quiet
     fade-rise grammar as the award and stats above it — a band of
     the page arriving, not a widget popping. Its marquee drift runs
     from load (CSS animation, no visibility gate): arriving
     mid-motion is the point, and off screen it costs nothing that
     matters. */
  #the-capsule .reveal__strip {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 0.55s ease 1.65s, transform 0.55s ease 1.65s;
  }

  #the-capsule.is-visible .reveal__bottle,
  #the-capsule.is-visible .frontrow-award,
  #the-capsule.is-visible .stats,
  #the-capsule.is-visible .reveal__strip {
    opacity: 1;
    transform: none;
  }

  /* Opacity ONLY — transform: none here would wipe the ring's
     centering translate. */
  #the-capsule.is-visible .reveal__ring {
    opacity: 1;
  }

  #the-capsule .reveal__ring-spin {
    transform-box: view-box;
    transform-origin: center;
    animation: ring-turn 75s linear infinite;
  }

  #the-capsule.is-resetting *,
  #the-capsule.is-resetting *::before {
    transition: none !important;
  }
}

@keyframes ring-turn {
  to { transform: rotate(360deg); }
}

.site-header {
  background: var(--header-bg);
  border-bottom: 1px solid var(--hairline);
  text-align: center;
  padding: 18px 24px 14px;
}

.site-header__logo {
  display: block;
  margin: 0 auto;
  width: min(230px, 64vw);
  height: auto;
}

/* Clinicians' Choice (slide 6, directly under the bottle): now
   FrontrowMD's LIVE badge iframe — they require their real clickable
   widget wherever the award appears (Ewan, Jul 13). The wrapper is
   the piece we own: it takes the award's choreography beat, centers
   the badge, and RESERVES the badge's settled height (113px at every
   phone width tested) so the iframe's async resize — height 0 until
   their script gets the RESIZE_HEIGHT postMessage — never reflows
   the fold mid-choreography. Width capped near the badge content's
   natural measure: the iframe fills its box but lays its content out
   left-aligned, so a wider box would read as off-center whitespace.
   .iframe-frontrow (class name is load-bearing — their script finds
   the badge by it, see index.html) starts at height 0; their script
   sets the real height inline. */
.frontrow-award {
  position: relative;
  display: flex;
  justify-content: center;
  width: 100%;
  max-width: 380px;
  min-height: 113px;
}

.iframe-frontrow {
  width: 100%;
  height: 0;
  margin: 0;
  border: 0;
  display: block;
}

/* The laurel lockup the badge replaced (our redrawing of the same
   award, retired Jul 13 to snippets/laurel.html — reference copy). */
.laurel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.laurel__title {
  display: flex;
  align-items: center;
  gap: 9px;
  font-family: var(--serif);
  font-weight: 600;
  font-size: 1.2rem;
  line-height: 1;
  letter-spacing: 0.02em;
}

.laurel__branch {
  height: 24px;
  width: auto;
  fill: var(--ink);
  color: var(--ink); /* the stem stroke rides currentColor */
}

.laurel__branch--r {
  transform: scaleX(-1);
}

.laurel__source {
  font-size: 0.68rem;
  line-height: 1.45;
  letter-spacing: 0.01em;
  color: var(--muted);
  max-width: 34ch;
  text-wrap: balance;
}

/* The receipts band (slide 6): the brand's four credibility numbers
   in one quiet strip — the stats-band idiom of berkeleylife.com
   itself, in the trust row's legit-commerce register. Value in ink,
   label in the uppercase specimen voice, hairline dividers. Four
   across on every screen: the type scales with the viewport (same
   min() pattern as the trust row) and the labels are written short
   enough to live in a quarter of a phone at two lines.

   Pinned to the fold's FOOT (Ewan, Jul 9): in flow after the laurel,
   space-evenly parked the page's leftover height in a dead strip
   between the band and the CTA bar; anchored here the band closes
   ranks with the bar (12px of clearance) and the leftover gathers
   above the numbers instead, between award and band. The fold's
   padding-bottom reserves this footprint, so flow content can't run
   into it. Positioned against the fold (the nearest positioned
   ancestor), not the hero: left/right mirror the hero's 24px side
   padding, margin:auto re-centers once the 480px cap bites. */
.stats {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  position: absolute;
  left: 24px;
  right: 24px;
  bottom: calc(124px + env(safe-area-inset-bottom));
  max-width: 480px;
  margin: 0 auto;
}

.stats__item {
  padding: 2px 6px;
}

.stats__item + .stats__item {
  border-left: 1px solid var(--hairline);
}

.stats__value {
  font-size: min(1.15rem, 4.6vw);
  font-weight: 600;
  line-height: 1.1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.stats__label {
  margin-top: 3px;
  font-size: min(0.55rem, 2.3vw);
  font-weight: 600;
  letter-spacing: 0.1em;
  line-height: 1.4;
  color: var(--muted);
}

/* hero ---------------------------------------------------------------------- */

/* Three blocks (headline, molecule, copy + action) share the leftover
   viewport height evenly, so the screen composes itself to any phone. */
.hero {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-evenly;
  text-align: center;
  gap: 20px;
  padding: clamp(20px, 3svh, 40px) 24px clamp(24px, 3.5svh, 48px);
  width: 100%;
  max-width: 680px;
  margin: 0 auto;
}

.hero__action {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(18px, 2.6svh, 26px);
  width: 100%;
}

.kicker {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--brand);
}

.hero__headline {
  font-family: var(--sans);
  font-weight: 600;
  font-size: clamp(2.1rem, 5.6vw, 3.4rem);
  line-height: 1.08;
  letter-spacing: -0.015em;
  text-wrap: balance;
  max-width: 17ch;
}

/* Brand signature: one serif italic word inside the bold sans headline. */
.hl-accent {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 600;
  font-size: 1.06em;
  color: var(--brand);
}

/* Same signature in Viagra blue, for the opening hook. */
.hl-pill {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 600;
  font-size: 1.06em;
  color: var(--pill-blue);
}

/* The quiet cousin, for one emphasized word inside a lede. Inherits
   the lede's muted color; no dotted underline — that styling means
   "tappable" on this page. */
.lede-accent {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 600;
  font-size: 1.06em;
}

/* Headline + lede travel together at the top of slide 1. */
.hero__intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

/* --- Slide 1 choreography (app.js stages these off the video clock) ---

   The claim, lede, and scroll cue all start hidden and rise in as the
   morph resolves; the hook types itself over the video, then dissolves
   with the pill. */

/* Hook and claim share one slot; the hook types in first, dissolves
   as the pill deforms, and the claim takes its place. */
.headline-stack {
  display: grid;
  justify-items: center;
}

.headline-stack > * {
  grid-area: 1 / 1;
  align-self: center;
}

.morph-hook {
  max-width: none;
  font-size: clamp(1.7rem, 6.9vw, 2.7rem);
  transition: opacity 0.6s ease, transform 0.6s ease, filter 0.6s ease;
}

.morph-hook.is-out {
  opacity: 0;
  transform: translateY(-12px);
  filter: blur(5px);
}

/* Hook and lede words rise in one at a time (app.js staggers .is-on). */
.morph-hook .w,
.hero__lede .w {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.3em);
  filter: blur(4px);
  transition: opacity 0.45s ease, transform 0.45s ease, filter 0.45s ease;
}

.morph-hook .w.is-on,
.hero__lede .w.is-on {
  opacity: 1;
  transform: none;
  filter: none;
}

@media (prefers-reduced-motion: reduce) {
  .morph-hook .w,
  .hero__lede .w { transition: none; }
}

.headline-claim {
  opacity: 0;
  transform: translateY(16px);
  filter: blur(6px);
  transition: opacity 0.7s ease, transform 0.7s ease, filter 0.7s ease;
}

.headline-claim.is-in {
  opacity: 1;
  transform: none;
  filter: none;
}

#the-better-way .scrollcue {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.6s ease 0.15s, transform 0.6s ease 0.15s;
}

#the-better-way .scrollcue.is-in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .headline-claim,
  #the-better-way .scrollcue,
  .morph-hook {
    transition: none;
  }
}

/* molecule ------------------------------------------------------------------- */

.molecule {
  width: min(70vw, 290px);
}

.molecule svg {
  display: block;
  width: 100%;
  height: auto;
}

.mol-symbol {
  font-family: var(--sans);
  font-size: 30px;
  font-weight: 600;
  fill: #fff;
  text-anchor: middle;
  dominant-baseline: central;
}

/* Figure annotation: hairline tick pointing at the model, serif label. */
.molecule__note {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  margin-top: 8px;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 500;
  font-size: 1.3rem;
  color: var(--muted);
}

.molecule__note::before {
  content: "";
  width: 1px;
  height: 18px;
  background: rgba(8, 11, 43, 0.28);
}

/* vessel (slide 3, the demonstration) -------------------------------------------

   Full bleed: the vessel runs edge to edge with no side margins,
   a floating object in white space like slide 2's molecule, its cut
   ends feathered away by the same mask idiom as slide 1's video.
   Walls, interior fill, and cells are drawn by app.js from one
   shared half-gap function; here is only how they look: a medical
   render, not a cartoon — cylinder-shaded rose interior (deeper at
   the walls, bright at the core), a thin crisp wall line riding the
   gradient's dark edge, a soft light sheen drifting along the blood,
   brand orange cells. The molecule here speaks a cleaner 2D voice
   than slide 2's hero render — flat white-ringed circles, subtle
   letters — because at this size the 3D gradients turn to mud. */

.vessel-scene {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-evenly;
  text-align: center;
  gap: 16px;
  width: 100%;
  padding: clamp(20px, 3svh, 40px) 24px clamp(24px, 3.5svh, 48px);
}

/* One paragraph in slide 2's voice; the wink is one quiet serif
   word inside it, not a bold line. */
.vessel__copy {
  width: 100%;
  max-width: 680px;
  margin: 0 auto;
  display: flex;
  justify-content: center;
}

.vessel {
  width: min(88vw, 540px);
  /* The specimen treatment: the vessel's cut ends dissolve into the
     page, same idiom as the morph video's feathered frame. */
  -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);
}

.vessel svg {
  display: block;
  width: 100%;
  height: clamp(130px, 24svh, 230px);
}

/* A thin line riding the gradient's already-darkened edge, so wall
   thickness comes from shading with a crisp finish — not a heavy
   cartoon outline. */
.vessel__wall {
  fill: none;
  stroke: var(--brand-deep);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.vessel__cell {
  fill: var(--brand);
  opacity: 0.9;
}

/* Anatomical callout: dot on the wall, diagonal hairline leader,
   uppercase specimen label — journal figure furniture, deliberately
   not slide 2's serif caption. */
.vessel__tag-dot {
  fill: rgba(8, 11, 43, 0.4);
}

.vessel__tag-line {
  stroke: rgba(8, 11, 43, 0.35);
  stroke-width: 1;
}

.vessel__tag-text {
  font-family: var(--sans);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  fill: rgba(8, 11, 43, 0.5);
}

/* Flat 2D molecule for this scene: N in the slide-2 navy, O in
   brand orange, letters quiet. The white ring lifts the pair off
   the red wall when it docks. */
.no-mol__n,
.no-mol__o {
  stroke: #fff;
  stroke-width: 1.5;
}
.no-mol__n { fill: #1c2356; }
.no-mol__o { fill: var(--brand); }

.no-mol__symbol {
  font-family: var(--sans);
  font-size: 9px;
  font-weight: 600;
  fill: #fff;
  opacity: 0.85;
  text-anchor: middle;
  dominant-baseline: central;
}

/* The pairs are flown by app.js on its rAF clock (inline styles);
   parked invisible until their flight begins, and invisible in the
   no-JS and reduced-motion states — their work is already done. */
.no-pair {
  opacity: 0;
}

/* The signal: a quiet ring in the wall's own color, expanding from
   the pair into the wall it has just told to relax; app.js spawns,
   grows and fades one per delivery. */
.vessel__pulse {
  fill: none;
  stroke: var(--brand-deep);
  stroke-width: 2;
}

/* supply (slide 4, the first dark room) — the lifespan chart (v5) ---------------

   Two series on one figure: a cream line, your Nitric Oxide supply,
   falling across the lifespan, and — from age 40 — the blue pill's
   brief spikes of blood flow that rise off it and fall straight back,
   shrinking as the line drops. Cream is the supply, blue is the pill's
   costume (as on slide 1); no orange TYPE in this room (hl-accent is
   cream). app.js draws both series and the leading marker per frame;
   the rules here are only how they look. The data paths are empty
   until app.js (or its reduced-motion branch) fills them. */

.chart-hero {
  width: 100%;
  display: flex;
  justify-content: center;
}

/* The chart is the slide: sized by width so it stays the dominant
   central object on any phone, height follows the 380:300 viewBox. */
.no-chart {
  display: block;
  width: min(92vw, 460px);
  height: auto;
  overflow: visible;
}

.chart__grid line {
  stroke: rgba(246, 244, 240, 0.12);
  stroke-width: 1;
}

/* The age axis sits a touch brighter than the reference grid. */
.chart__base {
  stroke: rgba(246, 244, 240, 0.26);
  stroke-width: 1;
}

/* Faint dashed marker where the pill enters (age 40). */
.chart__pill-mark {
  stroke: rgba(246, 244, 240, 0.16);
  stroke-width: 1;
  stroke-dasharray: 3 4;
}

.chart__ylab {
  font-family: var(--sans);
  font-size: 8px;
  font-weight: 600;
  letter-spacing: 0.02em;
  fill: rgba(246, 244, 240, 0.42);
  text-anchor: end;
}

.chart__xlab {
  font-family: var(--sans);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.06em;
  fill: rgba(246, 244, 240, 0.5);
  text-anchor: middle;
  font-variant-numeric: tabular-nums;
}

/* Axis titles: quieter than the tick labels themselves, so they read
   as furniture rather than new emphasis. */
.chart__axis-title {
  font-family: var(--sans);
  font-size: 7.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  fill: rgba(246, 244, 240, 0.34);
  text-anchor: middle;
}

/* The supply: a line over a faint cream wash, graded from brand orange
   at 20 (peak) to cream by the older end (what's left, worn pale). */
.chart__cream-line {
  fill: none;
  stroke: url(#chartLineGrad);
  stroke-width: 3;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.chart__cream-area {
  fill: rgba(246, 244, 240, 0.1);
}

/* The pill: blue spikes over a translucent blue lobe — the temporary,
   local blood-flow bump the pill buys, riding on what's left. */
.chart__blue-line {
  fill: none;
  stroke: #6675e6;
  stroke-width: 2.5;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.chart__blue-area {
  fill: rgba(102, 117, 230, 0.3);
}

/* Glow behind the spike: a wide, blurred, brighter-blue stroke tracing
   the same path as .chart__blue-line, so each dose reads as a pulse of
   blood flow rather than a plotted data point. */
.chart__blue-glow {
  fill: none;
  stroke: #8b9bf5;
  stroke-width: 7;
  stroke-linejoin: round;
  stroke-linecap: round;
  opacity: 0.65;
  filter: url(#blueGlow);
}

/* The leading point of the falling supply line. */
.chart__marker {
  fill: var(--header-bg);
}

/* Legend: a swatch and an uppercase label per series. */
.chart__leg-cream {
  stroke: var(--header-bg);
  stroke-width: 3;
  stroke-linecap: round;
}

.chart__leg-blue {
  stroke: #6675e6;
  stroke-width: 3;
  stroke-linecap: round;
}

.chart__leg-text {
  font-family: var(--sans);
  font-size: 8px;
  font-weight: 600;
  letter-spacing: 0.08em;
  fill: rgba(246, 244, 240, 0.62);
  text-anchor: start;
}

/* source (slide 5, the second dark room) — the conversion ----------------------

   The nitrate-nitrite-NO pathway as one molecule transforming in
   place: a nitrogen ringed by three oxygens sheds two of them to
   become the Nitric Oxide pair from slides 2-3. Reused motif, so the
   navy N / orange O carry over (orange is the molecule's own colour,
   the one exception to the room's no-orange-TYPE rule). The atoms are
   positioned by CSS transform so the conversion can animate; the rules
   here are the FINISHED state (the no-JS / reduced-motion poster), and
   the choreography query above rewinds them to nitrate. */

.conv {
  width: min(70vw, 300px);
  display: flex;
  justify-content: center;
}

.conv__svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
}

/* White ring lifts each sphere off the ink, same trick as slide 3's
   molecules. */
.conv__n circle,
.conv__o circle {
  stroke: rgba(255, 255, 255, 0.5);
  stroke-width: 1.5;
}

/* Finished positions: N left, the surviving O right, the two peeled
   oxygens parked off and invisible. */
.conv__n  { transform: translate(104px, 96px); }
.conv__o2 { transform: translate(148px, 96px); }
.conv__o1 { transform: translate(120px, 6px); opacity: 0; }
.conv__o3 { transform: translate(64px, 170px); opacity: 0; }

.conv__sym {
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 600;
  fill: #fff;
  text-anchor: middle;
  dominant-baseline: central;
}

.conv__sym--n { font-size: 20px; }

/* Names the current molecule, in slide 3-4's uppercase specimen idiom
   (not slide 2's serif caption). app.js swaps the text at each beat. */
.conv__label {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  fill: rgba(246, 244, 240, 0.6);
  text-anchor: middle;
}

/* reveal (slide 6) --------------------------------------------------------------

   The product, staged like slide 2's molecule: one object in bright
   space. The bottle is the real 60ct render on transparent alpha (its
   soft contact shadow is baked into the PNG), sized by height so it
   stays the dominant central object on any phone. Behind it, the
   stamp ring — live SVG text on a circle, quiet ink, turning slowly —
   the demo page's treatment rebuilt so it stays crisp and editable.
   Base styles below are the settled poster (the no-JS and reduced-
   motion state); the choreography above only assembles it. */

/* The sticky CTA turns on the moment this fold arrives and stays, so
   the fold reserves the bar's footprint (button + proof row + padding)
   up front — without this the lede renders under the fixed bar on
   phones ("Day after day" vanished behind it, Jul 8). */
#the-capsule {
  /* The sticky bar's footprint (112px — sized for the one-line badges
     row; "Clinically backed" bought the width back after Made in USA
     briefly forced two lines) plus the receipts band now pinned in
     the fold's foot above it (~64px + 12px clearance, see .stats).
     Everything in flow ends above both. */
  padding-bottom: calc(188px + env(safe-area-inset-bottom));
}

/* Short screens: the fold's minimum content overran the viewport
   (~35px on a 667px SE with the old laurel; the live FrontrowMD
   badge's 113px then pushed a 740px Galaxy S8+ over too), and since
   the stats band is pinned to the fold's FOOT the overflow surfaces
   as the band's labels sinking under the sticky bar (Ewan, Jul 13 —
   the receipts must never be the thing that gets cut). Under 750px
   tall, reclaim ~85px from the pieces with the most give: the logo
   band's air and width, the hero's stack gaps and paddings, four
   viewport-points of bottle, and the foot footprint itself (stats
   ride 2px above the bar instead of 12, buying the flow 12px).
   Verified fits: 375x667 and 360x740. Scoped to this fold — the
   other slides fit these heights as they are. */
@media (max-height: 750px) {
  #the-capsule {
    padding-bottom: calc(176px + env(safe-area-inset-bottom));
  }

  #the-capsule .site-header {
    padding: 10px 24px 8px;
  }

  #the-capsule .site-header__logo {
    width: min(200px, 56vw);
  }

  #the-capsule .hero {
    gap: 10px;
    padding-top: 12px;
    padding-bottom: 14px;
  }

  /* The ticker is thin already; on short screens shave its last few
     pixels of air so the flex-sized bottle above keeps as much of
     the fold's leftover as it can. */
  #the-capsule .reveal__strip {
    margin-top: 10px;
    padding: 6px 0 7px;
  }

  #the-capsule .stats {
    bottom: calc(114px + env(safe-area-inset-bottom));
  }
}

/* v5 sizing: the strip under the bottle is new vertical spend, so
   the bottle stops carrying a fixed clamp() height and absorbs
   EXACTLY the fold's leftover instead — the figure takes the hero's
   free space (flex: 1), the stage takes the figure's (the strip
   keeps its content height), and the bottle fills the stage. Every
   device gets the biggest bottle its screen can afford, and the
   stats band can never be the thing that overflows. Capped at 420px
   (the old clamp ceiling) so desktop keeps proportion. */
.reveal {
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: stretch;
  flex: 1 1 auto;
  min-height: 0;
}

.reveal__stage {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  align-self: stretch;
  flex: 1 1 0;
  min-height: 0;
  max-height: 420px;
}

.reveal__bottle {
  /* Absolutely positioned, NOT in-flow with height: 100%: the stage's
     height comes from flex inside a min-height fold, which the spec
     calls indefinite — older iOS Safari therefore ignores a percentage
     height on an in-flow img and renders it at its intrinsic 536x1000,
     swallowing the whole phone screen. Percentages on an absolutely
     positioned box resolve against the stage's USED height in every
     engine, so the bottle can never escape the stage. inset + margin
     auto centers it (no transform — the entrance animation owns that). */
  position: absolute;
  inset: 0;
  margin: auto;
  z-index: 1;
  display: block;
  height: 100%;
  width: auto;
}

/* The ring rides behind the bottle, sized off the bottle's height so
   the two scale together. Deliberately SMALLER than the bottle (like
   the demo reference): the circle's arcs surface well inside the
   bottle's edges and duck behind it, a watermark under the object —
   not a halo around it. Ink kept very light for the same reason.
   The phrase repeats around the FULL circle (no dead arcs); the
   letter-spacing here is only the starting guess, app.js fits it to
   the circumference once the real font is in. */
.reveal__ring {
  position: absolute;
  top: 50%;
  left: 50%;
  height: 78%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  overflow: visible;
}

.reveal__ring-text {
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.26em;
  fill: rgba(8, 11, 43, 0.17);
}

/* The review ticker (v5, Jul 13): the slide's review proof as one
   thin full-bleed marquee band under the bottle, drifting sideways
   on its own, seamlessly (Ewan: "a thin strip that side scrolls...
   automatically"). Cream fill + hairlines = the header's and sticky
   bar's band, so the strip reads as page architecture — the fix for
   two earlier treatments: cards flanking the bottle read as sus
   popup toasts, and a centered card column cost ~150px of a fold
   that has none (see the sizing note above .reveal). Full bleed by
   brute width: the strip is a centered flex item 100vw wide, so it
   overflows the hero's 680px column equally on both sides and
   .fold's overflow-x: clip eats the excess; the strip itself clips
   the drifting track. */
.reveal__strip {
  width: 100vw;
  margin-top: 14px;
  padding: 8px 0 9px;
  background: var(--header-bg);
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  overflow: hidden;
}

/* The marquee: the track holds two identical runs and drifts left
   by exactly one run's width (the -50% is of the track, and the
   track is precisely two runs), then loops — seamless because run 2
   has moved into run 1's opening position at the wrap instant. Each
   run carries the inter-item gap as trailing padding so the seam
   keeps the rhythm. ~30px/s: readable drift, not a stock ticker. */
.reveal__strip-track {
  display: flex;
  width: max-content;
  animation: strip-drift 30s linear infinite;
}

/* Hover or keyboard focus holds the strip still: the bylines are
   buttons, and a moving tap target is a hostile one. */
.reveal__strip:hover .reveal__strip-track,
.reveal__strip:focus-within .reveal__strip-track {
  animation-play-state: paused;
}

.reveal__strip-run {
  display: flex;
  align-items: center;
  gap: 36px;
  padding-right: 36px;
}

@keyframes strip-drift {
  to { transform: translateX(-50%); }
}

/* Reduced motion: no auto-drift. The duplicate run disappears and
   the real one becomes a hand-swipeable row — every quote still
   reachable, nothing moves on its own. */
@media (prefers-reduced-motion: reduce) {
  .reveal__strip {
    overflow-x: auto;
  }

  .reveal__strip-track {
    animation: none;
  }

  .reveal__strip-run[aria-hidden] {
    display: none;
  }

  .reveal__strip-run {
    padding: 0 24px;
  }
}

.reveal__strip-item {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  white-space: nowrap;
}

.reveal__strip-stars {
  gap: 0.4em;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ink);
}

/* 4.7 of 5 stars, in review-trust green (the page's only green — a
   deliberate outsider to the brand palette, like every real ratings
   widget). Grey base row underneath keeps the unfilled 30% of the
   fifth star legible. */
.reveal__strip-starsvg {
  width: 3.9em;
  height: 0.75em;
  flex-shrink: 0;
}

.reveal__strip-starsvg-bg {
  fill: #ddd9d0;
}

.reveal__strip-starsvg-fill {
  fill: #3e7d51;
}

.reveal__strip-quote {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.3;
  color: var(--ink);
}

/* The byline: face, name, credential in one line. A real <button>:
   tap opens the full-quote peek over the stage. Name wears the
   dotted underline, the page's "more here, not a link" affordance
   (Nobel pop). */
.reveal__strip-byline {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 0;
  background: none;
  border: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.reveal__strip-byline:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Same rounded-square ID-badge mask as ever (Ewan, Jul 9: never a
   social-circle avatar); ~30% radius ratio at this size. Intrinsic
   160px, shown at 22 — crisp on 2-3x screens. */
.reveal__strip-avatar {
  display: block;
  width: 22px;
  height: 22px;
  border-radius: 7px;
  object-fit: cover;
}

.reveal__strip-name {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--ink);
  text-decoration: underline dotted rgba(8, 11, 43, 0.4);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  white-space: nowrap;
}

.reveal__strip-cred {
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}

/* The middot belongs to the credential so name + cred read as one
   phrase; decorative, so generated rather than marked up. */
.reveal__strip-cred::before {
  content: "\00B7\00A0";
}

/* Full-quote peeks: tap a byline and the doctor's whole quote pops
   centered over the stage, above the bottle. The Nobel peek's
   grammar — scroll, tap-out, Escape, X all dismiss; never a modal.
   Sans for the long quote (the serif italic voice stays on the
   small cards); big soft shadow because it floats over everything
   this slide owns. */
.doc-pop {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 3;
  width: min(78vw, 310px);
  padding: 18px;
  background: #fff;
  border: 1px solid var(--hairline);
  border-radius: 16px;
  box-shadow: 0 24px 60px rgba(8, 11, 43, 0.25);
  text-align: left;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -50%) translateY(8px);
  transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s;
}

.doc-pop.is-open {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, -50%);
}

.doc-pop__close {
  position: absolute;
  top: 10px;
  right: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  background: var(--header-bg);
  border: 1px solid var(--hairline);
  border-radius: 9px;
  color: var(--ink);
  cursor: pointer;
}

.doc-pop__close svg {
  width: 11px;
  height: 11px;
}

.doc-pop__close:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* Same rounded-square mask as the card avatars (same ~30% ratio at
   this size) so the peek reads as the card's own doctor, larger. */
.doc-pop__photo {
  display: block;
  width: 60px;
  height: 60px;
  border-radius: 18px;
  object-fit: cover;
}

.doc-pop__quote {
  margin-top: 12px;
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--ink);
}

.doc-pop__byline {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-top: 10px;
}

.doc-pop__name {
  font-size: 0.72rem;
  font-weight: 700;
}

.doc-pop__cred {
  font-size: 0.64rem;
  font-weight: 600;
  color: var(--muted);
}

/* lede + cta ------------------------------------------------------------------ */

/* text-wrap: pretty deliberately left off: the spec allows browsers to
   compute it as a deferred second pass after first paint, and this
   element fades/rises in right at that moment -- the mid-transition
   reflow to better break points reads as a stutter on whichever line
   shifts. Plain wrapping has no such delayed second pass. */
.hero__lede {
  font-size: 1.05rem;
  line-height: 1.55;
  color: var(--muted);
  max-width: 42ch;
}

/* Headline + its stat line as one block, tight against each other so
   the hero's space-evenly doesn't drift them apart. */
.hero__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.hero__sub {
  font-size: 1.05rem;
  line-height: 1.55;
  color: var(--muted);
  max-width: 42ch;
}

/* Copy blocks that rise in on a transform transition keep their
   compositor layer at rest. Without this, the layer the browser
   promotes for the animation is torn down the instant the transition
   ends, and the text repaints snapped to the device pixel grid; the
   block sits at a fractional y (space-evenly leftovers, 26.04px line
   boxes), so that repaint lands up to half a pixel away from the last
   animated frame and reads as a line of the paragraph snapping into
   place right as the rise settles. */
@media (prefers-reduced-motion: no-preference) {
  .hero__lede,
  .hero__sub,
  .headline-claim,
  .nobel-lockup {
    will-change: transform;
  }
}

/* "Nobel Prize in Medicine" is tappable: serif italic with a dotted
   underline (the "more here, not a link" affordance). The card pops
   above it with the verbatim citation and a plain-words line. */
.nobel-pop {
  position: relative;
}

.nobel-pop__trigger {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 600;
  font-size: 1.06em;
  color: var(--ink);
  cursor: pointer;
  text-decoration: underline dotted rgba(8, 11, 43, 0.4);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}

.nobel-pop__trigger:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* Photographic proof: the 1998 ceremony as a small footnote peek
   above the phrase. No scrim, no scroll lock; scrolling or tapping
   anywhere dismisses it, so it never interrupts the flow. */
.nobel-pop__card {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%) translateY(5px);
  width: min(58vw, 230px);
  padding: 6px;
  background: #fff;
  border: 1px solid var(--hairline);
  border-radius: 14px;
  box-shadow: 0 12px 32px rgba(8, 11, 43, 0.18);
  text-align: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
  z-index: 20;
}

.nobel-pop.is-open .nobel-pop__card {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%);
}

.nobel-pop__photo {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 9px;
}

/* Quiet frosted close tucked into the photo's corner; scroll and
   tap-out also dismiss, this is just the visible exit. */
.nobel-pop__close {
  position: absolute;
  top: 10px;
  right: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  border-radius: 7px;
  background: rgba(255, 255, 255, 0.6);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: rgba(8, 11, 43, 0.55);
  cursor: pointer;
}

.nobel-pop__close svg {
  width: 10px;
  height: 10px;
}

/* Invisible halo keeps the tap target generous despite the quiet look. */
.nobel-pop__close::after {
  content: "";
  position: absolute;
  inset: -9px;
}

.nobel-pop__close:active {
  background: rgba(255, 255, 255, 0.98);
}

.nobel-pop__close:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* Serif italic dateline, then plain legible sans for the facts. */
/* text-wrap: pretty left off, same reason as .hero__lede above: this
   card fades/scales in on open, and the deferred reflow pass would
   land mid-transition. */
.nobel-pop__caption {
  display: block;
  padding: 11px 10px 7px;
  font-size: 0.72rem;
  font-weight: 500;
  line-height: 1.55;
  letter-spacing: 0.01em;
  color: rgba(8, 11, 43, 0.52);
}

.nobel-pop__dateline {
  display: block;
  margin-bottom: 4px;
  font-family: var(--serif);
  font-style: italic;
  font-size: 0.88rem;
  color: rgba(8, 11, 43, 0.66);
}

@media (prefers-reduced-motion: reduce) {
  .nobel-pop__card,
  .nobel-pop__scrim { transition: none; }
}

/* The medal does the emphasizing; the type stays quiet. */
.nobel-lockup {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: -6px;
}

.nobel-lockup__medal {
  width: 36px;
  height: 36px;
}

.nobel__source {
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.05em;
  color: rgba(8, 11, 43, 0.45);
}

.cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: min(100%, 360px);
  min-height: 56px;
  background: var(--brand);
  color: #fff;
  border-radius: 999px;
  text-decoration: none;
  font-weight: 700;
  font-size: 0.92rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  transition: background 160ms ease;
}

.cta:hover { background: var(--brand-deep); }

.cta:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.cta__arrow { font-family: Arial, sans-serif; letter-spacing: 0; }

/* trust row --------------------------------------------------------------------

   Fixed two-line composition (Ewan): the first two badges share a
   line, pipe-separated; the third sits centered on its own line
   below. The pipe only lives between line-mates — a leading pipe on
   the third would read as a dangling separator. */

.trust {
  margin-top: clamp(10px, 2svh, 22px);
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 9px 0;
}

/* The type scales with the viewport (capped at the full size) and the
   icon/gaps/padding ride it in em, so the first line's pair fits ONE
   line on any phone instead of wrapping and breaking the composition. */
.trust__item {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  font-size: min(0.8rem, 3vw);
  font-weight: 600;
  color: var(--ink);
  padding: 0 0.9em;
}

.trust__item:nth-child(2) { border-left: 1px solid var(--hairline); }

.trust__item:nth-child(3) { flex-basis: 100%; }

.trust__icon {
  width: 1.2em;
  height: 1.2em;
  flex-shrink: 0;
}

/* scroll cue -------------------------------------------------------------------- */

.scrollcue {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  color: rgba(8, 11, 43, 0.35);
  margin-top: clamp(18px, 3.5svh, 40px);
}

.scrollcue__label {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(8, 11, 43, 0.45);
}

.scrollcue__chevron {
  display: block;
  width: 18px;
  height: auto;
  animation: cue-drift 2.4s ease-in-out infinite;
}

@keyframes cue-drift {
  0%, 100% { transform: translateY(0); opacity: 0.7; }
  50%      { transform: translateY(5px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .scrollcue__chevron { animation: none; }
}

/* keep-scrolling cue -----------------------------------------------------------

   Slide 1 gets the full labelled cue above; every middle slide (after
   slide 1, before the last) gets this text-less chevron instead, fading
   in at the slide's foot once its choreography has settled — a quiet
   "keep going" that matches the reel-swipe model. app.js injects one per
   eligible fold and flips .is-cue after a per-slide delay. currentColor
   so it reads ink on bright slides and cream on the dark ones. */

.keepcue {
  position: absolute;
  left: 50%;
  bottom: clamp(10px, 2.4svh, 24px);
  transform: translateX(-50%);
  color: rgba(8, 11, 43, 0.3);
  opacity: 0;
  pointer-events: none;
}

.fold--dark .keepcue { color: rgba(246, 244, 240, 0.42); }

.fold.is-cue .keepcue { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .keepcue { transition: opacity 0.7s ease; }
}

.keepcue__chevron {
  display: block;
  width: 20px;
  height: auto;
  animation: cue-drift 2.4s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .keepcue__chevron { animation: none; }
}

/* dot rail --------------------------------------------------------------------

   One dot per slide on the right edge: a passive map that says the
   page is short and the end is in sight. Hidden while slide 1 has the
   screen to itself (app.js toggles .is-on past slide 1). Ink only;
   brand orange stays reserved for accent words and the CTA. */

.dotrail {
  position: fixed;
  top: 50%;
  right: max(12px, env(safe-area-inset-right));
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Above slide content, below the lightbox (scrim 50 / card 60). */
  z-index: 4;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.6s ease;
}

.dotrail.is-on { opacity: 1; }

.dotrail__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(8, 11, 43, 0.16);
  transition: background 0.35s ease, transform 0.35s ease;
}

.dotrail__dot.is-active {
  background: var(--ink);
  transform: scale(1.3);
}

/* On the dark slide the rail flips to cream (app.js toggles). */
.dotrail--dark .dotrail__dot { background: rgba(246, 244, 240, 0.28); }
.dotrail--dark .dotrail__dot.is-active { background: var(--header-bg); }

@media (prefers-reduced-motion: reduce) {
  .dotrail,
  .dotrail__dot { transition: none; }
}

/* sticky cta --------------------------------------------------------------------

   v2 (Jul 8): the v1 bare outlined pill read as thin and "floating"
   next to real commerce sticky footers, and Ewan called it out
   directly: "just a button plopped over a footer", not on brand, and
   disconnected from the page. v3 fixes all three:
     the bar    solid --header-bg fill + a top border matching the
                site header's own border-bottom — the same band that
                opens the page now closes it, a deliberate bookend,
                so it reads as a shelf rather than a sheet of glass;
     the button the brand's actual CTA — solid filled orange, the
                same .cta voice used everywhere else — from the first
                frame it appears, not an outline that upgrades later;
     the body   a proof row under the button by default (not gated),
                so it's never just a lone pill. app.js shows the whole
                bar once slide 6 (#the-capsule) is on screen and hides
                it again on the way back up. Hidden with opacity+
                visibility, same idiom as .nobel-pop__card, so the
                real link inside is out of the tab order while off
                rather than an invisible focus trap. */

.sticky-cta {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  display: flex;
  justify-content: center;
  padding: 14px max(16px, env(safe-area-inset-right)) calc(14px + env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  background: var(--header-bg);
  border-top: 1px solid var(--hairline);
  /* Real lift off the page content — the biggest source of the
     "disconnected" read was the old bar having only a hairline. */
  box-shadow: 0 -10px 28px rgba(8, 11, 43, 0.1);
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  transition: opacity 0.35s ease, transform 0.35s ease, visibility 0.35s;
}

.sticky-cta.is-on {
  opacity: 1;
  visibility: visible;
  transform: none;
}

.sticky-cta__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
  max-width: 420px;
}

/* The brand's actual CTA voice — same register as .cta (wide filled
   orange pill, uppercase, letter-spaced) — from the moment the bar
   appears. No quiet/outline phase: Ewan wants the on-brand look
   immediately; what escalates later is the proof row below, not this
   button's style. */
.sticky-cta__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 52px;
  padding: 13px 24px;
  border-radius: 999px;
  border: 1.5px solid var(--brand);
  background: var(--brand);
  color: #fff;
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  box-shadow: 0 5px 18px rgba(213, 79, 47, 0.3);
  transition: background 160ms ease, border-color 160ms ease;
}

.sticky-cta__link:hover {
  background: var(--brand-deep);
  border-color: var(--brand-deep);
}

.sticky-cta__link:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* Two proof rows sharing one slot, crossfaded — "small icon, bold
   text, pipe separator", the same legit-commerce-badge idiom as
   slide 6's trust row (never the louder discount-badge treatment).
   --badges is the default: an echo of badges already shown moments
   earlier on this same slide, so "more than a button" never means
   showing the visitor something new and unearned. --stats (the
   rating + provider count) only takes over once is-escalated fires,
   which app.js gates on a slide with id="the-proof" existing and
   being reached — no such slide exists (the Jul 9 proof room was
   cut), so the row is dormant markup; see its comment in index.html. */
.sticky-cta__proof {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 5px 0;
  transition: opacity 0.3s ease;
}


.sticky-cta__proof--stats {
  display: none;
}

.sticky-cta.is-escalated .sticky-cta__proof--badges {
  display: none;
}

.sticky-cta.is-escalated .sticky-cta__proof--stats {
  display: flex;
}

/* Type scales down a touch on narrow screens so all three badges
   ("Prescription free | Clinically backed | Made in USA") hold one
   line to ~360px; below that the row wraps gracefully. */
.sticky-cta__proof-item {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: min(0.72rem, 2.9vw);
  font-weight: 600;
  color: var(--ink);
  padding: 0 7px;
  white-space: nowrap;
}

.sticky-cta__proof-item + .sticky-cta__proof-item {
  border-left: 1px solid var(--hairline);
}

.sticky-cta__proof-icon {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
}

@media (prefers-reduced-motion: reduce) {
  .sticky-cta,
  .sticky-cta__link,
  .sticky-cta__proof { transition: none; }
}

/* --------------------------------------------------------------------------
   Slide 1: the morph video. Plays once and holds on the capsules;
   app.js handles replay-on-return and reduced motion.
   -------------------------------------------------------------------------- */

.morph {
  width: min(80vw, 44svh, 380px);
}

.morph__video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  /* Feather the square frame into the page so its edges never read;
     the outer ~9% of the clip is background only. */
  -webkit-mask-image:
    linear-gradient(to right, transparent, #000 9%, #000 91%, transparent),
    linear-gradient(to bottom, transparent, #000 9%, #000 91%, transparent);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to right, transparent, #000 9%, #000 91%, transparent),
    linear-gradient(to bottom, transparent, #000 9%, #000 91%, transparent);
  mask-composite: intersect;
}
