/* padelpapi.app — shared styles.

   Tokens mirror PadelPapi/Utilities/Theme.swift + BrandTokens.swift so the web
   and the app do not drift. If you change a colour or a radius there, change it
   here too.

   ONE STYLESHEET, deliberately. The brand pass was mocked up as a separate
   `brand.css`; it is folded in here instead of shipped alongside, because
   twelve <link> tags and two Cloud Function renderers already point at
   `/assets/site.css`, `tools/render-site-icons.py` names this file as the
   mirror of the Swift tokens, and a second request buys nothing on a page this
   small.

   Everything below is a port of a primitive that already exists in the app, so
   the web stops being a separate visual system:

     body::before   <- SurfaceKit.surfaceAtmosphere(.deep)     (the radial crown)
     .hero          <- SurfaceKit.GradientHeroCard             (gradient + court lines)
     .court         <- CourtLineGeometry / CourtLinesShape     (the 2:1 motif)
     .card          <- SurfaceKit.tintedCardFill / Hairline    (brand 10% / 28%)
     .kicker        <- SurfaceKit.ScreenKicker                 (AuthKit.fieldLabel ink)
     .badge         <- SurfaceKit.OnGradientBadge              (white chip, deep ink)
     .ill           <- Assets.xcassets/Illustrations           (5px stroke, round caps)
     .mark          <- Assets.xcassets/Illustrations/Mark.svg  (the mascot)

   ==================================================================
   CONTRAST RECEIPTS — the printed output of a WCAG relative-luminance
   script (`(L+0.05)` ratios, alpha-over-opaque composites) run against
   THESE colours over THESE bases, not copied from SurfaceKit.swift,
   whose bases are the app's own. Light composites are taken at the
   WORST point: the crown at the top of the page, where the glow peaks.
   Floors: 4.5:1 small text, 3:1 large text (>=18.66px bold / >=24px).

   LIGHT   crown = 11% #148C59 over #FBFDFC            -> #E2F1EA
           tinted card at the crown = +10% #148C59     -> #CDE7DB
     ink #1C2B24 on the tinted card ................ 11.30:1  PASS
     muted #4E6157 on the tinted card ...............  5.06:1  PASS
     ink-brand #0A6642 on the tinted card ...........  5.36:1  PASS
     kicker #0A6642 on the crown ....................  6.02:1  PASS
     muted #4E6157 on the plain wash ................  6.48:1  PASS
     ink-brand #0A6642 on the plain wash ............  6.87:1  PASS
     ink-brand #0A6642 on the neutral card #FFF .....  7.02:1  PASS
     muted #4E6157 on the neutral card #FFF .........  6.62:1  PASS

   DARK    crown = 24% #148C59 over #101613            -> #113224
           tinted card at the crown = +10% #52CC8C     -> #18412E
     ink #E4EFE8 on the tinted card .................  9.71:1  PASS
     muted #9DB3A7 on the tinted card ...............  5.15:1  PASS
     ink-brand #52CC8C on the tinted card ...........  5.66:1  PASS
     kicker #59A784 on the crown ....................  4.84:1  PASS
     muted #9DB3A7 on the dark wash .................  8.24:1  PASS
     ink #E4EFE8 on the neutral card #17201C ........ 14.14:1  PASS

   FOUR FAILURES FOUND BY COMPUTING, NOT BY LOOKING, AND FIXED HERE.
   1-3 were in the previous stylesheet; 4 was introduced by this pass's
   own markup and caught by re-measuring the FINAL pairs:

   1. `--muted: #5B6F64` reads 4.11:1 on the light tinted card, under the
      4.5:1 floor. Darkened to #4E6157 (5.06:1 tinted, 6.48:1 on the plain
      wash). Same hue family, one notch down. LIGHT only; dark's #9DB3A7
      already clears at 5.15:1.

   2. `--brand` as INK on a light tinted surface reads 3.26:1 — the exact
      pairing SurfaceKit.brandRowTintedCardFill refuses. So every
      brand-coloured text run (links, h2, .pill, .chip, .price, .langs)
      takes `--ink-brand`: #0A6642 in light (5.36:1 tinted, 7.02:1 on
      white), #52CC8C in dark (5.66:1 tinted). `--brand` is a FILL token
      only now.

   3. THE GRADIENT IS PINNED TO #148C59 -> #0A6642 IN BOTH MODES, which is
      DS.brandGradient. It used to be built from the per-mode tokens
      `var(--brand), var(--deep)`, so in DARK it resolved to
      #52CC8C -> #29855C and painted white on it: white on #52CC8C is
      **2.02:1**. That was a live dark-mode failure on the home hero, the
      bridge page and the coach page.

   THE GRADIENT CARRIES NO SMALL TEXT. Pinned, opaque white reads 4.26:1
   on the HIGH stop and 7.02:1 on the LOW stop — so it clears the 3:1
   LARGE floor everywhere and the 4.5:1 small floor only past t=0.119 of
   the gradient axis. Every run on it is therefore one of:
     - >=18.66px bold (h1 32/800, .hero p 19/700, .hero .kicker 19.2/700
       — all 4.26:1 at the worst point, the card's top-left corner);
     - on a white chip (.badge / .hero .btn ink #0A6642 on #FFF, 7.02:1);
     - small, but positioned BELOW the centre of a full-bleed gradient,
       where progress is >=0.5 by construction (go.html's .hint and
       .links, white at 90% over #0F794E -> 4.74:1). Nothing on a
       gradient CARD is small.

   4. `--kicker` ON A TINTED CARD, which no page reached until the coach
      page's price label (`.price .from`, 0.72rem — small text, 4.5:1
      floor). The token is receipted above only on the CROWN, where dark's
      #59A784 reads 4.84:1; on the dark tinted card it reads **3.97:1**.
      Found by re-measuring the final markup, not by looking — the light
      side passes at 5.37:1, so the failure is dark-only and the label
      looks correct in every screenshot taken in light mode. `.price .from`
      therefore takes `--ink-brand` (5.37:1 light, 5.66:1 dark), which is
      what receipt 2 already says brand-coloured text on a tint must use.
      It is the ONLY kicker-on-tint pairing in the stylesheet; verified by
      sweeping every `.card` in the twelve static pages for a `.kicker`.

   COVER SCRIM (the coach page): .cover::after is >=0.88 black at the
   bottom band, where the name and kicker sit. Over the darkest identity
   stop the composite is #030602 and over a blown-out white photo #1F1F1F;
   white reads 20.37:1 and 16.48:1, the 90% kicker 16.32:1 and 13.58:1.
   ================================================================== */

:root {
  /* Color.brand / Color.brandDeep, light mode */
  --brand: #148C59;          /* FILL only — see receipt 2 */
  --deep: #0A6642;
  --ink: #1C2B24;
  --ink-brand: #0A6642;      /* brand-coloured TEXT */
  --muted: #4E6157;          /* darkened from #5B6F64 — see receipt 1 */
  --bg: #F6FAF7;
  --card: #FFFFFF;
  --hairline: #DDE8E1;
  --kicker: #0A6642;         /* AuthKit.fieldLabel, light */

  /* the atmosphere (SurfaceKit .deep, light) */
  --atm-top: #FBFDFC;
  --atm-bot: #F2F6F4;
  --glow-peak: rgba(20, 140, 89, 0.11);
  --glow-mid: rgba(20, 140, 89, 0.025);

  /* the tinted glass card */
  --tint-fill: rgba(20, 140, 89, 0.10);
  --tint-edge: rgba(20, 140, 89, 0.28);

  /* DS.radius / radiusSmall / radiusHero */
  --r: 16px;
  --r-sm: 10px;
  --r-hero: 20px;

  /* DS.Spacing */
  --s-sm: 8px;
  --s-md: 12px;
  --s-lg: 16px;
  --s-xl: 24px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --brand: #52CC8C;
    --deep: #29855C;
    --ink: #E4EFE8;
    --ink-brand: #52CC8C;
    --muted: #9DB3A7;
    --bg: #101614;
    --card: #17201C;
    --hairline: #26332C;
    --kicker: #59A784;

    --atm-top: #101613;
    --atm-bot: #000000;
    --glow-peak: rgba(20, 140, 89, 0.24);
    --glow-mid: rgba(20, 140, 89, 0.05);

    --tint-fill: rgba(82, 204, 140, 0.10);
    --tint-edge: rgba(82, 204, 140, 0.28);
  }
}

* { box-sizing: border-box; }

/* Every page ships `viewport-fit=cover`, so the document extends under the
   status bar and the home indicator. Safari paints those safe-area bands with
   the CANVAS colour, which comes from <html> — leave it transparent and iOS
   fills them white. That was the white-band bug on the coach bridge page. */
html {
  -webkit-text-size-adjust: 100%;
  background: var(--atm-top);
}

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--ink);
  line-height: 1.6;
  /* The wash stretches over the whole document rather than being
     `background-attachment: fixed`: iOS Safari treats fixed backgrounds
     inconsistently, and a fixed wash leaves the canvas colour showing below
     the first viewport on any long page. The CROWN below is what stays fixed
     — that is the piece the app pins to the top of a screen. */
  background: linear-gradient(180deg, var(--atm-top) 0%, var(--atm-bot) 50%);
  min-height: 100dvh;
}

/* Nothing on this site may scroll the page sideways on a phone. */
img, svg { max-width: 100%; }

/* ---- THE ATMOSPHERE (SurfaceKit.surfaceAtmosphere(.deep)) --------------
   A 120% x 48% ellipse centred at (50%, -10%): peak -> mid at 55% ->
   transparent at 70%. Fixed, behind everything, never hit-testable — the
   modifier's `.allowsHitTesting(false)`.

   This is the piece the site had NO equivalent of, and it is why a translucent
   tint can be used at all: the 10% fill composites DARKER over a bare
   background. The tint without the glow behind it is a regression that reads
   as a fix in the diff. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background: radial-gradient(ellipse 120% 48% at 50% -10%,
              var(--glow-peak) 0%, var(--glow-mid) 55%, transparent 70%);
}

/* Safe-area insets keep text clear of the notch, the home indicator and — in
   landscape — the rounded corners. They resolve to 0 everywhere else. */
.wrap {
  max-width: 640px;
  margin: 0 auto;
  padding: calc(20px + env(safe-area-inset-top)) calc(20px + env(safe-area-inset-right))
           calc(56px + env(safe-area-inset-bottom)) calc(20px + env(safe-area-inset-left));
}

/* ---- THE MASCOT -------------------------------------------------------
   Mark.svg from Assets.xcassets/Illustrations, recoloured to currentColor and
   INLINED at each call site by tools/inline-mascot.py, so one drawing serves
   white-on-gradient and green-on-light and every copy is byte-identical.

   It is not a CSS `mask` over an external SVG: that renders in Chromium and
   renders NOTHING in WebKit, i.e. it silently deletes the mascot on the one
   engine that matters here. Checked in both engines before choosing. */
.mark { display: block; }
.mark svg { display: block; width: 100%; height: auto; fill: currentColor; }

/* ---- THE COURT-LINE MOTIF (CourtLineGeometry) -------------------------
   A regulation 2:1 court, letterboxed and centred: outline, net across the
   middle, service lines at 15.25% and 84.75% of the long axis, central service
   line joining them. Nothing else. Behind every gradient surface at 14% white
   — SurfaceKit.heroCourtLinesOpacity. Decorative: no contrast floor applies. */
.court {
  position: absolute;
  inset: var(--s-md);
  pointer-events: none;
  opacity: 0.14;
}
.court svg { width: 100%; height: 100%; display: block; }

/* ---- THE GRADIENT HERO (SurfaceKit.GradientHeroCard) ------------------ */
.hero {
  position: relative;
  overflow: hidden;
  /* PINNED in both modes — DS.brandGradient. See receipt 3. */
  background: linear-gradient(135deg, #148C59, #0A6642);
  border: 1px solid rgba(255, 255, 255, 0.18);    /* heroHairlineOpacity */
  border-radius: var(--r-hero);
  box-shadow: 0 9px 25px rgba(20, 140, 89, 0.35); /* AuthKit.glowGreen 35% */
  color: #fff;
  padding: var(--s-xl);
}
/* Content rides above the motif. `.court` must keep its own `position:
   absolute` — an unqualified `.hero > *` has the SAME specificity as `.court`
   and, coming later, would put the court back in flow and stretch the hero to
   twice its height. Measured, not assumed. */
.hero > * { position: relative; z-index: 1; }
.hero > .court { position: absolute; z-index: 0; }

.hero h1 { font-size: 32px; font-weight: 800; margin: 10px 0 6px; line-height: 1.15; color: #fff; }
.hero p { font-size: 19px; font-weight: 700; margin: 0; color: #fff; }

/* Full-bleed page hero (the home page's top) */
.hero.bleed {
  /* VERTICAL gradient here, deliberately (2026-08-17, third attempt at the
     status-bar seam — measured from device screenshots). The card hero keeps
     DS.brandGradient's 135deg; the BLEED hero cannot: a diagonal gradient's
     top edge is not one colour (left lighter than centre by ~11 display
     points), and the flat chrome band above it can match at most one point
     of that edge. 180deg makes the top row uniformly #148C59 == theme-color,
     and the seam ceases to exist by construction. The receipts pin the
     STOPS, which are unchanged. */
  background: linear-gradient(180deg, #148C59, #0A6642);
  border-radius: 0 0 var(--r-hero) var(--r-hero);
  border-left: 0; border-right: 0; border-top: 0;
  box-shadow: 0 12px 32px rgba(20, 140, 89, 0.30);
  padding: calc(44px + env(safe-area-inset-top)) calc(24px + env(safe-area-inset-right))
           36px calc(24px + env(safe-area-inset-left));
  text-align: center;
}
.hero.bleed .mark { width: 92px; margin: 0 auto; color: #fff; }
.hero.bleed p { max-width: 22em; margin: 0 auto; }

/* ---- THE KICKER VOICE (SurfaceKit.ScreenKicker) ---------------------- */
.kicker {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--kicker);
  margin: 0 0 4px;
}
/* ON THE GRADIENT THE KICKER IS LARGE TEXT, NOT A CAPTION. 19.2px at 700
   clears the >=18.66px-bold large-text definition, which is the only way a
   white run passes at the gradient's top-left corner (4.26:1 — the 4.5:1
   small floor is unreachable there at any alpha). The mockup had it at
   0.95rem/15.2px, i.e. small text at 3.54:1; computed, not eyeballed. */
.hero .kicker { font-size: 1.2rem; color: #fff; margin-bottom: 2px; }

/* ---- THE ON-GRADIENT BADGE (SurfaceKit.OnGradientBadge) --------------- */
.badge {
  display: inline-block;
  background: #fff;
  color: #0A6642;               /* brandGradientLow — 7.02:1 on white */
  font-size: 0.78rem;
  font-weight: 800;
  border-radius: 999px;
  padding: 4px 10px;
}

/* ---- HEADINGS AND SECTIONS ------------------------------------------- */
h1 { font-size: 1.7rem; margin: 0; line-height: 1.25; }
h2 { font-size: 1.3rem; margin: 0 0 2px; color: var(--ink); font-weight: 800; }

section { margin-top: 34px; }

/* ---- THE TINTED GLASS CARD (SurfaceKit .card(.tinted)) --------------- */
.card {
  background: var(--tint-fill);
  border: 1px solid var(--tint-edge);
  border-radius: var(--r);
  padding: var(--s-lg);
  margin: var(--s-md) 0;
}
.card h3 { margin: 0 0 4px; font-size: 1.02rem; color: var(--ink); }
.card p { margin: 0; color: var(--muted); font-size: 0.95rem; }

/* A card that carries an illustration chip becomes a row. Selected with
   `:has()` rather than an extra class so the layout cannot drift from the
   markup: the nine legal/support pages ship ~30 plain h3+p cards, and under an
   unconditional `display:flex` every one of them would silently lay the
   heading beside its paragraph. */
.card:has(> .ill) {
  display: flex;
  gap: var(--s-md);
  align-items: flex-start;
}

/* Trouble never wears the tint — SurfaceKit's third row. An error, a
   "couldn't load" retry or a destructive row takes the neutral card. */
.card.neutral {
  background: var(--card);
  border-color: var(--hairline);
}

/* ---- THE ILLUSTRATION CHIP ------------------------------------------- */
.ill {
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: var(--r-sm);
  background: var(--tint-fill);
  border: 1px solid var(--tint-edge);
  display: grid;
  place-items: center;
  color: var(--ink-brand);
}
.ill svg { width: 24px; height: 24px; display: block; }

/* ---- BUTTONS --------------------------------------------------------- */
.btn {
  display: block;
  background: linear-gradient(135deg, #148C59, #0A6642);
  color: #fff;
  font-size: 1.05rem;
  font-weight: 800;
  text-decoration: none;
  text-align: center;
  padding: 15px;
  border-radius: var(--r);
  margin: var(--s-sm) 0;
  box-shadow: 0 8px 20px rgba(20, 140, 89, 0.35);
  border: 1px solid rgba(255, 255, 255, 0.18);
}
.btn.ghost {
  background: transparent;
  color: var(--ink-brand);
  border: 1.5px solid var(--tint-edge);
  box-shadow: none;
}
/* On the gradient hero the primary button inverts — white fill, deep ink. */
.hero .btn {
  background: #fff;
  color: #0A6642;
  border-color: rgba(255, 255, 255, 0.9);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
  max-width: 320px;
  margin: 22px auto 0;
}

/* ---- PILLS, CHIPS, LINKS, LISTS -------------------------------------- */
.pill {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-brand);
  background: var(--tint-fill);
  border: 1px solid var(--tint-edge);
  border-radius: 999px;
  padding: 4px 11px;
  margin-bottom: 8px;
}

.chip {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--ink-brand);
  background: var(--tint-fill);
  border: 1px solid var(--tint-edge);
  border-radius: 999px;
  padding: 5px 11px;
}

a { color: var(--ink-brand); }

ul { padding-left: 20px; }
li { margin: 6px 0; }

.lead { color: var(--muted); margin: 0 0 var(--s-lg); }

footer {
  margin-top: 40px;
  color: var(--muted);
  font-size: 0.85rem;
  border-top: 1px solid var(--hairline);
  padding-top: var(--s-lg);
}
footer a { color: var(--muted); }
footer .mark { width: 32px; color: var(--ink-brand); margin-bottom: 10px; }

/* ---- LANGUAGE SWITCHER ----------------------------------------------- */
.langs { margin: 6px 0 var(--s-md); font-size: 0.9rem; }
.langs a { color: var(--ink-brand); text-decoration: none; margin-right: var(--s-md); font-weight: 700; }
.langs a:hover { text-decoration: underline; }
.langs span { margin-right: var(--s-md); font-weight: 700; }   /* the current language */

/* ---- THE LEGAL / SUPPORT PAGES (chrome only) -------------------------
   Nine files: terms/privacy/support x en/pt/es. They take the masthead, the
   kicker voice, the language row and the link colour, and NOTHING else —
   long-form legal text stays plain running prose on the wash, and none of it
   sits on a tinted card.

   Scoped off `.wrap.legal` rather than the mockup's `<article>` wrapper: one
   class on the element that already exists in all nine files, instead of
   wrapping the legal prose itself. Prose bytes stay untouched, which is the
   property that matters on these pages. */
.masthead { display: flex; align-items: center; gap: var(--s-md); margin-bottom: 6px; }
.masthead .mark { width: 34px; flex: none; color: var(--ink-brand); }
.masthead h1 { font-size: 1.45rem; font-weight: 800; }

.updated { color: var(--muted); font-size: 0.85rem; }

.legal h2 { font-size: 1.08rem; margin: 30px 0 4px; color: var(--ink-brand); font-weight: 800; }
.legal p, .legal li { color: var(--ink); }
.legal .card h3 { font-size: 1rem; }
.legal .card p { color: var(--muted); font-size: 0.95rem; }

/* ---- THE COACH PAGE (coachPreview, functions/src/preview/render.ts) ---- */
.coach { max-width: 480px; }

/* The 2:1 cover. With no photo it carries the identity gradient
   (DS.identityStops — a stable hue derived from the coach's name, so a person
   is the same colour in the app and on the web), plus the scrim the in-app
   coach card uses. */
.coach .cover {
  position: relative;
  width: 100%;
  aspect-ratio: 2 / 1;
  border-radius: var(--r-hero);
  overflow: hidden;
  background-size: cover;
  background-position: center;
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: 0 9px 25px rgba(20, 140, 89, 0.28);
  margin-bottom: var(--s-lg);
  display: flex;
  align-items: flex-end;
  padding: var(--s-lg);
}
/* DS.heroScrim: 0.88 black at the bottom, 0.15 at 45%, 0.35 at the top. */
.coach .cover::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,0.88), rgba(0,0,0,0.15) 45%, rgba(0,0,0,0.35));
}
.coach .cover > * { position: relative; z-index: 1; }

/* The mark rides the cover's top-left corner — the coach page carried no
   brand mark at all before. */
.coach .cover .mark {
  position: absolute;
  top: var(--s-lg); left: var(--s-lg);
  width: 34px;
  color: #fff;
  opacity: 0.95;
  z-index: 1;
}

.coach .cover .who { color: #fff; text-align: left; }
.coach .cover .who .kicker { color: rgba(255,255,255,0.9); font-size: 0.9rem; }
.coach .cover .who h1 { font-size: 1.75rem; font-weight: 800; color: #fff; margin: 0; }

.coach .chips { display: flex; gap: var(--s-sm); flex-wrap: wrap; margin: 0 0 var(--s-md); padding: 0; }
.coach .bio { color: var(--ink); margin: 0 0 var(--s-lg); }

/* "Where I coach" — the aggregated location list. A plain list inside the
   tinted glass card, so it takes the page's existing brand chrome without a
   new surface token. `list-style: none` because the mappin is carried by the
   left rule, not by a bullet: a bulleted list of club names reads as
   boilerplate rather than as an address. */
.card.places ul { list-style: none; margin: 4px 0 0; padding: 0; }
.card.places li { color: var(--ink); font-size: 0.95rem; padding: 3px 0 3px 10px; border-left: 2px solid var(--ink-brand); }
.card.places li + li { margin-top: 2px; }
.card.places a { color: var(--ink-brand); text-decoration: none; font-weight: 600; }
.card.places a:hover { text-decoration: underline; }

/* The Results credit's counts. The numerals wear the SAME money/count voice
   the price does — rounded and tabular — because they are the same kind of
   fact, and `CoachResultsCard.numeralFont` gives them that face in the app.
   The paragraph itself stays `.card p` muted; only the numbers step forward. */
.card.results p { color: var(--muted); }
.card.results b {
  font-family: ui-rounded, -apple-system, BlinkMacSystemFont, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--ink);
}

/* The price is the money voice: rounded, tabular, symbol in front. */
.price {
  font-family: ui-rounded, -apple-system, BlinkMacSystemFont, sans-serif;
  font-variant-numeric: tabular-nums;
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--ink);
  margin: 0;
}
/* NOT `var(--kicker)`. This is the one kicker-styled run in the stylesheet
   that sits on a TINTED CARD rather than on the wash, and dark's #59A784
   reads 3.97:1 there — under the 4.5:1 small-text floor at 0.72rem. See
   receipt 4. `--ink-brand` is the token receipt 2 already reserves for
   brand-coloured text on a tinted surface: 5.37:1 light, 5.66:1 dark. */
.price .from {
  display: block;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--ink-brand);
}

.hint { color: var(--muted); font-size: 0.85rem; text-align: center; margin-top: var(--s-sm); }
.links { margin-top: var(--s-lg); font-size: 0.85rem; text-align: center; }
.links a { color: var(--muted); margin: 0 6px; }

/* ---- THE ACADEMY PAGE (academyPreview, .../preview/academyRender.ts) ---
   NOT part of the brand pass — it keeps the plain centred layout it shipped
   with, and this block is what keeps it that way. Everything above that could
   reach it is either scoped to `.coach` (the cover, its scrim, the mark) or is
   a token change it benefits from (the wash, the atmosphere, the buttons).
   Delete this block only together with a rewrite of `academyRender.ts`. */
.coach-preview {
  max-width: 480px;
  margin: 0 auto;
  padding: calc(24px + env(safe-area-inset-top)) calc(20px + env(safe-area-inset-right))
           calc(40px + env(safe-area-inset-bottom)) calc(20px + env(safe-area-inset-left));
  text-align: center;
}

/* 2:1 photo hero with the brand scrim, echoing the in-app academy card. */
.coach-preview .cover {
  width: 100%;
  aspect-ratio: 2 / 1;
  background-size: cover;
  background-position: center;
  border-radius: var(--r-hero);
  background-color: var(--brand);   /* fallback while the image loads */
  box-shadow: inset 0 -60px 60px -20px rgba(0,0,0,0.55);
  margin-bottom: var(--s-lg);
}

.coach-preview h1 { font-size: 1.9rem; margin: 0 0 4px; }
.coach-preview .subtitle { color: var(--muted); margin: 0 0 var(--s-md); }
.coach-preview .bio { color: var(--ink); margin: var(--s-md) 0; line-height: 1.5; }

.coach-preview .chips { display: flex; gap: var(--s-sm); justify-content: center; flex-wrap: wrap; margin: 0 0 var(--s-sm); padding: 0; }

.coach-preview .btn { display: block; max-width: 320px; margin: var(--s-sm) auto; }
.coach-preview .links { margin-top: var(--s-lg); font-size: 0.85rem; }

/* Hero CTA pair — player + coach doors, side by side, stacking on narrow. */
.hero-ctas{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin-top:4px}
.hero-ctas .btn{margin:0}

/* ---- GUIDE CHAPTER STEPS (Task 5, tools/guide_tool.py's `render_page`) ---
   One `.step` per numbered step: the `.card` tint every other legal/support
   page already uses, holding the phone screenshot above the copy. Every
   token below is one already defined at the top of this file — no new
   colours. `.shot` is boxed to the render size `GuideRenderTests` writes
   (390:844 @1x) so the layout never depends on which locale's copy is
   longer, and holds a neutral fill while the HEIC/JPG loads. */
.step { margin: var(--s-lg) 0; }
.step .card { display: flex; flex-direction: column; gap: var(--s-md); }
.step .shot {
  display: block;
  width: 100%;
  max-width: 260px;
  margin: 0 auto;
  aspect-ratio: 390 / 844;
  border-radius: var(--r-sm);
  overflow: hidden;
  border: 1px solid var(--tint-edge);
  background: var(--card);
}
.step .shot img { display: block; width: 100%; height: 100%; object-fit: cover; }
.step .card p { color: var(--ink); margin: 0; }
.step .btn.ghost { max-width: 220px; margin: 0 auto; }
