/* ═══════════════════════════════════════════════════════════════════════
   TOTWO — STYLE
   Tokens read off Figma (Flect-Visuals '26 · LANDING · Totwo v2, 1728 grid).
   Same fluid method as Pebb: every size is clamp(min, design-px / 17.28 vw,
   design-px), so any desktop width under 1728 keeps the exact composition.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  /* ── color ── */
  --ink:        #252220;
  --accent:     #3e5866;
  --paper:      #faf7f1;
  --sand:       #eae6dd;   /* the circle wipe + hero gradient tail */
  --card:       #e0dad0;
  --field-line: #dcd5c9;
  --mut:        #83796e;
  --hairline:   rgba(37, 34, 32, 0.25);
  --ctl-ghost:  rgba(62, 88, 102, 0.1);

  /* ── type ── */
  --font-sans:  'PP Neue Montreal', 'Helvetica Neue', Arial, sans-serif;
  --font-serif: 'PP Editorial New', Georgia, serif;

  --fs-display:   clamp(46px, 5.208vw, 90px);   /* hero h1        90 */
  --fs-display-2: clamp(48px, 5.556vw, 96px);   /* closing h1     96 */
  --fs-h2:        clamp(38px, 4.167vw, 72px);   /* section head   72 */
  --fs-h3:        clamp(28px, 2.778vw, 48px);   /* card lines     48 */
  --fs-h4:        clamp(26px, 2.431vw, 42px);   /* step title     42 */
  --fs-h5:        clamp(21px, 1.968vw, 34px);   /* statements     34 */
  --fs-h6:        clamp(20px, 1.736vw, 30px);   /* clinical title 30 */
  --fs-lead:      clamp(18px, 1.505vw, 26px);   /* hero sub       26 */
  --fs-body-lg:   clamp(17px, 1.389vw, 24px);   /* clinical body  24 */
  --fs-body:      clamp(16px, 1.157vw, 20px);   /* body           20 */
  --fs-num:       clamp(22px, 1.852vw, 32px);   /* ( 01 )         32 */
  --fs-btn:       clamp(16px, 1.157vw, 20px);   /* form button    20 */
  --fs-btn-sm:    clamp(15px, 0.984vw, 17px);   /* header button  17 */
  --fs-caption:   clamp(13px, 0.868vw, 15px);   /* footer         15 */

  /* ── space ── */
  --pad-x:      clamp(24px, 6.944vw, 120px);   /* 120 */
  --sec-py:     clamp(72px, 8.681vw, 150px);   /* 150 */
  --gap-head:   clamp(40px, 4.63vw, 80px);     /*  80 */
  --gap-block:  clamp(36px, 3.704vw, 64px);    /*  64 */
  --header-p:   16px;
  --radius-card: clamp(24px, 2.662vw, 46px);   /*  46 */
  /* where a stacking card parks: clear of the fixed header band */
  --stack-top:  clamp(72px, 5.556vw, 96px);

  /* ── controls (pills) ── */
  --ctl-h:      clamp(52px, 3.704vw, 64px);    /*  64 */
  --ctl-px:     clamp(24px, 2.083vw, 36px);    /*  36 */
  --input-px:   clamp(18px, 1.62vw, 28px);     /*  28 */
  --input-w:    clamp(200px, 20.833vw, 360px); /* 360 */
  --header-btn-h: clamp(38px, 2.604vw, 45px);  /*  45 */

  /* ── motion ── */
  --ease:      cubic-bezier(0.22, 1, 0.36, 1);
  --ease-luxe: cubic-bezier(0.16, 1, 0.22, 1);
  --ease-snap: cubic-bezier(0.32, 0.72, 0, 1);

  --muted-opacity: 0.7;
}

/* ═══════════ FONTS ═══════════ */
@font-face {
  font-family: 'PP Neue Montreal';
  src: url('../assets/fonts/PPNeueMontreal-Regular.woff') format('woff');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'PP Neue Montreal';
  src: url('../assets/fonts/PPNeueMontreal-Medium.woff') format('woff');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'PP Editorial New';
  src: url('../assets/fonts/PPEditorialNew-Regular.woff') format('woff');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'PP Editorial New';
  src: url('../assets/fonts/PPEditorialNew-Ultralight.woff') format('woff');
  font-weight: 200; font-style: normal; font-display: swap;
}

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

html {
  -webkit-text-size-adjust: 100%;
  overscroll-behavior-y: none;
  /* the page tint every untinted section sits on — the hero radial and the
     clinical gradient both land on it, so the seams disappear */
  background: var(--sand);
  scrollbar-width: none;
}
html::-webkit-scrollbar { width: 0; height: 0; display: none; }

body {
  font-family: var(--font-sans);
  font-weight: 400;
  background: var(--sand);
  color: var(--ink);
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
}

a { color: inherit; text-decoration: none; }
img, svg { display: block; max-width: 100%; }
img { height: auto; }
button { font-family: inherit; cursor: pointer; background: none; border: none; color: inherit; }
ul, ol { list-style: none; }
figure { margin: 0; }
::selection { background: var(--ink); color: var(--paper); }

:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; border-radius: 4px; }
.btn:focus-visible, .rail-btn:focus-visible { outline-color: var(--ink); }

.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 ROLES ═══════════ */
.serif {
  font-family: var(--font-serif);
  font-weight: 400;
  font-style: normal;
  /* the editorial accent carries the slate blue everywhere except the
     clinical head, where the comp keeps it in ink (scoped further down) */
  color: var(--accent);
}

.display    { font-size: var(--fs-display);   font-weight: 500; line-height: 0.9;  letter-spacing: -0.03em; }
.display-2  { font-size: var(--fs-display-2); font-weight: 500; line-height: 1;    letter-spacing: -0.03em; }
.h2         { font-size: var(--fs-h2);        font-weight: 500; line-height: 1;    letter-spacing: -0.03em; }
.body-lg    { font-size: var(--fs-body-lg);   font-weight: 500; line-height: 1.45; letter-spacing: -0.015em; }
.body       { font-size: var(--fs-body);      font-weight: 400; line-height: 1.3;  letter-spacing: -0.01em; }

/* ═══════════ BUTTON + LETTER ROLL ═══════════ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--ctl-h);
  padding: 0 var(--ctl-px);
  border-radius: 100px;
  background: var(--accent);
  color: #ffffff;
  font-size: var(--fs-btn);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1;
  white-space: nowrap;
  flex: 0 0 auto;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 0.4s var(--ease), color 0.4s var(--ease);
}
.btn:hover { background: color-mix(in srgb, var(--accent) 88%, #000000); }
.btn-label { display: inline-flex; line-height: 1; }
.roll-space { display: inline-block; }
.roll-char {
  position: relative;
  display: inline-block;
  overflow: hidden;
  line-height: 1;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.roll-char > span {
  display: block;
  transition: transform 0.55s var(--ease-luxe);
  transition-delay: var(--cd, 0ms);
}
.roll-char .r-bot { position: absolute; inset: 0; transform: translateY(110%); }
.btn:hover .roll-char .r-top { transform: translateY(-110%); }
.btn:hover .roll-char .r-bot { transform: translateY(0); }

/* ═══════════ FORM (pill input + pill button, 10px seam) ═══════════ */
.access-form { display: flex; flex-direction: column; gap: 16px; }
.form-row { display: flex; align-items: center; gap: 10px; }
.input {
  height: var(--ctl-h);
  width: var(--input-w);
  padding: 0 var(--input-px);
  border: 1px solid var(--field-line);
  border-radius: 100px;
  background: var(--paper);
  font-family: inherit;
  font-size: var(--fs-btn);
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--ink);
  transition: box-shadow 0.25s var(--ease), border-color 0.25s var(--ease);
}
.input::placeholder { color: var(--mut); }
.input:focus { outline: none; border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.input.is-error { border-color: #c34434; box-shadow: inset 0 0 0 1px #c34434; }
.form-note { font-size: var(--fs-caption); letter-spacing: -0.01em; line-height: 1; color: var(--mut); }
.access-form .form-success {
  display: flex;
  align-items: center;
  height: var(--ctl-h);
  font-size: var(--fs-lead);
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--ink);
}

/* ═══════════ HEADER (fixed) ═══════════ */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--header-p);
  pointer-events: none;
  --header-fg: var(--ink);
}
.site-header > * { pointer-events: auto; }
/* over the photo block (and the blue-grey top of the clinical gradient) the
   mark and label flip to paper */
.site-header.on-dark { --header-fg: var(--paper); }

.logo { display: inline-flex; align-items: center; height: var(--header-btn-h); }
.logo-mark {
  display: block;
  width: clamp(80px, 6.019vw, 104px);
  aspect-ratio: 104 / 29;
  /* stays ink over every section — the tone tracker still re-inks the CTA,
     but the mark no longer flips */
  background: var(--ink);
  -webkit-mask: url('../assets/logo.svg') center / contain no-repeat;
  mask: url('../assets/logo.svg') center / contain no-repeat;
}

/* header CTA: hidden until the hero form scrolls away, docked once the
   closing form is on screen */
.header-cta {
  height: var(--header-btn-h);
  padding: 0 clamp(18px, 1.505vw, 26px);
  font-size: var(--fs-btn-sm);
  letter-spacing: -0.01em;
  opacity: 0;
  visibility: hidden;
  pointer-events: none !important;
  transform: translate3d(12px, 0, 0);
  transition:
    opacity 0.5s var(--ease-luxe),
    transform 0.6s var(--ease-luxe),
    background-color 0.7s var(--ease),
    visibility 0s linear 0.5s;
}
body.cta-active .header-cta {
  opacity: 1;
  visibility: visible;
  pointer-events: auto !important;
  transform: none;
  transition:
    opacity 0.5s var(--ease-luxe),
    transform 0.6s var(--ease-luxe),
    background-color 0.7s var(--ease);
}
body.cta-docked .header-cta {
  opacity: 0;
  visibility: hidden;
  pointer-events: none !important;
}

.mobile-cta { display: none; }

/* ═══════════ HERO ═══════════ */
.hero {
  position: relative;
  overflow: clip;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: clamp(120px, 13.889vw, 240px) var(--pad-x) var(--sec-py);
  /* exact radial from the comp: a very wide, flat ellipse anchored to the
     top edge — 6778 x 1042 px on the 1728 grid */
  background: radial-gradient(392.25% 100% at 50% 0%, #cfdce4 0%, #faf7f1 54.8%, #eae6dd 100%);
  color: var(--ink);
}
.hero-copy {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(24px, 2.315vw, 40px);
}
/* the mockup bleeds past the right edge and under the fold, exactly as the
   comp places it (1309x1303 centred at 1320.5 / 688.5 on the 1728 frame) */
.hero-mockup {
  position: absolute;
  left: 38.54%;
  bottom: -17.25vw;
  width: 75.75%;
  opacity: 0;
  transform: translate3d(7%, 9%, 0) scale(0.965);
  animation: mockup-glide 1.9s var(--ease-luxe) 120ms forwards;
}
.hero-mockup img { width: 100%; height: auto; }
@keyframes mockup-glide {
  to { opacity: 1; transform: none; }
}

.hero-title {
  /* Holds the comp's break after "coach". Measured: "Relationship coach"
     is 7.33em, "who knows you both" 8.22em, and the run through "who"
     9.27em — so anything in 8.3–9.2em keeps line 1 short and line 2 whole.
     Em-based, so it tracks the fluid title; phones just wrap naturally. */
  max-width: 8.6em;
  opacity: 0;
}
.hero-title.is-split { opacity: 1; }
.hero-title .line-clip {
  display: block;
  overflow: hidden;
  padding-bottom: 0.1em;
  margin-bottom: -0.1em;
}
.hero-title .line-inner {
  display: block;
  animation: hero-line-rise 0.95s var(--ease-luxe) var(--ld, 0ms) both;
}
.hero-title.settled .line-inner { animation: none; transform: none; }
@keyframes hero-line-rise { from { transform: translateY(112%); } }
@keyframes hero-rise { from { opacity: 0; transform: translateY(26px); } }

.hero-sub {
  font-size: var(--fs-lead);
  font-weight: 500;
  line-height: 1.34;
  letter-spacing: -0.02em;
  opacity: var(--muted-opacity);
  max-width: clamp(300px, 41.667vw, 720px);
  animation: hero-rise 0.85s var(--ease-luxe) 240ms both;
}
.hero .access-form { animation: hero-rise 0.85s var(--ease-luxe) 360ms both; }

/* ═══════════ SECTION SHARED ═══════════ */
.section { padding: var(--sec-py) var(--pad-x); }

/* ═══════════ THE TALK (photo · circle wipe · statements) ═══════════
   One pinned stage. The shot holds while you scroll; the sand circle grows
   out of the centre and takes the frame, and the copy lands inside it.
   Scroll only writes two custom properties — a composited scale and an
   opacity — so nothing repaints per frame. */
.talk {
  position: relative;
  /* the track only needs to cover the wipe plus a beat to read the copy —
     the old 280svh left more than a screen of nothing after everything had
     already landed */
  height: 180svh;
  background: var(--sand);
}
.talk-stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
}
.talk-media, .talk-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.talk-media img { object-fit: cover; object-position: 50% 42%; }
.talk-circle {
  position: absolute;
  left: 50%;
  top: 50%;
  /* runs well past every edge at full scale, so the wipe reads as a clean
     takeover — you never catch the arc landing inside the frame */
  width: max(150vw, 150svh);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--sand);
  transform: translate(-50%, -50%) scale(var(--circle, 0));
}
/* the copy rides the same sticky line as the stage and sits on top of it —
   a sibling, not a child, so phones can drop it below the band instead */
.talk-card {
  position: sticky;
  top: 0;
  z-index: 1;
  height: 100svh;
  margin-top: -100svh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sec-py) var(--pad-x);
  opacity: var(--talk-copy, 0);
  transform: translateY(calc((1 - var(--talk-copy, 0)) * 20px));
}
.talk-copy {
  /* 620 of the 1728 grid — scaled with the grid, not pinned, so the head
     keeps the comp's break ("Nobody teaches you" / "how to be a couple")
     at every desktop width */
  width: clamp(300px, 35.88vw, 620px);
  display: flex;
  flex-direction: column;
  gap: var(--gap-block);
}
.talk-statements {
  display: flex;
  flex-direction: column;
  gap: var(--gap-head);
  font-size: var(--fs-h5);
  font-weight: 500;
  line-height: 1.26;
  letter-spacing: -0.025em;
}
.talk-statements p { opacity: var(--muted-opacity); }

/* ═══════════ HOW IT WORKS ═══════════ */
.how { display: flex; flex-direction: column; gap: var(--gap-head); }
.steps {
  display: flex;
  gap: 16px;
  align-items: flex-start;
}
.step {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(22px, 2.315vw, 40px);
}
.step-num {
  font-family: var(--font-serif);
  font-weight: 200;
  font-size: var(--fs-num);
  line-height: 1;
  color: var(--accent);
  white-space: nowrap;
  /* no text-box-trim here: it crops the line box to the cap height, and the
     parentheses reach above and below that — inside the phone rail, whose
     overflow-y is hidden, the trimmed overhang gets sliced off */
}
.step-body { display: flex; flex-direction: column; gap: clamp(14px, 1.157vw, 20px); }
.step-desc {
  display: flex;
  flex-direction: column;
  gap: clamp(12px, 1.157vw, 20px);
  padding-right: clamp(12px, 1.389vw, 24px);
}
.step-title {
  font-size: var(--fs-h4);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.03em;
}
.step-desc .body {
  opacity: var(--muted-opacity);
  --reveal-opacity: var(--muted-opacity);
  /* the comp pads the one-line captions with an empty second line so all
     three shots start on the same baseline — reserve those two lines here
     instead of shipping blank paragraphs */
  min-height: 2.6em;
}
.step-img {
  width: 100%;
  aspect-ratio: 485 / 540;
  overflow: hidden;
}
.step-img img { width: 100%; height: 100%; object-fit: cover; }
.steps .step:nth-child(2) { --step-reveal-delay: 110ms; }
.steps .step:nth-child(3) { --step-reveal-delay: 220ms; }

/* ═══════════ WHAT TOTWO COACH DOES ═══════════
   Both cards park at the same sticky line, so card 2 rides up over card 1
   the moment card 1 reaches the top and holds there until it lands itself. */
/* The head has to be off the screen BEFORE card 1 parks, otherwise you watch
   it crawl upward past a card that is already frozen. Card 1 parks when its
   top hits --stack-top, and the head's bottom is exactly one gap above that
   — so the gap has to clear --stack-top, with a little margin. */
.what {
  display: flex;
  flex-direction: column;
  gap: max(var(--gap-block), calc(var(--stack-top) + 28px));
}
.stack { display: flex; flex-direction: column; gap: 16px; }
/* Trailing room, and it is load-bearing. A sticky item can only travel
   inside its containing block — for a flex item that is the flex container's
   CONTENT box, so padding on .stack would not count. The last card's bottom
   sits flush with that box, which leaves it a sticky range of zero: it never
   parks at the line, it slides straight through and drags card 1 out with
   it. This spacer is a real flex item, so it lengthens the content box and
   hands card 2 a range of its own. It sits INSIDE the section, above its
   bottom padding — the gap to the next block is untouched. */
.stack::after {
  content: '';
  display: block;
  flex: 0 0 auto;
  height: clamp(100px, 13vw, 200px);
}
.tw-card {
  position: sticky;
  top: var(--stack-top);
  display: flex;
  align-items: stretch;
  background: var(--card);
  border-radius: var(--radius-card);
  overflow: hidden;
  /* the comp's 924 on the 1728 grid, trimmed 15% — and never taller than the
     band under the header. The mockup is height-driven, so it follows the
     card down on both counts. */
  height: min(45.45vw, calc(100svh - var(--stack-top) - 16px));
  min-height: 330px;
}
/* the comp splits the card exactly in half: the mockup centred in its side
   with a 40 gutter above and below, the copy in the other half */
.tw-media {
  /* basis 50%, not 0: with border-box a zero basis still adds the copy's
     160px gutter on top of its share, which pushed the split to 45/55 */
  flex: 0 0 50%;
  min-width: 0;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-block: clamp(20px, 2.315vw, 40px);
}
/* height-driven, not width-driven — it fills whatever the card gives it */
.tw-media img {
  height: 100%;
  width: auto;
  max-width: 100%;
  object-fit: contain;
}
.tw-copy {
  flex: 0 0 50%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(18px, 1.852vw, 32px);
  padding-block: clamp(40px, 5.787vw, 100px);
  padding-right: clamp(24px, 9.259vw, 160px);
}
.tw-card--flip {
  /* the second card sits a shade cooler than the first */
  background: #d5d4ce;
}
.tw-card--flip .tw-copy {
  padding-right: 0;
  padding-left: clamp(24px, 9.259vw, 160px);
}
.tw-line {
  font-size: var(--fs-h3);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.03em;
}
.tw-card--flip .body { opacity: var(--muted-opacity); --reveal-opacity: var(--muted-opacity); line-height: 1.45; }
.tw-hair { height: 1px; background: var(--hairline); }

/* ═══════════ GROUNDED IN CLINICAL RESEARCH ═══════════ */
.clinical {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 1.852vw, 32px);
  padding: var(--sec-py) 0;
  /* the comp's tail is #e8e4db; landed on the page tint instead so the
     section dissolves into the background with no seam */
  background: linear-gradient(180deg, #9db0bb 0%, var(--sand) 100%);
}
.clinical .rail { padding-inline: var(--pad-x); }
.cl-row {
  display: flex;
  align-items: stretch;
  width: max-content;
  height: clamp(380px, 31.019vw, 536px);
}
.cl-cell { width: clamp(280px, 27.778vw, 480px); flex: 0 0 auto; }
.cl-intro {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(24px, 2.315vw, 40px);
  padding-right: clamp(20px, 1.852vw, 32px);
}
/* the head holds the comp's three-line break ("Grounded" / "in clinical" /
   "research"). Measured at this size: "Grounded" is 250px, "Grounded in"
   302px, and the longest kept line ("research") 217px — so a cap between
   them breaks it where the comp does instead of letting "clinical research"
   run 15px past the column and collide with the photo. */
.cl-intro .h2 { max-width: 4.6em; }
/* the comp keeps this one editorial accent in ink, not slate */
.cl-intro .serif { color: var(--ink); }
.cl-intro-body { display: flex; flex-direction: column; gap: clamp(14px, 1.389vw, 24px); }
.cl-intro-body .body-lg { opacity: var(--muted-opacity); --reveal-opacity: var(--muted-opacity); }
.cl-photo { overflow: hidden; }
.cl-photo img { width: 100%; height: 100%; object-fit: cover; }
.cl-rule { flex: 0 0 auto; width: 1px; background: var(--hairline); }
.cl-hair { height: 1px; background: var(--hairline); }
.cl-pair { display: flex; flex-direction: column; }
.cl-block {
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 1.852vw, 32px);
  padding-inline: clamp(16px, 1.852vw, 32px);
}
.cl-pair .cl-block { flex: 1 1 0; min-height: 0; }
.cl-block--end { justify-content: flex-end; }
.cl-block--mid { justify-content: center; height: 100%; }
.cl-title {
  font-size: var(--fs-h6);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: -0.02em;
}
.cl-block .body-lg { opacity: var(--muted-opacity); --reveal-opacity: var(--muted-opacity); }

.rail-nav {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-inline: var(--pad-x);
}
.rail-btn {
  width: clamp(96px, 7.407vw, 128px);
  height: 44px;
  border-radius: 42px;
  background: var(--ctl-ghost);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.3s var(--ease), opacity 0.3s var(--ease);
}
.rail-btn:hover:not(:disabled) { background: rgba(62, 88, 102, 0.18); }
.rail-btn:active:not(:disabled) svg { transform: scale(0.9); }
.rail-btn svg { transition: transform 0.2s var(--ease); }
.rail-btn:disabled { opacity: 0.4; cursor: default; }

/* ═══════════ RAIL (shared: clinical row, mobile steps) ═══════════ */
.rail {
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  cursor: grab;
}
.rail::-webkit-scrollbar { display: none; }
.rail.is-dragging { cursor: grabbing; scroll-snap-type: none; }
.rail.is-dragging .cl-cell, .rail.is-dragging .step { pointer-events: none; }

/* ═══════════ CLOSING CTA + FOOTER ═══════════ */
.cta {
  position: relative;
  overflow: clip;
}
/* second blue blob, placed exactly as the comp: 1277x1117 centred at
   1111.5 / -65 relative to the section's top-left */
.cta-mockup {
  position: absolute;
  left: 27.37%;
  top: -3.76vw;
  width: 73.9%;
  opacity: 0;
  transform: translate3d(6%, 8%, 0) scale(0.965);
  transition:
    opacity 1.1s var(--ease-luxe),
    transform 1.9s var(--ease-luxe);
}
.cta-mockup.visible { opacity: 1; transform: none; }
.cta-mockup img { width: 100%; height: auto; }

.cta-card {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(28px, 2.778vw, 48px);
  padding: var(--sec-py) var(--pad-x);
}
.cta-card .display-2 { max-width: 6.6em; }

.footer {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: clamp(28px, 2.315vw, 40px) var(--pad-x) clamp(32px, 2.778vw, 48px);
}
.footer-links {
  display: flex;
  align-items: center;
  gap: clamp(20px, 3.241vw, 56px);
  font-size: var(--fs-caption);
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1;
}
.footer-links .logo-mark { width: clamp(72px, 5.093vw, 88px); --header-fg: var(--ink); }
/* the rule wipes in from the left, then retracts back to the left on the
   way out — origin stays put so it never reads as a slide */
.footer-links a:not(.footer-logo) {
  position: relative;
  transition: color 0.35s var(--ease);
}
.footer-links a:not(.footer-logo)::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.4em;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.45s var(--ease-luxe);
}
.footer-links a:not(.footer-logo):hover { color: var(--accent); }
.footer-links a:not(.footer-logo):hover::after { transform: scaleX(1); }
.footer-copyright {
  font-size: var(--fs-caption);
  letter-spacing: -0.01em;
  line-height: 1;
  color: var(--mut);
}

/* ═══════════ REVEALS ═══════════ */
.reveal-up {
  opacity: 0;
  transform: translateY(32px);
  transition:
    opacity 0.8s var(--ease-luxe) var(--delay, 0s),
    transform 0.9s var(--ease-luxe) var(--delay, 0s);
}
.reveal-up.visible { opacity: var(--reveal-opacity, 1); transform: none; }

/* Editorial line reveal: whole lines rise through a tight clipping mask. */
.word-fade .line-measure {
  /* inline (not inline-block) so measurement wraps like real text and two
     fonts on one line are read as ONE line, never split at the font seam */
  display: inline;
  /* one span = one word = one line box. Without this the browser happily
     breaks inside a hyphenated word ("real-life" → "real-" / "life"); the
     span's rect then covers two line boxes, its width reads as the full
     column, and every grouping decision after it is garbage. Spaces between
     words live in the parent, so normal wrapping is untouched. */
  white-space: nowrap;
}
.word-fade .line-clip {
  display: block;
  overflow: hidden;
  padding-bottom: 0.1em;
  margin-bottom: -0.1em;
}
.word-fade .line-inner {
  display: block;
  transform: translateY(112%);
  transition: transform 0.9s var(--ease-luxe) calc(var(--step-reveal-delay, 0ms) + var(--ld, 0ms));
}
.word-fade.visible .line-inner { transform: none; }
.word-fade.revealed .line-inner { transition: none; }

/* photo bloom: the shot rests over-scaled inside its clipped frame and
   settles home slowly */
.img-reveal { overflow: hidden; }
.img-reveal img {
  opacity: 0;
  transform: scale(1.1);
  transition:
    opacity 0.9s var(--ease-luxe) var(--step-reveal-delay, 0ms),
    transform 1.5s var(--ease-luxe) var(--step-reveal-delay, 0ms);
  will-change: transform;
}
.img-reveal.visible img { opacity: 1; transform: none; will-change: auto; }

/* the pinned stage's shot is always simply there — the bloom is a phone-only
   treatment, where that block is a plain square photo in the flow */
@media (min-width: 901px) {
  .talk-media.img-reveal img {
    opacity: 1;
    transform: none;
    transition: none;
    will-change: auto;
  }
}

/* ═══════════ RESPONSIVE ═══════════ */
@media (max-width: 900px) {
  /* ── hero: mockup on top, copy under it ── */
  /* exactly one device-window tall. --hero-h is a px value JS locks at load
     and only refreshes on a real width change, so a collapsing in-app
     browser toolbar can't grow the section mid-scroll. */
  .hero {
    justify-content: flex-start;
    height: 100svh;
    height: var(--hero-h, 100svh);
    min-height: 560px;
    /* the mockup is height-bound here, so every px trimmed above and below
       it goes straight into how wide the phone reads. The render carries
       transparent headroom, so the top band can tuck under the header
       without the device itself touching it. */
    padding: clamp(40px, 10vw, 96px) var(--pad-x) clamp(20px, 5vw, 40px);
  }
  /* the mockup takes whatever height is left between the header band and the
     copy and scales itself down to it — so the form is never pushed under
     the fold on a short phone */
  .hero-mockup {
    position: relative;
    left: auto;
    bottom: auto;
    width: auto;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: clamp(8px, 2vw, 20px);
  }
  .hero-mockup img { width: auto; max-width: 148%; max-height: 100%; }
  .hero-copy { flex: 0 0 auto; }
  .hero-title { max-width: none; }
  .hero-sub { max-width: 100%; }

  /* ── phones drop the wipe entirely: a plain full-width shot in its own
     aspect ratio, copy underneath, revealed line by line like every other
     block ── */
  .talk { height: auto; }
  .talk-stage { position: static; height: auto; overflow: visible; }
  /* display:block matters: <picture> is inline by default, so once it stops
     being absolutely positioned it has no box — the overflow clip that the
     bloom needs is ignored, and the reveal observer sees nothing to watch */
  .talk-media, .talk-media img { position: static; display: block; width: 100%; height: auto; }
  /* square crop, and it blooms in like the step shots (.img-reveal) */
  .talk-media img { aspect-ratio: 1 / 1; object-fit: cover; }
  .talk-circle { display: none; }
  .talk-card {
    position: static;
    height: auto;
    margin-top: 0;
    padding: var(--sec-py) var(--pad-x);
    opacity: 1;
    transform: none;
  }
  .talk-copy { width: 100%; }

  /* the three-line cap is a desktop composition — on a phone column it just
     shreds the head */
  .cl-intro .h2 { max-width: none; }

  /* ── how it works becomes the swipe rail (desktop keeps all three in view) ── */
  .how .rail {
    margin-inline: calc(var(--pad-x) * -1);
    padding-inline: var(--pad-x);
    /* the rail hides overflow-y to keep it from scrolling vertically, which
       also slices anything flush with its edge — the serif parentheses of
       ( 01 ) stand 2px proud of their line box. This keeps them clear. */
    padding-block: 6px;
    margin-block: -6px;
    scroll-snap-type: x mandatory;
    scroll-padding-inline-start: var(--pad-x);
  }
  .steps { width: max-content; gap: 20px; }
  .step {
    flex: 0 0 auto;
    width: min(76vw, 420px);
    scroll-snap-align: start;
    /* one card per swipe, momentum included */
    scroll-snap-stop: always;
  }

  /* ── stacking cards flatten: no sticky, copy on top, mockup below ── */
  /* nothing parks here, so neither the trailing room nor the head's clearance
     is needed */
  .what { gap: var(--gap-block); }
  .stack::after { display: none; }
  .tw-card {
    --card-pad: clamp(20px, 5.5vw, 40px);
    position: static;
    height: auto;
    min-height: 0;
    max-height: none;
    align-items: stretch;
    /* card 1's DOM order is media → copy, so it reverses; card 2 already
       reads copy → media */
    flex-direction: column-reverse;
  }
  .tw-card--flip { flex-direction: column; }
  /* the mockup is cut in half at the card's bottom edge — the top half of
     the phone shows, the rest runs off */
  .tw-media {
    flex: 0 0 auto;
    padding: 0;
    align-items: flex-start;
    overflow: hidden;
    aspect-ratio: 100 / 94;
  }
  .tw-media img {
    width: 72%;
    max-width: none;
    max-height: none;
    height: auto;
  }
  /* equal pad on the sides and the top; the mockup closes the card below */
  .tw-copy,
  .tw-card--flip .tw-copy {
    flex: 0 0 auto;
    padding: var(--card-pad) var(--card-pad) var(--card-pad);
  }

  /* ── clinical drops the photo and reads as a plain vertical column ── */
  .clinical .rail { overflow: visible; cursor: auto; }
  .cl-row {
    width: auto;
    height: auto;
    flex-direction: column;
    gap: clamp(32px, 7vw, 56px);
  }
  .cl-cell { width: 100%; }
  .cl-photo { display: none; }
  /* the row's vertical hairlines lie down and become the rules between the
     stacked blocks; the trailing one has nothing after it, so it goes */
  .cl-rule { width: 100%; height: 1px; }
  .cl-rule:last-child { display: none; }
  .cl-intro { padding-right: 0; }
  .cl-pair { gap: clamp(32px, 7vw, 56px); }
  /* the pair's blocks share a fixed row height on desktop via flex-basis 0;
     in the auto-height mobile column that basis collapses them to nothing
     and the copy piles up on itself */
  .cl-pair .cl-block { flex: 0 0 auto; }
  .cl-block { padding-inline: 0; }
  .cl-block--end, .cl-block--mid { justify-content: flex-start; height: auto; }
  .rail-nav { display: none; }

  /* ── closing blob sits above the card instead of behind it ── */
  /* wider, and pulled left: the render was bleeding off the right edge while
     its transparent left margin read as a gap */
  .cta-mockup {
    position: relative;
    left: auto;
    top: auto;
    width: 142%;
    margin: 0 -14% clamp(-16vw, -10vw, -8vw) -28%;
  }
  .footer { flex-direction: column; align-items: flex-start; gap: 20px; }
}

@media (max-width: 767px) {
  .header-cta { display: none; }

  /* buttons stay static on touch: no hover carry-over or roll mid-tap */
  .btn, .btn:hover, .btn:active, .rail-btn, .rail-btn:hover, .rail-btn:active {
    -webkit-appearance: none;
    appearance: none;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    transition: none !important;
  }
  .btn .roll-char { overflow: visible; padding-bottom: 0; margin-bottom: 0; }
  .btn .roll-char > span { transition: none !important; transform: none !important; }
  .btn .roll-char .r-bot { display: none; }

  .mobile-cta {
    display: inline-flex;
    position: fixed;
    left: 50%;
    bottom: calc(8px + env(safe-area-inset-bottom));
    z-index: 120;
    height: 52px;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translate3d(-50%, 8px, 0) scale(0.985);
    transition:
      opacity 0.58s var(--ease-luxe),
      transform 0.82s var(--ease-luxe),
      visibility 0s linear 0.58s;
  }
  body.cta-active .mobile-cta {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translate3d(-50%, 0, 0) scale(1);
    transition:
      opacity 0.58s var(--ease-luxe),
      transform 0.82s var(--ease-luxe);
  }
  body.cta-docked .mobile-cta,
  html.keyboard-open .mobile-cta {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
  }
}

@media (max-width: 640px) {
  :root {
    --pad-x: 20px;
    --sec-py: 72px;
    --gap-head: 40px;
    --stack-top: 68px;
  }

  /* input shrinks to whatever is left beside the button so the pair stays on
     one row — wrapping reads like two separate controls */
  .access-form { width: 100%; }
  .input { flex: 1 1 0; min-width: 0; width: auto; }
  .btn { flex: 0 0 auto; padding-inline: clamp(18px, 5vw, 28px); }

  .hero-sub { animation-delay: 200ms; }
  .hero .access-form { animation-delay: 320ms; }

}

/* ═══════════ REDUCED MOTION ═══════════ */
@media (prefers-reduced-motion: reduce) {
  .reveal-up { opacity: 1; transform: none; transition: none; }
  .hero-mockup { opacity: 1; transform: none; animation: none; }
  .hero-title, .hero-title .line-inner, .hero-sub, .hero .access-form { animation: none; }
  .hero-title { opacity: 1; }
  .cta-mockup { opacity: 1; transform: none; transition: none; }
  .talk { height: auto; }
  .talk-stage { position: static; height: 60svh; }
  .talk-circle { transform: translate(-50%, -50%) scale(1); }
  .talk-card {
    position: static;
    height: auto;
    margin-top: 0;
    opacity: 1;
    transform: none;
  }
  .word-fade { opacity: var(--reveal-opacity, 1); transform: none; transition: none; }
  .word-fade .line-clip,
  .word-fade .line-inner {
    display: inline;
    overflow: visible;
    padding: 0;
    margin: 0;
    transform: none;
    transition: none;
  }
  .img-reveal img { opacity: 1; transform: none; transition: none; }
  .roll-char > span { transition: none; }
  .btn:hover .roll-char .r-top { transform: none; }
  .btn:hover .roll-char .r-bot { transform: translateY(110%); }
  html { scroll-behavior: auto; }
}
