/* =========================================================================
   THE YEAR-ROUND ATHLETE WORKSHOP — page styles.  Direction: B · CINEMATIC.

   LOAD ORDER MATTERS:
     assets/tokens.css    colour, type, radii, shadows, easing (the kit)
     assets/effects.css   the AUTHORISED DEVIATION layer — gradients, glow,
                          orbs, and the one secondary hue, violet (written without its hash
                          here so the audit reports the real declaration in
                          effects.css, not this sentence about it)
     styles.css           this file: layout, rhythm, components

   ⚠️ THIS PAGE DELIBERATELY BREAKS THE KIT. See BRAND-DEVIATION.md beside
   this file before "fixing" a gradient or the violet. kit-audit.mjs is
   SUPPOSED to keep reporting them.

   WHY B: the approved copy is one long headline and one long subhead, which
   is centred-stack copy. Variant B plays to that instead of fighting it, and
   the video ends up wider than the split layout allowed.

   TWO STANDING CONSTRAINTS, both learned the hard way on this page:

   1. COPY IS FROZEN. Everything that reads as new content is existing copy
      re-arranged — the details bar became the floating plates, and the
      figures inside the testimonials were pulled to display scale INSIDE
      their own quotes. Not one word was added.

   2. NEUTRALS COME FROM THE KIT. The design handoff quoted its own neutral
      ramp and its own spacing scale; neither exists in this brand. Both are
      mapped to kit tokens or declared as page scale below. There is not one
      hex literal in this file — deliberately, so the audit can prove that
      the ONLY colour deviation is the violet the effect layer introduces.
   ========================================================================= */

/* =========================================================================
   1. PAGE SCALE
   The handoff bundle shipped --space-1..8 / --pad-card, none of which exist
   in the SPC kit (DESIGN.md declares four spacing values, total). They live
   here as page scale rather than being faked into tokens.css.
   ========================================================================= */
:root {
  --w-read: 760px;
  --w-mid: 900px;
  --w-wide: 1000px;
  --w-max: 1240px;

  --band-py: clamp(36px, 6vw, 88px);
  --gap-prose: 20px;
  --card-pad: 28px;

  --fs-h1: clamp(30px, 3.6vw, 48px);
  --fs-h1-ty: clamp(28px, 3.4vw, 44px);
  --fs-h2: clamp(26px, 3.4vw, 36px);
  --fs-h3: clamp(21px, 2.4vw, 24px);
  --fs-lede: clamp(17px, 1.9vw, 20px);
  --fs-cta-body: clamp(17px, 1.8vw, 19px);
}

* { box-sizing: border-box; }
body { margin: 0; background: var(--bg-page); color: var(--text-body); font-family: var(--font-body); }

/* =========================================================================
   THE MOST IMPORTANT RULE IN THIS FILE. DO NOT DELETE IT.

   A `data-theme` on a subtree flips that subtree's TOKENS, but it does NOT
   change any colour already inherited into it. `body` resolves
   `color: var(--text-body)` ONCE, at the root, and every descendant without
   its own `color` inherits that computed value — not the variable.

   So on a black band inside a LIGHT-themed page, `--text-body` correctly
   became #f0f1f3 while the text carried on rendering in the root's dark ink.
   Measured: 1.39:1. Effectively invisible, and it looked like a deliberate
   "subtle" choice rather than a bug.

   It only ever looked right in dark mode, because root and band agreed there
   by luck. Light is an equal default in this brand, so half the audience was
   getting unreadable copy.

   Re-asserting `color` on every themed element forces the token to be read
   again inside that subtree. One line, and it fixes the thank-you intro, the
   bridge copy and all four testimonial quotes at once.
   ========================================================================= */
[data-theme] { color: var(--text-body); }

/* Accent used as TEXT, which is a different problem from accent used as a
   fill. Azure on black is 6.68:1 and fine; azure on white is 3.14:1 and on
   the #f7f8fa canvas 2.96:1, both failing. So the text step flips with the
   canvas while --accent itself stays #0094ff for marks, rules and borders. */
:root, [data-theme='light'] { --accent-text: var(--blue-700); }   /* 6.44:1 on white */
[data-theme='dark']         { --accent-text: var(--blue-400); }   /* 8.30:1 on black */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) { --accent-text: var(--blue-400); }
}
.spc-mono--accent { color: var(--accent-text); }

/* The kit caps every <p> at the reading column. Right for prose, wrong for a
   paragraph already inside a narrower grid cell. */
p { max-width: none; }
img { max-width: 100%; height: auto; display: block; }

/* =========================================================================
   2. BANDS
   Each band carries its own ambient depth. .fx-layer / .fx-orb / .fx-grid /
   .fx-dots come from effects.css; this only positions content above them.
   ========================================================================= */
.band {
  position: relative;
  padding-block: var(--band-py);
  padding-inline: var(--gutter);
  overflow: hidden;
  isolation: isolate;
}
.band > .wrap { position: relative; z-index: 1; }

.band--dark    { background: var(--spc-black); }
.band--surface { background: var(--bg-subtle); }
.band--plain   { background: var(--bg-page); }
.band--rule-top { border-top: var(--bw-base) solid var(--border); }

.wrap       { max-width: var(--w-read); margin-inline: auto; }
.wrap--mid  { max-width: var(--w-mid); }
.wrap--wide { max-width: var(--w-wide); }
.wrap--max  { max-width: var(--w-max); }

/* =========================================================================
   3. TYPE
   Type ROLE classes (.spc-title / .spc-heading / .spc-subtitle / .spc-mono)
   come from tokens.css and go on the text element itself, never a container.
   ========================================================================= */
.eyebrow { font-size: var(--fs-mono); margin: 0; }
.h1 { font-size: var(--fs-h1); margin: 0; line-height: 1.02; text-wrap: balance; }
.h1--ty { font-size: var(--fs-h1-ty); }
.h2 { font-size: var(--fs-h2); margin: 0; }
.h3 { font-size: var(--fs-h3); margin: 0; }
.lede { font-size: var(--fs-lede); margin: 0; }
.prose { font-size: var(--fs-body); line-height: var(--lh-body); margin: 0; }
.prose--strong { color: var(--text-strong); font-weight: 700; }
.prose--beat { font-size: clamp(18px, 2vw, 20px); font-weight: 700; color: var(--text-strong); }
.stack { display: flex; flex-direction: column; gap: var(--gap-prose); }
.stack--tight { gap: 16px; }

/* Keeps a hyphenated compound off a line break. "YEAR-" ending one line and
   "ROUND," starting the next reads as a rendering fault at display size.
   RE-CHECK after any headline copy edit. */
.nowrap { white-space: nowrap; }

.pullquote {
  margin: 0;
  border-left: 4px solid var(--accent);
  padding: 4px 0 4px 24px;
  font-size: clamp(18px, 2vw, 21px);
  line-height: 1.5;
  font-weight: 700;
  color: var(--text-strong);
  text-align: left;
}

/* Status pill with the live dot. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 9px 18px 9px 14px;
  border: var(--bw-base) solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--fx-scrim, rgba(0, 0, 0, 0.66));
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  font-size: var(--fs-mono);
  margin: 0;
}

/* =========================================================================
   4. BUTTONS
   Hover/press are pinned to --blue-600 / --blue-700 rather than the semantic
   --accent-hover / --accent-press, because those flip LIGHTER under dark and
   this button appears on both canvases.
   ========================================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  /* --blue-700, not --accent. White on #0094ff measures 3.14:1 and fails AA;
     white on #005fb0 measures 6.44:1. Same azure hue, so the page still reads
     as one accent, and it matches the Scaling Kit's fix so every SPC surface
     agrees. --accent stays #0094ff everywhere it is a FILL or a MARK rather
     than a background for white text. */
  background: var(--blue-700);
  color: var(--text-on-accent);
  border: none;
  border-radius: var(--radius-md);
  padding: 17px 34px;
  min-height: 44px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: clamp(16px, 1.7vw, 18px);
  cursor: pointer;
  text-decoration: none;
  box-shadow: var(--fx-glow, 0 0 44px rgba(0, 148, 255, 0.42));
  transition: background var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.btn:hover { background: var(--blue-800); box-shadow: var(--fx-glow-hi, 0 0 68px rgba(0, 148, 255, 0.62)); transform: translateY(-2px); }
.btn:active { background: var(--blue-900); transform: none; }
a.btn { border-bottom: none; }
a.btn:hover { color: var(--text-on-accent); border-bottom: none; }
.btn--block { width: 100%; padding: 17px 24px; }
.btn__tri { width: 0; height: 0; border-left: 9px solid currentColor; border-top: 6px solid transparent; border-bottom: 6px solid transparent; }

/* =========================================================================
   5. THE VIDEO FACADE
   Facade, not an embed: poster and play button are plain HTML; the
   third-party iframe is injected only on click. Nothing is requested from
   YouTube or Vimeo until someone asks for the video. On the landing page the
   click opens the opt-in form instead — that is the gate.

   Sized by WIDTH against the height left below the copy. aspect-ratio does
   the rest, which is what keeps 16:9 EXACT — a max-height on an
   aspect-ratio box overrides the ratio instead of scaling the box.
   ========================================================================= */
.video {
  position: relative;
  aspect-ratio: 16 / 9;
  width: clamp(320px, calc((100svh - var(--reserve, 470px)) * 16 / 9), var(--video-max, 900px));
  max-width: 100%;
  background:
    linear-gradient(180deg, var(--fx-card-wash, rgba(0, 148, 255, 0.1)) 0%, transparent 55%),
    var(--n-850);
  border: var(--bw-base) solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: grid;
  place-items: center;
  padding: 20px;
  font: inherit;
  color: inherit;
  text-align: center;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  box-shadow: var(--fx-glow-soft, 0 8px 30px -12px rgba(0, 30, 60, 0.25));
  transition: border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.video:hover { border-color: var(--fx-card-edge, rgba(0, 148, 255, 0.55)); box-shadow: var(--fx-glow, 0 0 44px rgba(0, 148, 255, 0.42)); }
/* Stacks the in-flow children in one cell. The poster is EXCLUDED on purpose:
   for an absolutely positioned grid child, a definite grid-area becomes its
   containing block, so inset:0 would pin it to the content box and leave the
   frame's 20px padding showing as a bare border on three sides. Excluded, its
   containing block is the padding box, which is what covering the frame needs. */
.video > *:not(.video__poster) { grid-area: 1 / 1; }
.video__inner { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; gap: 14px; }

/* ABSOLUTE, not a grid cell. Two things were stopping the poster filling the
   frame: .video is a grid with place-items:center, which sizes children to
   their CONTENT rather than stretching them, and the frame carries 20px of
   padding, which a grid cell sits inside. Together they left a 21px bare
   border on three sides.

   Taking the poster out of flow and pinning it to inset:0 covers the whole
   box, and lets object-fit:cover crop the letterbox bars YouTube bakes into
   the thumbnail of a recording that is not exactly 16:9.

   z-index 0 keeps it under the ::after scrim (1) and the play button (2). */
.video__poster {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.video::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--spc-black);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out);
  pointer-events: none;
  z-index: 1;
}
.video.has-poster::after { opacity: 0.5; }
.video.has-poster:hover::after { opacity: 0.38; }

.video__play {
  width: clamp(64px, 7vw, 88px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--accent);
  display: grid;
  place-items: center;
  box-shadow: var(--fx-glow, 0 0 44px rgba(0, 148, 255, 0.42));
  transition: transform var(--dur-base) var(--ease-out), background var(--dur-base) var(--ease-out);
}
.video:hover .video__play { transform: scale(1.06); background: var(--blue-600); }
/* CSS primitive on purpose — this brand ships no icon set and the handoff is
   explicit: no Lucide, no Heroicons, no emoji. */
.video__play::before {
  content: "";
  width: 0; height: 0;
  border-left: 26px solid var(--spc-white);
  border-top: 16px solid transparent;
  border-bottom: 16px solid transparent;
  margin-left: 6px;
}
.video__label { font-size: var(--fs-mono); margin: 0; }
.video__note { font-size: 11px; color: var(--text-muted); margin: 0; }

.video--playing { display: block; padding: 0; cursor: default; }
.video--playing::after { display: none; }
.video iframe { width: 100%; height: 100%; border: 0; display: block; }

/* Floating plates over the video. Content is the existing details bar. */
.plate {
  position: absolute;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  font-size: var(--fs-mono);
  color: var(--text-muted);
  white-space: nowrap;
  margin: 0;
}
.plate b { display: block; color: var(--text-strong); font-size: 15px; font-family: var(--font-body); letter-spacing: 0; text-transform: none; }
.plate--tr { top: -14px; right: -14px; }
.plate--bl { bottom: -14px; left: -14px; }

/* =========================================================================
   6. HERO — cinematic centred
   ========================================================================= */
.hero {
  text-align: center;
  padding-block: clamp(24px, 3.4svh, 52px) clamp(32px, 5svh, 72px);
  /* --reserve is the MEASURED height of everything above the video plus a
     little breathing room, not a guess. Measured at 533-540px across
     viewports, so 556 leaves ~16px of clearance. Setting it higher "to be
     safe" is not safe: it shrinks the video on every screen for no reason. */
  --reserve: 556px;
  --video-max: 900px;
}
.hero .pill { margin-inline: auto; }
/* This headline is ~140 characters. A narrow measure at display size turns it
   into a ten-line column that eats the viewport, so the measure stays wide
   even though the type is big. */
.hero .h1 { max-width: 1080px; margin: 20px auto 0; }
.hero-rule { width: 120px; height: 7px; background: var(--accent); margin: 26px auto 0; border-radius: 4px; box-shadow: var(--fx-glow, 0 0 44px rgba(0, 148, 255, 0.42)); }
.hero .lede { margin: 20px auto 0; max-width: 62ch; color: var(--text-body); }
.hero__media { position: relative; width: fit-content; max-width: 100%; margin: clamp(20px, 3svh, 40px) auto 0; }
.hero__cta { margin-top: 28px; }

/* The scroll cue. The page is long; this says so. */
.cue { display: block; text-align: center; margin-top: 26px; font-size: var(--fs-mono); color: var(--text-muted); text-decoration: none; border-bottom: 0; }
.cue:hover { color: var(--accent-text); border-bottom: 0; }
.cue span { display: block; margin: 8px auto 0; width: 1px; height: 26px; background: linear-gradient(var(--accent), transparent); }

/* =========================================================================
   THE NEXT-STEP CUE — thank-you page only.

   Without it, everything under the video is invisible to anyone who does not
   scroll unprompted, and the discovery-call CTA measured 519px below the fold.
   So this is not decoration: it is the only thing on the first screen that
   says the page continues.

   It is a LINK, not a graphic. Clicking it jumps to the bridge card, so it
   does the thing it is hinting at rather than only hinting.
   ========================================================================= */
.next-cue {
  /* flex + fit-content + auto margins, NOT inline-flex. The band is centred,
     and .strip is inline-level too, so an inline cue shares its line and lands
     beside it instead of under the chapters where it belongs. */
  display: flex;
  width: fit-content;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin: 20px auto 0;
  padding: 4px 12px 8px;
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: var(--ls-mono);
  text-transform: uppercase;
  color: var(--text-strong);
  text-decoration: none;
  border-bottom: 0;
  transition: color var(--dur-base) var(--ease-out);
}
/* Base is already white, so hover moves to the accent rather than nowhere. */
.next-cue:hover { color: var(--accent-text); border-bottom: 0; }

/* The ring. 56px, so it reads at the same scale as the play button above it
   and is unmistakably a thing you press — the earlier 18px chevron measured
   smaller than every other element in the region and got lost. */
.next-cue__ring {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  display: grid;
  place-items: center;
  color: var(--accent);
  /* Two animations on one element: the bob moves it, fxPulse (from
     effects.css, the same device as the live dot on the status pill) rings
     it. Reusing fxPulse keeps ONE vocabulary for "act on this" instead of
     inventing a second. */
  animation: nextBob 1.9s var(--ease-out) infinite,
             fxPulse 2.4s var(--ease-out) infinite;
  transition: background var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out);
}
.next-cue:hover .next-cue__ring { background: var(--accent); color: var(--text-on-accent); }

/* The chevron is two borders rotated 45deg — a CSS primitive, because this
   brand ships no icon set and the handoff prohibits introducing one. The
   -4px nudge optically centres it: a rotated square hangs low in its box. */
.next-cue__chev {
  width: 16px;
  height: 16px;
  border-right: 3px solid currentColor;
  border-bottom: 3px solid currentColor;
  transform: rotate(45deg) translate(-4px, -4px);
}

@keyframes nextBob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(5px); }
}

@media (prefers-reduced-motion: reduce) {
  /* Present and clickable, just not moving. A looping animation is precisely
     what this setting exists to stop, so the cue must not depend on motion to
     do its job — the ring and the chevron still point down. */
  .next-cue__ring { animation: none; }
}

.reassure { margin: 18px 0 0; font-size: 11px; color: var(--text-muted); }

/* =========================================================================
   7. CONTENT
   ========================================================================= */

/* Module cards, with the step number at display scale beside the copy. */
.modules { display: flex; flex-direction: column; gap: 16px; }
.module {
  display: grid;
  grid-template-columns: 92px 1fr;
  gap: 24px;
  align-items: start;
  padding: var(--card-pad);
  text-align: left;
}
.module__num { font-family: var(--font-title); font-size: clamp(48px, 5vw, 78px); line-height: 1; color: var(--accent); margin: 0; }
.module__title { margin: 0 0 10px; font-size: var(--fs-h3); }

/* Checklist */
.check { display: grid; grid-template-columns: 26px 1fr; gap: 16px; align-items: start; text-align: left; }
.check__mark {
  width: 22px; height: 22px;
  border-radius: var(--radius-sm);
  background: var(--accent);
  display: grid; place-items: center;
  margin-top: 3px;
  box-shadow: var(--fx-glow-soft, 0 8px 30px -12px rgba(0, 30, 60, 0.25));
}
.check__mark::before {
  content: "";
  width: 6px; height: 11px;
  border-right: 2.5px solid var(--spc-white);
  border-bottom: 2.5px solid var(--spc-white);
  transform: rotate(45deg);
  margin-bottom: 3px;
}

/* About Graham — portrait on a gradient plate. */
.about { display: grid; grid-template-columns: 300px 1fr; gap: 44px; align-items: center; text-align: left; }
.portrait { position: relative; }
.portrait::before {
  content: "";
  position: absolute;
  inset: -14px -14px 14px 14px;
  border-radius: var(--radius-lg);
  background: linear-gradient(140deg, var(--fx-plate, rgba(0, 148, 255, 0.28)), var(--fx-plate-2, rgba(124, 92, 255, 0.26)));
  z-index: 0;
}
.portrait img {
  position: relative;
  z-index: 1;
  aspect-ratio: 4 / 5;
  width: 100%;
  object-fit: cover;
  border-radius: var(--radius-lg);
  border: var(--bw-base) solid var(--border);
}

/* Testimonials. The figures are pulled to display scale INSIDE the existing
   quotes — they are quoted, not invented. Anonymous by client decision. */
.quote { padding: var(--card-pad); font-size: 17px; line-height: 1.55; margin: 0; text-align: left; }
.quote b { font-family: var(--font-mono); font-size: clamp(20px, 2.2vw, 26px); color: var(--accent); font-weight: 700; letter-spacing: -0.01em; }

.rail { overflow: hidden; position: relative;
        -webkit-mask-image: linear-gradient(90deg, transparent, var(--spc-black) 8%, var(--spc-black) 92%, transparent);
        mask-image: linear-gradient(90deg, transparent, var(--spc-black) 8%, var(--spc-black) 92%, transparent); }
.rail__track { display: flex; gap: 16px; width: max-content; animation: railScroll 52s linear infinite; }
.rail:hover .rail__track, .rail:focus-within .rail__track { animation-play-state: paused; }
.rail .quote { width: min(420px, 78vw); flex: none; }
@keyframes railScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* =========================================================================
   8. STICKY BAR + FOOTER
   Light and dark are equal defaults in this brand, so the toggle is a real
   control rather than a dark-mode switch.
   ========================================================================= */
.bar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px clamp(16px, 4vw, 40px);
  background: var(--fx-veil, rgba(0, 0, 0, 0.45));
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border);
}
.bar img { height: 34px; width: auto; }
.toggle {
  display: inline-flex; align-items: center; gap: 8px;
  background: none; color: var(--text-muted);
  border: var(--bw-base) solid var(--border);
  border-radius: var(--radius-pill);
  padding: 8px 16px; min-height: 40px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase;
  cursor: pointer;
  transition: color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}
.toggle:hover { color: var(--text-strong); border-color: var(--border-strong); }

.footer { display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap; padding-block: 34px; }
.footer__pillars { font-size: 15px; color: var(--text-muted); margin: 0; }
.footer__legal { font-size: 12px; color: var(--text-muted); margin: 0; }

/* =========================================================================
   9. THE OPT-IN MODAL
   Unchanged in behaviour from the accessible version already shipped: this
   only restyles it. role="dialog", aria-modal, focus trap, focus return,
   Escape and body-scroll lock all live in page.js and are NOT touched.
   ========================================================================= */
.modal[hidden] { display: none; }
.modal__overlay {
  position: fixed;
  inset: 0;
  background: rgb(0 0 0 / 0.76);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 60;
  display: flex;
  align-items: flex-start;   /* NOT centre — see below */
  justify-content: center;
  padding: 24px;
  overflow-y: auto;
  animation: spcFadeUp var(--dur-slow) var(--ease-out) both;
}
/* flex-start + max-height + overflow is a requirement, not a preference. The
   card is ~677px tall; centred without scroll it clips on a short laptop and
   the submit button becomes unreachable. */
.modal__card {
  position: relative;
  width: 460px;
  max-width: 100%;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  background:
    linear-gradient(180deg, var(--fx-card-wash, rgba(0, 148, 255, 0.1)) 0%, transparent 45%),
    var(--n-900);
  border: var(--bw-base) solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--card-pad);
  box-shadow: var(--shadow-xl), var(--fx-glow-soft, 0 8px 30px -12px rgba(0, 30, 60, 0.25));
}
.modal__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.modal__kicker { font-size: 11px; margin: 0; }
.modal__title { margin: 10px 0 0; font-size: var(--fs-h3); }
.modal__intro { margin: 12px 0 22px; font-size: 15px; line-height: 1.55; color: var(--text-muted); }
.modal__close {
  background: none;
  border: none;
  color: var(--text-muted);
  /* Explicit: a <button> does NOT inherit font-family, and left unset it
     falls back to the UA default, putting a sixth typeface on a five-face
     brand. */
  font-family: var(--font-body);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  min-width: 44px;
  min-height: 44px;
  border-radius: var(--radius-sm);
  transition: color var(--dur-base) var(--ease-out);
}
.modal__close:hover { color: var(--text-strong); }

/* The slot the GoHighLevel iframe mounts into. The dashed frame is the
   PLACEHOLDER state only and is removed once a real form is in. */
.ghl { border: var(--bw-base) dashed var(--fx-card-edge, rgba(0, 148, 255, 0.55)); border-radius: var(--radius-md); padding: 18px; }
.ghl--live { border: none; padding: 0; }
.ghl__label { font-size: 11px; margin: 0 0 16px; }
.ghl iframe { width: 100%; border: none; border-radius: var(--radius-sm); display: block; }

/* The three fields drawn at brand spec. They are a STYLING REFERENCE for
   whoever builds the real form inside GHL — the live form is in a
   cross-origin iframe this page cannot style. Disabled and aria-hidden. */
.fieldset-demo { display: flex; flex-direction: column; gap: 13px; }
.field { display: flex; flex-direction: column; gap: 7px; }
.field__label { font-size: 11px; color: var(--text-muted); }
.field__input {
  background: var(--spc-black);
  color: var(--text-strong);
  border: var(--bw-base) solid var(--border);
  border-radius: var(--radius-sm);
  padding: 13px 14px;
  font-family: var(--font-body);
  font-size: 16px;   /* 16px stops iOS Safari zooming on focus */
  width: 100%;
}
.field__input:focus { outline: none; border-color: var(--accent); box-shadow: var(--shadow-focus); }
.modal__foot { margin: 16px 0 0; font-size: 11px; color: var(--text-muted); text-align: center; }

@keyframes spcFadeUp {
  from { opacity: 0; transform: translateY(26px); }
  to { opacity: 1; transform: none; }
}

/* =========================================================================
   10. THANK-YOU PAGE
   Same shell as the lander so the funnel matches end to end.
   ========================================================================= */
/* Its own top padding, NOT the shared --band-py, which is clamp(36px,6vw,88px)
   and resolved to 77px on top of the 65px sticky bar. That combination read as
   dead space above the fold on a page whose whole job is "here is your video". */
.ty {
  /* Raised from 430 to make room for the chapter row AND the next-step cue
     BELOW the video. A cue that is itself below the fold is worse than no cue
     at all, because it is the one element whose whole job is to be seen on the
     first screen. On this page --reserve is literally "viewport height minus
     video height", so every px here is a px the video gives back.
     Raised again 485 -> 520 when the cue grew from 60px to a 56px ring. */
  --reserve: 520px;
  --video-max: 900px;
  text-align: center;
  padding-block: clamp(18px, 2.4svh, 40px) clamp(36px, 5vw, 72px);
}
.ty__pill { margin: 0 auto; }
.ty .h1 { margin: 20px auto 0; max-width: 20ch; }
.ty__intro { margin: 18px auto 0; font-size: var(--fs-lede); line-height: var(--lh-body); max-width: 62ch; }
/* max-width:100% on the WRAPPER is load-bearing, not belt-and-braces. The
   wrapper is width:fit-content so the floating plates can hang off the video's
   real corners; without a cap, fit-content adopts the video's clamp width and
   the child's own max-width:100% then resolves against that same width, so it
   never constrains anything. Measured: a 581px video on a 375px screen, 226px
   of it off-canvas and silently swallowed by .band{overflow:hidden}. Capping
   the wrapper gives the child a definite width to be a percentage OF. */
.ty__media { position: relative; width: fit-content; max-width: 100%; margin: clamp(20px, 3svh, 40px) auto 0; }

.chapters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 20px;
  justify-content: center;
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
  font-size: 12px;
}
.chapters li { color: var(--text-strong); }
.chapters a { color: var(--text-strong); border-bottom-color: transparent; min-height: 44px; display: inline-flex; align-items: center; }
.chapters a:hover { color: var(--accent); border-bottom-color: currentColor; }

.strip {
  margin: 22px auto 0;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: center;
  border-radius: var(--radius-md);
  padding: 14px 20px;
}
.strip__label { font-size: 11px; margin: 0; }
.strip__text { font-size: 15px; line-height: 1.5; color: var(--text-muted); margin: 0; }

.bridge { margin-top: clamp(32px, 5vw, 56px); padding: clamp(24px, 3vw, 40px); text-align: left; }
.bridge__body { margin: 16px 0 22px; font-size: var(--fs-body); line-height: var(--lh-body); max-width: 62ch; }
.bridge__pillars { font-size: 15px; color: var(--text-muted); margin: 0 0 22px; }

/* =========================================================================
   11. RESPONSIVE
   ========================================================================= */
@media (max-width: 900px) {
  .about { grid-template-columns: 1fr; gap: 34px; }
  .portrait { max-width: 300px; margin-inline: auto; }
  /* The plates stop floating and stack under the video, or they overlap the
     frame on a narrow screen. */
  .plate { position: static; display: inline-flex; margin: 10px 6px 0 0; }
}
@media (max-width: 768px) {
  :root { --gutter: 20px; }
  /* The headline wraps to far more lines on a phone, so more of the viewport
     is spent before the video. Its own measured reserve, not the desktop one. */
  .hero { --reserve: 620px; }
  .btn { width: 100%; }
  .module { grid-template-columns: 1fr; gap: 6px; }
  .module__num { font-size: 40px; }
  .modal__overlay { padding: 12px; }
  .modal__card { max-height: calc(100vh - 24px); padding: 20px; }
  .footer { justify-content: center; text-align: center; }
  .bridge { text-align: left; }
}
@media (max-width: 560px) {
  .strip { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* =========================================================================
   12. REDUCED MOTION
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .rail__track { animation: none; }
  .btn:hover, .video:hover .video__play { transform: none; }
}
