/* ============================================================================
   Beensock — one stylesheet for the whole site.

   Design notes, so future-you knows what the rules were:
   - Palette is a deep desaturated blue-slate, never pure black. Body text is
     a warm off-white, never #fff. There is exactly ONE accent (warm brass)
     and it is used sparingly.
   - The signature element is field-line geometry: thin chalk strokes that
     echo a Rocket League pitch. Section dividers, the hero framing and the
     quiz progress scrubber all speak that one language. Everything else is
     kept deliberately quiet.
   - Mobile-first. Nothing here assumes a viewport wider than 360px.
   ========================================================================= */

/* ---------- 1. Tokens ---------------------------------------------------- */

:root {
  /* Surfaces */
  --bg:         #141A22;
  --bg-raise:   #1B242F;
  --bg-sunk:    #10151C;

  /* Lines */
  --line:       #2A3542;
  --line-soft:  #222C38;
  --chalk:      rgba(236, 231, 222, 0.16);
  --chalk-dim:  rgba(236, 231, 222, 0.07);

  /* Ink */
  --text:       #ECE7DE;
  --text-dim:   #A3AEBC;
  --text-faint: #7C8798;

  /* The one accent */
  --accent:     #E0A45C;
  --accent-dim: #B57F3F;
  --accent-ink: #1A1206;

  --ok:         #7FB08A;

  /* Type */
  --display: "Archivo", "Segoe UI", system-ui, sans-serif;
  --body:    "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;

  /* Type scale — 1.25 minor third, clamped for mobile */
  --t-xs:  0.78rem;
  --t-sm:  0.875rem;
  --t-base: 1rem;
  --t-md:  1.125rem;
  --t-lg:  clamp(1.25rem, 1.05rem + 0.9vw, 1.5rem);
  --t-xl:  clamp(1.5rem, 1.2rem + 1.5vw, 2rem);
  --t-2xl: clamp(1.9rem, 1.4rem + 2.4vw, 2.75rem);
  --t-3xl: clamp(2.3rem, 1.5rem + 3.6vw, 3.6rem);

  /* Spacing */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --sp-8: 4rem;
  --sp-9: 6rem;

  --radius:    10px;
  --radius-sm: 6px;

  --wrap:   64rem;
  --measure: 38rem;
}

/* ---------- 2. Reset ----------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

/* The `hidden` attribute must always win. The UA rule behind it is
   `[hidden] { display: none }` at the lowest possible specificity, so any
   ordinary class that sets `display` (`.grid .card { display: flex }`, for
   one) silently defeats it — which would leave a feature-flagged block on
   screen after site.js had "hidden" it. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--body);
  font-size: var(--t-base);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.015em;
  margin: 0 0 var(--sp-4);
  text-wrap: balance;
}

h1 { font-size: var(--t-3xl); }
h2 { font-size: var(--t-2xl); }
h3 { font-size: var(--t-lg); letter-spacing: -0.005em; }
h4 { font-size: var(--t-md); font-weight: 600; letter-spacing: 0; }

p { margin: 0 0 var(--sp-4); max-width: var(--measure); }
ul, ol { margin: 0 0 var(--sp-4); padding-left: 1.15rem; }
li { margin-bottom: var(--sp-2); max-width: var(--measure); }

img, svg { max-width: 100%; }
strong { font-weight: 600; color: var(--text); }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: #F0BE83; }

/* Every interactive thing gets a visible focus ring. Non-negotiable. */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 3px;
}

/* ---------- 3. Layout ---------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--sp-5);
}

.wrap--narrow { max-width: 46rem; }

.section { padding-block: var(--sp-7); }
.section--tight { padding-block: var(--sp-6); }

.stack > * + * { margin-top: var(--sp-4); }

@media (min-width: 48rem) {
  .wrap { padding-inline: var(--sp-6); }
  .section { padding-block: var(--sp-9); }
}

.eyebrow {
  font-family: var(--display);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--sp-3);
}

.eyebrow--quiet { color: var(--text-faint); }

.lede {
  font-size: var(--t-md);
  color: var(--text-dim);
  max-width: var(--measure);
}

.muted { color: var(--text-dim); }
.faint { color: var(--text-faint); font-size: var(--t-sm); }
.nowrap { white-space: nowrap; }
.center { text-align: center; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- 4. Header / footer ------------------------------------------- */

.site-head {
  border-bottom: 1px solid var(--line-soft);
  padding-block: var(--sp-4);
}

/* THREE CLUSTERS: wordmark left, social group CENTRED, nav right. Michael,
   2026-08-08, looking at the v1 screenshots: "You see how it's centered to the
   right on the desktop one... I feel like centered is a little cleaner."

   The centring is real, not "centred in the leftover space". The two 1fr
   tracks always take an equal share of the free space, so the middle track is
   centred in the header's own content box regardless of how wide the wordmark
   or the nav happen to be — and they differ a lot per page: the nav is 192px
   on the home page, 109px on /coaching/, 67px on /quiz/, and next-live and
   next-replays have NO nav element at all. Under the old right-aligned flex
   the group therefore landed in a different place on every page (measured
   2026-08-08: 48px, 131px, 173px and 256px right of centre). Now it lands in
   the same place on all six.

   1fr, not minmax(0, 1fr): if the side content ever outgrew its share the
   group would drift off-centre, which is visible and honest, rather than
   silently overlapping the nav. VERIFIED 2026-08-08 that it does not drift
   anywhere between 1440px and 768px — above 1024px the .wrap is capped at
   64rem so the share is a constant 233.6px against a 192px worst-case nav,
   and below 1024px the labels come off and the group shrinks to ~174px, which
   only widens the share. */
.site-head__inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--sp-4);
}

.wordmark {
  /* Grid items are blockified, so without this the wordmark would stretch
     across its whole 1fr track and its click target would reach halfway to
     the chips. Same reason for justify-self: end on .site-head__nav below. */
  justify-self: start;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--t-md);
  letter-spacing: -0.01em;
  color: var(--text);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  min-height: 44px;
}

.wordmark:hover { color: var(--text); }

/* The mark is a goal-mouth: two posts and a crossbar, drawn as chalk. */
.wordmark__mark { width: 22px; height: 22px; flex: none; color: var(--accent); }

.site-head__nav {
  justify-self: end;  /* see .wordmark above */
  display: flex;
  gap: var(--sp-4);
  font-size: var(--t-sm);
}

.site-head__nav a {
  color: var(--text-dim);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.site-head__nav a:hover { color: var(--text); }

.site-foot {
  border-top: 1px solid var(--line-soft);
  margin-top: var(--sp-7);
  padding-block: var(--sp-6) var(--sp-7);
  color: var(--text-faint);
  font-size: var(--t-sm);
}

.site-foot__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
  margin-bottom: var(--sp-4);
}

.site-foot__links a {
  color: var(--text-dim);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.site-foot__links a:hover { color: var(--text); }

/* ---------- 4b. Social chips (header) ------------------------------------ */

/* Twitch, Discord, TikTok, Instagram, added 2026-08-07 by job
   20260807-website-social-header-v1. YouTube is deliberately not among them
   and stays in the footer — Michael's call.

   Polished 2026-08-08 by job 20260808-website-social-header-polish-v1: equal
   widths, a real fill on all four, and the group centred on desktop.

   THE RULE THAT CONSTRAINS THIS WHOLE BLOCK, UNCHANGED: the amber CTA is the
   conversion path and must remain the loudest element on the page. Four
   saturated brand colours in the header can beat it, so every chip shares one
   quiet dark base — var(--bg-sunk) — and the brand reaches it only through the
   ~18px glyph, a hairline border, and a wash whose alpha stays in the 0.07–0.13
   band. Do NOT fill a chip with a saturated brand colour. Dial the chips down,
   never the CTA up.

   The marks themselves are official and untouched — no recolour, no rotation,
   no distortion. All four companies' brand guidelines require that, and the
   treatment below colours the chip, never the mark's geometry. */

/* ALL FOUR CHIPS ARE THE SAME WIDTH. Michael, 2026-08-08: "I kinda don't like
   how the Discord button is, like, shorter than the rest of them even though I
   know the word is shorter." Measured before the fix: three chips at 109.20px
   and Discord at 96.44px, because "Discord" is 12.76px narrower than
   "beensock".

   THE WIDTH IS DRIVEN BY THE WIDEST REAL LABEL, NOT BY A NUMBER TYPED HERE.
   grid-auto-columns: 1fr under a max-content constraint resolves every column
   to the largest column's max-content contribution, so the shared width IS
   whatever the longest label needs and nothing has to be re-measured if a
   label ever changes. No min-width, no magic constant.

   ⚠ The labels are NOT the lever here and must not become one. "Discord" is
   correct: that link is a SERVER INVITE, not a profile, so a username would be
   wrong. Uniform width removes the problem without inventing text. */
.social {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  width: max-content;
  align-items: center;
  gap: var(--sp-2);
}

.social__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  min-height: 40px;
  padding-inline: 0.6rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  background: var(--bg-sunk);
  color: var(--text);
  font-family: var(--display);
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1;
  text-decoration: none;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.social__link:hover { color: var(--text); }

.social__icon { width: 18px; height: 18px; flex: none; display: block; }

.social__label { white-space: nowrap; }

/* ALL FOUR CHIPS CARRY A REAL FILL. Michael, 2026-08-08: "Can we make the
   background for the Twitch one a little bit cooler too? Because the TikTok
   one is black with the logo, the Instagram one's got this really nice
   gradient, but then the Discord and the Twitch just have some really boring
   things."

   ⚠ INSTAGRAM IS THE REFERENCE AND IS NOT TOUCHED — "the Instagram button is
   tough, I like what they made." These two match it in WEIGHT, not in hue: the
   same shape of rule (a low-alpha 45deg wash of the brand's own ramp over the
   same var(--bg-sunk) base), the same alpha band (0.07–0.13 against
   Instagram's 0.10–0.12), and the same roughly-double lift on hover.

   This is also the fix for the height complaint. All four chips are and always
   were exactly 40px tall — VERIFIED 2026-08-08, computed heights 40.000px with
   identical tops and bottoms on all six pages at every width. What Michael saw
   as "the Instagram button is slightly taller" was Instagram and TikTok
   reading as filled BLOCKS while Twitch and Discord read as empty OUTLINES.
   Filling all four removes the illusion; there was never a box to resize.

   The amber CTA still has to win. Four filled chips can beat it, so these stay
   in the same quiet band as Instagram. Dial these down, never the CTA up. */
.social__link--twitch {
  border-color: rgba(145, 70, 255, 0.30);
  background:
    linear-gradient(45deg,
      rgba(119, 44, 232, 0.07) 0%,
      rgba(145, 70, 255, 0.12) 60%,
      rgba(184, 138, 255, 0.13) 100%),
    var(--bg-sunk);
}
.social__link--twitch:hover {
  border-color: rgba(145, 70, 255, 0.65);
  background:
    linear-gradient(45deg,
      rgba(119, 44, 232, 0.14) 0%,
      rgba(145, 70, 255, 0.22) 60%,
      rgba(184, 138, 255, 0.24) 100%),
    var(--bg-sunk);
}

.social__link--discord {
  border-color: rgba(88, 101, 242, 0.30);
  background:
    linear-gradient(45deg,
      rgba(64, 78, 237, 0.07) 0%,
      rgba(88, 101, 242, 0.12) 60%,
      rgba(122, 132, 247, 0.13) 100%),
    var(--bg-sunk);
}
.social__link--discord:hover {
  border-color: rgba(88, 101, 242, 0.65);
  background:
    linear-gradient(45deg,
      rgba(64, 78, 237, 0.14) 0%,
      rgba(88, 101, 242, 0.22) 60%,
      rgba(122, 132, 247, 0.24) 100%),
    var(--bg-sunk);
}

/* Black, because that is what Michael asked for and what the mark is drawn
   against everywhere else. It sits a shade under --bg-sunk, so next to the
   other three it reads as intentional rather than inconsistent. */
.social__link--tiktok  { border-color: rgba(254, 44, 85, 0.28); background: #000; }
.social__link--tiktok:hover {
  border-color: rgba(37, 244, 238, 0.60);
  background: #000;
}

/* "The background can be a solid-ish color, but maybe a little bit of
   gradient could be cool" — a 10% wash of the brand ramp over the same dark
   base as the others. Solid-ish, with a little bit of gradient. */
.social__link--instagram {
  border-color: rgba(185, 0, 180, 0.32);
  background:
    linear-gradient(45deg,
      rgba(255, 213, 33, 0.10) 0%,
      rgba(245, 0, 0, 0.10) 45%,
      rgba(185, 0, 180, 0.12) 70%,
      rgba(76, 104, 215, 0.12) 100%),
    var(--bg-sunk);
}
.social__link--instagram:hover {
  border-color: rgba(185, 0, 180, 0.70);
  background:
    linear-gradient(45deg,
      rgba(255, 213, 33, 0.20) 0%,
      rgba(245, 0, 0, 0.20) 45%,
      rgba(185, 0, 180, 0.22) 70%,
      rgba(76, 104, 215, 0.22) 100%),
    var(--bg-sunk);
}

/* site.js applies this to the Discord chip if CONFIG.discordInvite goes UNSET,
   the same way it does to a buy button. Without this rule the chip would look
   live while doing nothing. */
.social__link[aria-disabled="true"] {
  opacity: 0.45;
  border-style: dashed;
  pointer-events: none;
}

/* ── Twitch live state — NOT BUILT IN THIS JOB ───────────────────────────────
   ⚠ THE PLAN THIS SLOT ORIGINALLY DESCRIBED IS SUPERSEDED. It said the
   follow-on would swap the Twitch chip's LABEL to "Live now", and sized the
   slot on the claim that "'beensock' and 'Live now' are the same length".
   That is no longer what Michael wants. Michael, 2026-08-08:

     "What I would wanna do is actually just have a red flashing, you know,
      recording button, and then the word 'live' and, like, have it to the left
      of my Twitch thing."

   So the real plan is a SEPARATE PILL to the LEFT of the Twitch chip — a red
   pulsing dot plus the word "live" — and THE TWITCH CHIP KEEPS SAYING
   "beensock" THROUGHOUT. Nothing swaps. That is also why Twitch is the
   leftmost chip: the pill needs the left edge of the group to appear at.

   The label-swap rules that implemented the old plan have been REMOVED rather
   than left to rot, because a dormant rule that contradicts the live plan is
   worse than no rule. What remains is the chip emphasis, which is true under
   either design.

   Reading the real state still needs a Cloudflare Pages Function holding
   Twitch API credentials — a separate, separately-authorized follow-on (no job
   id assigned as of 2026-08-08). NOTHING IN THIS REPOSITORY SETS
   data-twitch-state="live", so nothing here can claim he is live without a
   real answer.

   FOR WHOEVER BUILDS THE PILL: the group is centred by .site-head__inner's
   middle grid track, which is auto-sized, so a fifth element simply widens
   that track and the group stays centred — no rule here needs changing. But
   the headroom is thin. MEASURED 2026-08-08 at 1280px: the two side tracks get
   233.6px each against a 191.9px worst-case nav on the home page. A pill of
   more than ~78px (including its gap) pushes the side share below the nav's
   width and the centring starts to drift. If the pill needs to be wider than
   that, move the label-hiding breakpoint up from 63.99rem rather than letting
   it drift. */
.social__link--twitch[data-twitch-state="live"] {
  border-color: rgba(145, 70, 255, 0.75);
  background:
    linear-gradient(45deg,
      rgba(119, 44, 232, 0.16) 0%,
      rgba(145, 70, 255, 0.26) 60%,
      rgba(184, 138, 255, 0.28) 100%),
    var(--bg-sunk);
}

/* Below 64rem the labels come off and the glyphs carry the chips. Four
   labelled chips plus the nav do not fit, and every link keeps its
   aria-label, so the accessible name survives the label going away. */
@media (max-width: 63.99rem) {
  .social__label { display: none; }
  .social__link { padding-inline: 0.55rem; }
}

/* Below 48rem the chips take their own row under the wordmark and nav. Most
   of the traffic arrives from TikTok, which means phones, so the top row is
   left exactly as it was and the chips are added beneath it rather than
   squeezed in beside it. A deliberate second row, not a ragged wrap.

   ⚠ MOBILE LAYOUT IS DELIBERATELY UNCHANGED by the 2026-08-08 polish pass —
   Michael did not complain about it, and only the chip fills carry down here.
   The desktop three-cluster GRID IS UNDONE and the original flex layout is
   restored verbatim, rather than re-expressed in grid terms.

   ⚠ THAT IS NOT A STYLE PREFERENCE — a grid version was built, measured and
   REJECTED on 2026-08-08. Two auto columns force the wordmark and the nav to
   share one row, and at 360px the home page's nav does not fit beside the
   wordmark, so it shrank to min-content and broke MID-PHRASE: "Find your /
   session", "All / options". flex-wrap does the right thing instead — it moves
   the whole nav to its own row and the header becomes three rows. Ragged wrap
   at 360 is a stop condition for this header; keep the flex.

   grid-auto-columns also goes back to max-content, because the desktop 1fr
   would stretch four chips across the whole phone width. VERIFIED 2026-08-08
   at 430, 375 and 360, on all six pages: geometry identical to before this
   job, chips 42.38 x 44.00, group flush left. */
@media (max-width: 47.99rem) {
  .site-head__inner {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    row-gap: var(--sp-3);
  }
  .social {
    order: 3;
    width: 100%;
    grid-auto-columns: max-content;
    justify-content: start;
  }
  .social__link { min-height: 44px; padding-inline: 0.7rem; }
}

/* ---------- 5. Field-line geometry — the signature ----------------------- */

/* A section divider drawn as the halfway line of a pitch. */
.fieldline {
  display: block;
  width: 100%;
  height: 34px;
  margin-block: var(--sp-6);
  color: var(--chalk);
  overflow: visible;
}

.fieldline line,
.fieldline circle,
.fieldline rect,
.fieldline polyline,
.fieldline path {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  vector-effect: non-scaling-stroke;
}

.fieldline__pip { fill: currentColor; stroke: none; }

/* The hero sits inside a faint goal box. */
.pitch {
  position: relative;
  isolation: isolate;
}

.pitch__lines {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  color: var(--chalk-dim);
  pointer-events: none;
}

.pitch__lines line,
.pitch__lines path,
.pitch__lines rect,
.pitch__lines circle {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  vector-effect: non-scaling-stroke;
}

/* ---------- 6. Buttons --------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: 52px;
  padding: 0.85rem 1.4rem;
  font-family: var(--display);
  font-size: var(--t-base);
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  background: none;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.btn svg { width: 18px; height: 18px; flex: none; }

.btn--primary {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: var(--accent);
}

.btn--primary:hover { background: #EDB471; border-color: #EDB471; color: var(--accent-ink); }

.btn--secondary {
  border-color: var(--line);
  color: var(--text);
  background: var(--bg-raise);
}

.btn--secondary:hover { border-color: var(--text-faint); color: var(--text); }

.btn--ghost {
  border-color: var(--line);
  color: var(--text-dim);
}

.btn--ghost:hover { border-color: var(--text-faint); color: var(--text); }

.btn--block { display: flex; width: 100%; }

.btn--lg { min-height: 58px; font-size: var(--t-md); padding-inline: 1.75rem; }

/* Config guard state. Applied by site.js when a Payment Link is UNSET.
   It must look obviously broken to Michael and obviously inert to a buyer. */
.btn.is-disabled,
.btn[aria-disabled="true"] {
  background: var(--bg-raise);
  color: var(--text-faint);
  border-color: var(--line);
  border-style: dashed;
  cursor: not-allowed;
  pointer-events: none;
}

.btn.is-disabled:hover { background: var(--bg-raise); color: var(--text-faint); }

.btn-note {
  font-size: var(--t-xs);
  color: var(--text-faint);
  margin-top: var(--sp-2);
}

/* A low-emphasis text link. Used for the quiz escape hatch — this must never
   read as a second call to action. */
.escape {
  display: inline-block;
  font-size: var(--t-sm);
  color: var(--text-faint);
  text-decoration: underline;
  text-decoration-color: var(--line);
  padding-block: 0.75rem;
}

.escape:hover { color: var(--text-dim); text-decoration-color: var(--text-faint); }

/* ---------- 7. Cards ----------------------------------------------------- */

.card {
  background: var(--bg-raise);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: var(--sp-5);
}

.card > :last-child { margin-bottom: 0; }

.card__price {
  font-family: var(--display);
  font-size: var(--t-xl);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
  display: block;
}

.card__unit {
  font-family: var(--body);
  font-size: var(--t-sm);
  font-weight: 400;
  color: var(--text-dim);
  letter-spacing: 0;
}

.grid {
  display: grid;
  gap: var(--sp-4);
}

/* Cards in a grid line their buy buttons up along the bottom. */
.grid .card { display: flex; flex-direction: column; }
.grid .card .btn { margin-top: auto; }

/* Applied by site.js when a feature flag has hidden all but one card in a
   grid, so a dormant product doesn't leave a half-empty row behind. The
   two-class selector outranks the .grid--2/.grid--3 media-query rules. */
.grid.grid--solo { grid-template-columns: minmax(0, 1fr); }

@media (min-width: 40rem) {
  .grid--2 { grid-template-columns: repeat(2, 1fr); }
  .grid--3 { grid-template-columns: repeat(3, 1fr); }
}

/* ---------- 8. Steps (how it works) -------------------------------------- */

.steps { list-style: none; padding: 0; margin: 0; counter-reset: step; }

.steps li {
  counter-increment: step;
  position: relative;
  padding-left: 3rem;
  padding-bottom: var(--sp-5);
  max-width: var(--measure);
  margin: 0;
}

.steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0;
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  font-family: var(--display);
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--accent);
  border: 1px solid var(--line);
  border-radius: 50%;
}

/* the chalk line joining the steps */
.steps li:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 1rem;
  top: 2.25rem;
  bottom: 0.25rem;
  width: 1px;
  background: var(--line-soft);
}

.steps li:last-child { padding-bottom: 0; }

/* ---------- 9. Callouts -------------------------------------------------- */

.callout {
  border-left: 2px solid var(--accent-dim);
  background: var(--bg-raise);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: var(--sp-4) var(--sp-5);
  margin-bottom: var(--sp-5);
}

.callout > :last-child { margin-bottom: 0; }

.callout--plain { border-left-color: var(--line); }

.callout__title {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--t-sm);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 0 0 var(--sp-2);
}

.clock-note {
  font-weight: 600;
  color: var(--text);
}

/* ---------- 10. Quiz ----------------------------------------------------- */

.quiz {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.quiz__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: var(--sp-6);
}

/* Progress: a replay scrubber. Same chalk vocabulary as the field lines. */
.scrub {
  padding-block: var(--sp-4);
}

.scrub__meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-family: var(--display);
  font-weight: 600;
  color: var(--text-faint);
  margin-bottom: var(--sp-2);
}

.scrub__track {
  position: relative;
  height: 14px;
}

.scrub__rail {
  position: absolute;
  left: 0; right: 0; top: 6px;
  height: 2px;
  background: var(--line);
  border-radius: 2px;
}

.scrub__fill {
  position: absolute;
  left: 0; top: 6px;
  height: 2px;
  width: 0;
  background: var(--accent);
  border-radius: 2px;
  transition: width 0.25s ease;
}

.scrub__ticks {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.scrub__tick {
  width: 1px;
  height: 14px;
  background: var(--line);
}

.scrub__head {
  position: absolute;
  top: 0;
  width: 2px;
  height: 14px;
  background: var(--accent);
  border-radius: 1px;
  transform: translateX(-1px);
  transition: left 0.25s ease;
}

.quiz__q {
  font-size: var(--t-xl);
  margin-bottom: var(--sp-2);
}

.quiz__help {
  color: var(--text-dim);
  font-size: var(--t-sm);
  margin-bottom: var(--sp-5);
}

.options {
  display: grid;
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
}

@media (min-width: 34rem) {
  .options--split { grid-template-columns: repeat(2, 1fr); }
}

.option {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-1);
  width: 100%;
  min-height: 56px;
  padding: var(--sp-4);
  text-align: left;
  background: var(--bg-raise);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--display);
  font-size: var(--t-base);
  font-weight: 600;
  line-height: 1.35;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.option:hover { border-color: var(--accent-dim); }

.option__label { display: block; }

.option__sub {
  font-family: var(--body);
  font-size: var(--t-sm);
  font-weight: 400;
  color: var(--text-dim);
  line-height: 1.5;
}

.option[aria-pressed="true"],
.option.is-selected {
  border-color: var(--accent);
  background: rgba(224, 164, 92, 0.08);
}

.option[aria-pressed="true"]::after,
.option.is-selected::after {
  content: "";
  position: absolute;
}

.option--check {
  position: relative;
  padding-right: 3rem;
}

.option--check .option__box {
  position: absolute;
  right: var(--sp-4);
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  border: 1px solid var(--line);
  border-radius: 4px;
  display: grid;
  place-items: center;
  color: transparent;
}

.option--check[aria-pressed="true"] .option__box {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-ink);
}

.option--check .option__box svg { width: 12px; height: 12px; }

/* Progressive cards: a follow-up revealed on the same question card. */
.quiz__reveal:empty { display: none; }

.quiz__sub {
  margin-top: var(--sp-5);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line-soft);
}

.quiz__subq {
  font-family: var(--display);
  font-size: var(--t-md);
  font-weight: 600;
  letter-spacing: -0.005em;
  margin: 0 0 var(--sp-2);
}

.quiz__sub .quiz__help { margin-bottom: 0; }
.quiz__reveal .options { margin-top: var(--sp-3); }

.quiz__extra:empty { display: none; }
.quiz__extra { margin-bottom: var(--sp-5); }

/* Rank grid: eight normal ranks, then two wider special options centred. */
.options--ranks { grid-template-columns: repeat(2, 1fr); }

@media (min-width: 34rem) {
  .options--ranks { grid-template-columns: repeat(4, 1fr); }
}

.options--special {
  margin-top: var(--sp-3);
}

@media (min-width: 34rem) {
  .options--special {
    grid-template-columns: repeat(2, minmax(0, 15rem));
    justify-content: center;
  }
}

.options--tiers {
  grid-template-columns: repeat(3, 1fr);
  max-width: 18rem;
}

.options--tiers .option {
  align-items: center;
  justify-content: center;
  font-size: var(--t-md);
}

/* The one free-text field in the quiz. */
.field__label {
  display: block;
  font-size: var(--t-sm);
  color: var(--text-dim);
  margin-bottom: var(--sp-2);
}

.field {
  width: 100%;
  min-height: 52px;
  padding: 0.75rem 1rem;
  font-family: var(--body);
  font-size: var(--t-base);
  color: var(--text);
  background: var(--bg-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.field::placeholder { color: var(--text-faint); }

.field:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-color: var(--accent);
}

/* Continue button before the minimum selection is met. */
.btn.is-waiting {
  background: var(--bg-raise);
  color: var(--text-faint);
  border-color: var(--line);
  border-style: dashed;
  cursor: not-allowed;
}

.quiz__nav {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
  padding-block: var(--sp-4);
}

.quiz__back {
  background: none;
  border: 0;
  color: var(--text-faint);
  font-family: var(--body);
  font-size: var(--t-sm);
  cursor: pointer;
  padding: 0.75rem 0;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.quiz__back:hover { color: var(--text-dim); }
.quiz__back[hidden] { display: none; }

/* Cross-fade between questions. This is the only motion on the site. */
.fadeable { animation: fade-in 0.22s ease both; }

@keyframes fade-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

/* ---------- 11. Result --------------------------------------------------- */

.result__name {
  font-size: var(--t-2xl);
  margin-bottom: var(--sp-1);
}

.result__spec {
  font-family: var(--display);
  font-size: var(--t-lg);
  font-weight: 600;
  color: var(--accent);
  letter-spacing: -0.01em;
  margin-bottom: var(--sp-5);
}

.includes {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--sp-5);
  border-top: 1px solid var(--line-soft);
}

.includes li {
  margin: 0;
  padding: var(--sp-4) 0;
  border-bottom: 1px solid var(--line-soft);
  max-width: none;
  display: grid;
  gap: var(--sp-1);
}

.includes__label {
  font-family: var(--display);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.includes__body { color: var(--text); }

.includes li.is-excluded .includes__body { color: var(--text-faint); }

/* The 2–3 personalised reasons under the core "why" copy. */
.reasons {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--sp-5);
}

.reasons li {
  position: relative;
  padding-left: 1.25rem;
  color: var(--text);
  margin-bottom: var(--sp-2);
}

.reasons li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7em;
  width: 8px;
  height: 1px;
  background: var(--accent);
}

/* "What a first session covers" — flexible list, not a time allocation. */
.ticks {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--sp-3);
}

.ticks li {
  position: relative;
  padding-left: 1.6rem;
  margin-bottom: var(--sp-2);
  color: var(--text);
}

.ticks li::before {
  content: "";
  position: absolute;
  left: 0.15rem;
  top: 0.45em;
  width: 9px;
  height: 5px;
  border-left: 1.5px solid var(--accent);
  border-bottom: 1.5px solid var(--accent);
  transform: rotate(-45deg);
}

.buy {
  margin-block: var(--sp-5);
}

.upsell {
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  padding: var(--sp-5);
  margin-bottom: var(--sp-5);
}

.upsell > :last-child { margin-bottom: 0; }

/* ---------- 12. Handoff string ------------------------------------------- */

.handoff {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-4);
  background: var(--bg-sunk);
  margin-bottom: var(--sp-5);
}

.handoff__row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}

.handoff__code {
  flex: 1 1 14rem;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: var(--t-sm);
  color: var(--text);
  overflow-x: auto;
  white-space: nowrap;
  padding-bottom: 2px;
}

.handoff__copy {
  flex: none;
  min-height: 44px;
  padding: 0.5rem 1rem;
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--t-sm);
  background: var(--bg-raise);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.handoff__copy:hover { border-color: var(--text-faint); }
.handoff__copy.is-done { color: var(--ok); border-color: var(--ok); }

/* Multi-line ticket summary (1:1 Coaching). */
.handoff__block {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: var(--t-xs);
  line-height: 1.7;
  color: var(--text);
  margin: 0 0 var(--sp-3);
  padding: 0;
  overflow-x: auto;
  white-space: pre;
  tab-size: 2;
}

.handoff__row--actions { justify-content: flex-start; }

.handoff__details {
  margin-top: var(--sp-3);
  border-top: 1px solid var(--line-soft);
  padding-top: var(--sp-3);
}

.handoff__details > summary {
  cursor: pointer;
  font-size: var(--t-sm);
  color: var(--text-dim);
  min-height: 44px;
  display: flex;
  align-items: center;
}

.handoff__details > summary:hover { color: var(--text); }
.handoff__details .handoff__block { margin-top: var(--sp-3); }

/* ---- Debug / calibration panel — internal, ?debug=1 only ---------------- */

.debug {
  margin-top: var(--sp-7);
  padding: var(--sp-5);
  border: 1px dashed var(--accent-dim);
  border-radius: var(--radius);
  background: var(--bg-sunk);
}

.debug__title {
  font-size: var(--t-md);
  margin-bottom: var(--sp-1);
}

.debug__note {
  font-size: var(--t-xs);
  color: var(--text-faint);
  margin-bottom: var(--sp-4);
}

.debug__h {
  font-family: var(--display);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  margin: var(--sp-4) 0 var(--sp-2);
}

.debug__pre {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: var(--t-xs);
  line-height: 1.6;
  color: var(--text-dim);
  background: var(--bg);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: var(--sp-3);
  margin: 0;
  overflow-x: auto;
  white-space: pre;
}

/* ---------- 13. Tables --------------------------------------------------- */

.table-scroll { overflow-x: auto; }

table {
  border-collapse: collapse;
  width: 100%;
  font-size: var(--t-sm);
}

th, td {
  text-align: left;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--line-soft);
  white-space: nowrap;
}

th { font-family: var(--display); font-weight: 600; color: var(--text-dim); }

/* ---------- 14. Misc ----------------------------------------------------- */

.noscript {
  border: 1px solid var(--accent-dim);
  border-radius: var(--radius-sm);
  padding: var(--sp-4);
  margin-bottom: var(--sp-5);
  color: var(--text);
}

hr {
  border: 0;
  border-top: 1px solid var(--line-soft);
  margin-block: var(--sp-6);
}

/* ---------- 15. Reduced motion ------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
