/* LeagueForge marketing site — redesign in the design language of the two
   products (league app + shop site). No framework, no build step, and no
   third-party request: the two product typefaces are self-hosted from this
   folder (see @font-face below), so the page fetches three same-origin font
   files and nothing else reaches off the site.

   Re-skin contract: every colour, size, spacing step and radius is a custom
   property below. A client instance overrides `:root` and nothing else
   (docs/design-system.md §8). No hex literal appears outside `:root`.
   Every ratio quoted in redesign/contrast-report.md comes from
   `node tools/contrast-check.mjs audit` run against this file.

   LEA-307 adds, after LEA-235's phone-nav rules: the `.shot` frame
   component (placeholder frames until the screenshot and art passes land
   their images), the six-stop product tour, the who-it-is-for cards, the
   inline-SVG icon set, the hero's second column, the lead-form aside, and
   the scroll-entrance block at the foot of the sheet. No token moved, no
   hex was added outside `:root`, and every new colour use reads an
   existing token. */

/* ══ self-hosted type ════════════════════════════════════════════════════
   Board decision, 2 Oct 2026 (interaction b7cfe79c): ship the real product
   faces instead of the fallback stack. Both are SIL Open Font License 1.1, so
   self-hosting is permitted; the licence text travels with the files in
   fonts/OFL-rajdhani.txt and fonts/OFL-chakra-petch.txt, as OFL 1.1 §3
   requires.

   Three files, not two. The page renders exactly three faces — Rajdhani 400
   for body copy, Rajdhani 700 for the brand mark, Chakra Petch 700 for the
   display headings — and each is the `latin` subset only. Measured 40564
   bytes for all three; `chakra-petch` is 9900, `rajdhani-400` 14976,
   `rajdhani-700` 15688. Latin-ext and devanagari are deliberately not shipped:
   the page is US English and the extra subsets would be bytes nobody loads.

   `unicode-range` is the exact value Google serves for the latin subset, so a
   character outside it falls through to the next family in the stack instead
   of pulling a file. `font-display: swap` keeps text readable while the file
   loads. No `font-src` is needed in netlify.toml: its CSP is
   `default-src 'self'` with no font-src directive, and 'self' already covers
   these files. Nothing here is a third-party request. */

@font-face {
  font-family: "Rajdhani";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./fonts/rajdhani-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Rajdhani";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("./fonts/rajdhani-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Chakra Petch";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("./fonts/chakra-petch-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ══ tokens ═════════════════════════════════════════════════════════════ */

:root {
  /* ── palette ────────────────────────────────────────────────────────────
     Neutral near-black ramp, lifted verbatim from the two products'
     globals.css. The blue cast is gone; DESIGN-LANGUAGE.md §4 calls it "the
     single biggest tell that this is a template".
       --bg       #050505  shop site  --color-background
       --bg-alt   #0f0f0f  shop site  --color-card
       --surface  #1a1a1a  shop site  --color-muted
       --line     #333333  shop site  --color-border      (decorative only)
       --ink      #e5e5e5  shop site  --color-foreground                     */
  --bg: #050505;
  --bg-alt: #0f0f0f;
  --surface: #1a1a1a;
  --line: #333333;
  --ink: #e5e5e5;

  /* Secondary ink, two measured steps below `--ink`. Both clear 4.5:1 on every
     plane above, `--surface` being the darkest (6.90:1 and 5.04:1). */
  --ink-dim: #a3a3a3;
  --ink-faint: #8a8a8a;

  /* Control boundary. The shop site's `--color-border-control`, the token the
     token document introduced precisely because a decorative border fails SC
     1.4.11 as the only boundary on an input. 3.55 / 3.34 / 3.03 on the three
     planes. */
  --line-strong: #666666;

  /* ── accent ──────────────────────────────────────────────────────────────
     `--accent` is the league app's `--color-secondary` (#e8213f): the token
     every component in that app already reads, and the crimson family
     DESIGN-LANGUAGE.md §4 asks a page selling the app to lead on.
     `--accent-hi` is that app's `--color-primary` (#ff3b57), declared there as
     "brighter crimson used for glows and hover states" — glow and hover
     only, never body text. */
  --accent: #e8213f;
  --accent-hi: #ff3b57;

  /* Text on an accent fill. docs/site-design-tokens.md §1 rule 1: #ffffff or
     #000000, whichever is higher, equal counting as #000000. Measured:
     #ffffff on --accent is 4.45:1 and fails; #000000 is 4.72:1. */
  --accent-ink: #000000;

  /* Accent used AS TEXT. Same document, rule 2: the fill mixed toward #ffffff
     in 1% steps until it clears 4.5:1. The strictest plane is `--surface`,
     and 15% clears it at 4.55:1, so that is the step taken. The raw fill only
     reaches 3.91:1 there, which is why this token exists at all. */
  --accent-text: #eb425c;

  /* ── translucent structure ──────────────────────────────────────────────
     Measured, not invented: the 50px white-2% grid on `body` is identical in
     both products' globals.css. The hero floor tint is the league app's
     `perspective-floor`; the glow stops are its `glow-box` and `text-glow`. */
  --grid-line: rgba(255, 255, 255, 0.02);
  --grid-size: 50px;
  --floor-line: rgba(232, 33, 63, 0.28);
  --floor-size: 64px;
  --floor-opacity: 0.32;
  --floor-fade: 62%;
  --glow-ring: rgba(232, 33, 63, 0.35);
  --glow-alpha: 0.45;
  --glow-btn: rgb(255 59 87 / var(--glow-alpha));
  --glow-text-a: rgba(255, 59, 87, 0.5);
  --glow-text-b: rgba(232, 33, 63, 0.25);
  --head-bg: rgba(5, 5, 5, 0.9);
  --mask-in: rgb(0 0 0 / 0.9);
  --mask-out: rgb(0 0 0 / 0);

  /* ── type ────────────────────────────────────────────────────────────────
     Both product faces are declared by @font-face above and self-hosted from
     this folder, so a visitor gets the real LeagueForge type on any machine
     with no third-party request. The condensed grotesques after them are kept
     as the fallback: they take over on any character outside the shipped
     latin subset, and if the two woff2 files are ever missing the page still
     renders in a near-identical condensed grotesque instead of falling to the
     system UI face. */
  --font-sans:
    "Rajdhani", "Bahnschrift", "Bahnschrift SemiCondensed", "DIN Alternate",
    "Avenir Next Condensed", "Roboto Condensed", "Segoe UI", system-ui,
    -apple-system, "Helvetica Neue", Arial, sans-serif;
  --font-display:
    "Chakra Petch", "Bahnschrift", "Bahnschrift SemiCondensed", "DIN Condensed",
    "Avenir Next Condensed", "Roboto Condensed", "Segoe UI", system-ui,
    -apple-system, "Helvetica Neue", Arial, sans-serif;

  /* The league app's `body` declaration, verbatim: 1.0625rem / 1.6. Nothing is
     sized in px and nothing is below --text-xs (13px). */
  --text-xs: 0.8125rem;
  --text-sm: 0.875rem;
  --text-base: 1.0625rem;
  --text-lg: 1.1875rem;
  --text-xl: 1.3125rem;
  --text-2xl: 1.625rem;
  --leading-tight: 1.15;
  --leading-body: 1.6;

  /* Both products track POSITIVE: 0.04em body and 0.06em headings in the league
     app, 0.05em and 0.1em in the shop site. There is no negative tracking in
     this file and the check in §6 of DESIGN-LANGUAGE.md proves it. */
  --track-body: 0.04em;
  --track-head: 0.08em;
  --track-micro: 0.14em;
  --track-input: 0.03em;

  /* ── geometry ───────────────────────────────────────────────────────────
     Clipped corners, never a soft radius. 10px and 5px are the two sizes the
     products use in `angled-corners` and `angled-corners-sm`; 18px is the
     one-sided shear in `skew-slab`. site-design-tokens.md §3 fixes the corner
     treatment as not-a-token, so the three cut sizes are the tokens and the
     polygons are built from them. `--radius` stays in the override contract
     for a client that wants its corners back, and ships at 0. */
  --clip-card: 10px;
  --clip-sm: 5px;
  --clip-hero: 18px;
  --radius: 0;

  --clip-card-polygon: polygon(
    var(--clip-card) 0,
    100% 0,
    100% calc(100% - var(--clip-card)),
    calc(100% - var(--clip-card)) 100%,
    0 100%,
    0 var(--clip-card)
  );
  --clip-sm-polygon: polygon(
    var(--clip-sm) 0,
    100% 0,
    100% calc(100% - var(--clip-sm)),
    calc(100% - var(--clip-sm)) 100%,
    0 100%,
    0 var(--clip-sm)
  );
  --clip-hero-polygon: polygon(
    0 0,
    100% 0,
    calc(100% - var(--clip-hero)) 100%,
    0 100%
  );

  /* ── space — the 4px scale from docs/design-system.md §4 ──────────────── */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;
  --gutter: 1.5rem;
  --measure: 44rem;
  --max: 68rem;
  --tap: 2.75rem;

  /* ── motion ─────────────────────────────────────────────────────────────
     site-design-tokens.md §4: 150 / 250 / 400ms on one curve. The long
     ambient cycle is the league app's own `hero-drift` at 28s. Nothing
     autoplays, nothing bounces, and nothing repeats without a
     prefers-reduced-motion sibling. */
  --motion-fast: 150ms;
  --motion-base: 250ms;
  --motion-slow: 400ms;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ambient-glow: 6s;
  --ambient-drift: 28s;
}

/* Registered so the hero button's glow can be animated as a number rather
   than as a colour, which keeps the label's contrast out of the animation. */
@property --glow-alpha {
  syntax: "<number>";
  inherits: false;
  initial-value: 0.45;
}

/* ══ reset and page ══════════════════════════════════════════════════════ */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* Rule 10: no horizontal scroll at any width. `clip` and not `hidden`,
     because `hidden` would make <html> a scroll container and break the
     sticky header. */
  overflow-x: clip;
}

body {
  margin: 0;
  background-color: var(--bg);
  background-image:
    linear-gradient(var(--grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
  background-size: var(--grid-size) var(--grid-size);
  color: var(--ink);
  color-scheme: dark;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  letter-spacing: var(--track-body);
  -webkit-font-smoothing: antialiased;
}

::selection {
  background: var(--accent);
  color: var(--accent-ink);
}

::-webkit-scrollbar {
  width: 8px;
}
::-webkit-scrollbar-track {
  background: var(--bg);
}
::-webkit-scrollbar-thumb {
  background: var(--line-strong);
}
::-webkit-scrollbar-thumb:hover {
  background: var(--accent);
}

h1,
h2,
h3 {
  margin: 0 0 0.5em;
  font-family: var(--font-display);
  font-stretch: 87.5%;
  font-weight: 700;
  line-height: var(--leading-tight);
  letter-spacing: var(--track-head);
  text-transform: uppercase;
  text-wrap: balance;
  overflow-wrap: break-word;
}

h1 {
  font-size: clamp(2.25rem, 5.6vw, 3.75rem);
}
h2 {
  font-size: clamp(1.5rem, 3.6vw, 2.25rem);
}
h3 {
  font-size: var(--text-lg);
}

p {
  margin: 0 0 var(--space-4);
}

a {
  color: var(--accent-text);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 3px;
}

.skip-link {
  position: absolute;
  left: var(--space-4);
  top: var(--space-4);
  z-index: 20;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: var(--space-3) var(--space-4);
  background: var(--accent);
  color: var(--accent-ink);
  text-decoration: none;
  clip-path: var(--clip-sm-polygon);
  transform: translateY(-250%);
}
.skip-link:focus-visible {
  transform: none;
}

/* ══ header ══════════════════════════════════════════════════════════════ */

.site-head {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: var(--space-5);
  padding: var(--space-3) var(--gutter);
  background: var(--head-bg);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap);
  margin-right: auto;
  color: var(--ink);
  font-weight: 700;
  letter-spacing: var(--track-head);
  text-decoration: none;
  text-transform: uppercase;
}

.brand-mark {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 800;
  clip-path: var(--clip-sm-polygon);
}

.site-head nav {
  display: flex;
  gap: var(--space-5);
}

/* `min-width` as well as `min-height`: a 44px-tall but 31px-wide nav link is
   still under the 44x44 floor in DESIGN-LANGUAGE.md rule 10. A measured
   1280px render is what caught it. */
.site-head nav a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  min-width: var(--tap);
  color: var(--ink-dim);
  font-size: var(--text-sm);
  letter-spacing: var(--track-micro);
  text-decoration: none;
  text-transform: uppercase;
  transition: color var(--motion-fast) var(--ease);
}

.site-head nav a:hover {
  color: var(--ink);
}

/* ── the phone's section menu ─────────────────────────────────────────
   The header carries the same five sections twice — the comment in the
   markup records why. Above 46rem the row paints them and the <details> is
   display:none; below it the reverse, so exactly one list is ever laid
   out or exposed, and the two rules above already style whichever list
   that is.

   The toggle rides the sheet's own ghost button — the same classes as the
   "Talk to us" control beside it — so it inherits the 44x44 floor, the
   clipped corners, the visible focus ring and the reduced-motion handling
   for free. What is added here is only the FAQ's own disclosure idiom:
   no marker, and the same accent "+" that becomes "−" when the answer is
   open. The menu speaks the page's existing disclosure language, not a
   new one. */

.nav-menu {
  display: none;
}

.nav-toggle {
  gap: var(--space-2);
  list-style: none;
}

.nav-toggle::-webkit-details-marker {
  display: none;
}

.nav-toggle::after {
  content: "+";
  flex: none;
  color: var(--accent-text);
}

.nav-menu[open] > .nav-toggle::after {
  content: "\2212";
}

/* The open state carries the same accent the FAQ's open answers carry, on
   the ghost border. One accent use, no glow. */
.nav-menu[open] > .nav-toggle::before {
  border-color: var(--accent);
}

@media (max-width: 46rem) {
  /* `.site-head nav` above carries `display: flex` at (0,1,1), so hiding the
     row needs a selector that beats it — `.nav-row` alone (0,1,0) loses,
     which a 320px render caught: the row never hid and the header crushed
     itself. */
  .site-head .nav-row {
    display: none;
  }

  /* The 24px desktop gap is header air; a phone bar carrying three controls
     takes the smaller step. */
  .site-head {
    gap: var(--space-3);
  }

  .nav-menu {
    display: block;
    order: 1;
  }

  /* A closed <details> hides its non-summary contents — but an author
     `display: flex` would beat the user-agent rule doing it, which is the
     exact trap the markup comment records. The inner list is therefore
     display:none by author rule as well, and only flexes on [open]. */
  .nav-menu > nav {
    display: none;
    flex-direction: column;
    gap: 0;
  }

  .nav-menu[open] > nav {
    display: flex;
  }

  /* The open list overlays the page instead of growing the sticky bar, so
     the offset of everything under the header never moves. `--bg` is the
     panel's own plane and `--line-strong` its measured boundary. */
  .nav-menu[open] {
    position: absolute;
    inset: 100% 0 auto 0;
    padding: var(--space-2) var(--gutter) var(--space-3);
    background: var(--bg);
    border-bottom: 1px solid var(--line-strong);
  }

  .nav-menu > nav a {
    padding: var(--space-3) 0;
  }

  .nav-menu > nav a:not(:last-child) {
    border-bottom: 1px solid var(--line);
  }
}

/* Under 27rem the three controls the phone bar carries stop fitting on one
   row. Measured at the natural widths the sheet gives them: brand 150.9 +
   toggle 90.6 + CTA 115.2 + two 12px gaps is 380.7px of content, and with
   the 48px of header gutter the row needs 428.7px of viewport — at 420px
   the CTA is already crushed onto two lines and at 440px it lays out whole,
   so 27rem (432px) is the first whole-rem step above the boundary. The CTA
   loses, for the reason main recorded when its own bar hit the same
   squeeze: the hero carries the same action one scroll down, and the menu
   is the only way to move around the page on a phone. `>` because the
   toggle is also a .btn-sm — only the bar's direct child is the CTA. */
@media (max-width: 27rem) {
  .site-head > .btn-sm {
    display: none;
  }
}

/* ══ buttons ═════════════════════════════════════════════════════════════
   The fill is painted by a pseudo-element, not by the button's own background.
   `clip-path` on a focused element clips that element's own outline away — the
   outline paints outside the border box and the clip applies to the element's
   whole rendering — so the corner cut lives one level down where the ring is
   not. Same picture, working focus indicator, no extra markup. */

.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: var(--space-3) var(--space-5);
  border: 0;
  background: none;
  color: var(--accent-ink);
  font: inherit;
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-decoration: none;
  text-transform: uppercase;
  cursor: pointer;
  transition: transform var(--motion-fast) var(--ease),
    filter var(--motion-fast) var(--ease);
}

.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--accent);
  box-shadow: 0 0 0 1px var(--glow-ring), 0 0 34px -8px var(--glow-btn);
  clip-path: var(--clip-sm-polygon);
}

.btn:hover {
  filter: brightness(1.12);
  transform: translateY(-1px);
}
.btn:active {
  transform: translateY(0);
}

.btn-ghost {
  color: var(--ink);
}
.btn-ghost::before {
  background: transparent;
  border: 1px solid var(--line-strong);
  box-shadow: none;
}
.btn-ghost:hover {
  filter: none;
}
.btn-ghost:hover::before {
  border-color: var(--ink-dim);
}

.btn-sm {
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
}
.btn-lg {
  padding: var(--space-4) var(--space-6);
  font-size: var(--text-lg);
}

/* The hero's one ambient motion: the league app's `pulse-glow`, 6s, driving
   the glow's alpha only. The fill itself never moves, so the label's measured
   4.72:1 is the label's contrast in every frame of the cycle. */
@keyframes pulse-glow {
  0%,
  100% {
    --glow-alpha: 0.3;
  }
  50% {
    --glow-alpha: 0.7;
  }
}
.hero-actions .btn:first-child::before {
  animation: pulse-glow var(--ambient-glow) ease-in-out infinite;
}

/* ══ hero ════════════════════════════════════════════════════════════════ */

.hero {
  position: relative;
  isolation: isolate;
  /* The floor is a 3D-transformed layer inside the hero box. Without this it
     paints past the section into the page below, which a 1280px render showed
     plainly. `clip`, not `hidden`, so nothing here becomes a scroll container. */
  overflow: clip;
  max-width: var(--max);
  margin: 0 auto;
  padding: clamp(3.5rem, 9vw, 7rem) var(--gutter) clamp(2.5rem, 6vw, 4rem);
}

.hero-inner {
  position: relative;
}

/* The league app's `perspective-floor`: a grid in perspective, fading toward
   the horizon. Pure CSS, decorative, out of the accessibility tree. */
.hero-floor {
  position: absolute;
  inset: -20% 0 0 0;
  z-index: -1;
  pointer-events: none;
  opacity: var(--floor-opacity);
  background-image:
    linear-gradient(var(--floor-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--floor-line) 1px, transparent 1px);
  background-size: var(--floor-size) var(--floor-size);
  transform: perspective(340px) rotateX(64deg);
  transform-origin: center top;
  -webkit-mask-image: linear-gradient(
    to bottom,
    var(--mask-in),
    var(--mask-out) var(--floor-fade)
  );
  mask-image: linear-gradient(
    to bottom,
    var(--mask-in),
    var(--mask-out) var(--floor-fade)
  );
  animation: hero-drift var(--ambient-drift) ease-in-out infinite alternate;
}

/* The skewed slab. `skew-slab` in the league app is documented as "for hero
   slabs and section headers", so this is the product's own intent. */
.eyebrow {
  display: inline-block;
  margin: 0 0 var(--space-5);
  padding: var(--space-2) var(--space-8) var(--space-2) var(--space-4);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  clip-path: var(--clip-hero-polygon);
}

.lede {
  max-width: 40rem;
  color: var(--ink-dim);
  font-size: clamp(1.0625rem, 2.2vw, 1.3125rem);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin: var(--space-7) 0 var(--space-5);
}

/* `.hero-actions`' --space-5 bottom margin was the gap between the buttons and
   the hero note that used to follow them. With the note gone the margin has no
   sibling to separate and simply adds to the hero's own bottom padding, which
   inverted the hero's rhythm: measured on `6560056` the hero ran 56/40 at
   320px and 69/46 at 768px — deliberately top-heavy — and after the removal it
   ran 56/64 and 69/70, bottom-heavy. With the actions last, the padding
   carries the space on its own. */
.hero-actions:last-child {
  margin-bottom: 0;
}

/* LEA-402: the tour reuses the hero's button row for its demo CTAs. The hero
   scale (--space-7 above, --space-5 below) is hero rhythm; inside a section
   the pair sits closer to the copy it belongs to. */
.tour-actions {
  margin: var(--space-4) 0 var(--space-6);
}

/* `.hero-note` lived here. Its only instance was the hero's "running live
   today with a partner shop in <city>" line, removed by LEA-245, so the rule
   is removed with it rather than left as a selector nothing matches. */

/* The league app's `text-glow`, used once. */
.hero h1 {
  text-shadow: 0 0 22px var(--glow-text-a), 0 0 60px var(--glow-text-b);
}

/* No entrance animation on the hero copy, and this is a decision worth
   stating. A staggered `rise-in ... both` on the hero's children was the
   league app's `rise-in` applied to the page's most important text, and it
   made the headline conditionally invisible: with `both`, every child sits on
   the `from` keyframe until its delay expires, so the h1 was unreadable for
   the length of the stagger, and unreadable outright wherever the animation
   does not run. Rule 9 asks for long, slow motion on purpose. The headline
   is not decoration. */

/* ── the hero's two columns ──────────────────────────────────────────────
   Copy left, product frame right. The LEA-224 hero was a single left-railed
   column; LEA-307 adds the visual the board asked for. Under 48rem the frame
   simply stacks under the copy, so the phone reads top-down: claim, then
   proof. */
@media (min-width: 48rem) {
  .hero-grid {
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    gap: var(--space-8);
    align-items: center;
  }
}

/* `.shot` below carries `margin: 0` at (0,1,0), so the stacked-figure
   margin needs the compound to win at (0,2,0) — checked against the order
   the shot block is declared in. */
.shot.hero-visual {
  margin: var(--space-6) 0 0;
}

@media (min-width: 48rem) {
  .shot.hero-visual {
    margin: 0;
  }
}

/* ══ shot frames ═════════════════════════════════════════════════════════
   The one component every screenshot and every generated piece sits in:
   the corner-cut card in `--bg-alt` with a measured `--line-strong` boundary,
   the same geometry the product puts around its own panels. This commit the
   frame holds only a placeholder mark; the screenshot pass replaces
   `.shot-frame` with an `<img>` (AVIF/WebP, `srcset` + `sizes`,
   `loading="lazy"` below the fold, explicit `width`/`height` so nothing
   shifts) and resets `aspect-ratio` to the real image's. The aspects below
   are the placeholder's, chosen to read as their framing at a glance:
   desktop 16/10, phone 9/16, wide 16/9, hero 16/11. */

.shot {
  margin: 0;
}

/* Real images (art pass, screenshot pass) sit where the placeholder
   `.shot-frame` div used to be: same card geometry — the clipped corner,
   the `--line-strong` boundary, the `--bg-alt` ground — with the aspect
   coming from the img's own width/height so nothing shifts. `picture` is
   inline by default; the figure's width has to come from the img. */
.shot picture {
  display: block;
}

.shot-img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--bg-alt);
  border: 1px solid var(--line-strong);
  clip-path: var(--clip-card-polygon);
}

.shot-frame {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 10;
  background: var(--bg-alt);
  border: 1px solid var(--line-strong);
  clip-path: var(--clip-card-polygon);
}

.shot-desktop .shot-frame {
  aspect-ratio: 16 / 10;
}
.shot-phone .shot-frame {
  aspect-ratio: 9 / 16;
}
.shot-wide .shot-frame {
  aspect-ratio: 16 / 9;
}
.shot-hero .shot-frame {
  aspect-ratio: 16 / 11;
}

/* The placeholder mark: the brand's angular cut box around the accent L.
   Decorative by contract — every real image replaces it with honest alt
   text, so it stays `aria-hidden` and out of the contrast floors. */
.shot-mark {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  color: var(--accent);
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 700;
  clip-path: var(--clip-sm-polygon);
  box-shadow: inset 0 0 0 1px var(--line-strong);
}

.shot-label {
  margin: var(--space-3) 0 0;
  color: var(--ink-faint);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}

/* ══ stat strip ══════════════════════════════════════════════════════════ */

.strip {
  background: var(--bg-alt);
  border-block: 1px solid var(--line);
}

.strip-item {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  max-width: var(--max);
  margin: 0 auto;
  padding: var(--space-4) var(--gutter);
  border-bottom: 1px solid var(--line);
}

.strip-item:last-child {
  border-bottom: 0;
}

.strip-value {
  min-width: 4.5rem;
  color: var(--accent-text);
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--track-head);
  text-transform: uppercase;
}

.strip-label {
  color: var(--ink-dim);
  font-size: var(--text-sm);
}

.strip-label a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.strip-label a:hover {
  color: var(--accent-text);
}

/* Three items, so three columns — stated, not auto-fitted (LEA-402).
   Before that: two items, two columns. The strip carried four
   until LEA-245 cut the two that belonged to a named reference league rather
   than to the product. An `auto-fit` here would have left the pair hugging the
   left edge of a four-track row; the count is explicit so the next edit to the
   markup has to come back and look at this line.

   The band also changes hands here. The mobile rule above measures each item
   with `max-width: var(--max); margin: 0 auto`, which is right while the items
   are stacked rows. As grid items those auto margins switch off `stretch` and
   shrink each item to its own text, so an item sat centred in its track and
   carried its `border-right` divider at the right edge of its *text* rather
   than at the column boundary. Measured on `6560056`, four columns: the
   dividers were 9px/38px/42px adrift at 1280 — close enough to read as
   deliberate. At two columns the single divider lands 188px adrift, which does
   not. Same bug `.section-alt` carries a comment about further down the sheet,
   same fix: the band takes the measure through padding and the items stretch
   to their tracks. The inner padding is split onto the facing edges so the
   first item's text starts on the page rail, the last item's ends on it, and
   the divider keeps --space-7 of air on both sides. */
@media (min-width: 40rem) {
  .strip {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    padding-inline: max(var(--gutter), calc((100% - var(--max)) / 2 + var(--gutter)));
  }
  .strip-item {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
    max-width: none;
    margin: 0;
    padding-inline: 0;
    border-bottom: 0;
    border-right: 1px solid var(--line);
  }
  .strip-item:not(:last-child) {
    padding-right: var(--space-7);
  }
  .strip-item + .strip-item {
    padding-left: var(--space-7);
  }
  .strip-item:last-child {
    border-right: 0;
  }
}

/* ══ sections ════════════════════════════════════════════════════════════ */

.section {
  max-width: var(--max);
  margin: 0 auto;
  padding: clamp(3rem, 7vw, 5.5rem) var(--gutter);
}

/* The skewed mark above every section title — `skew-slab` again. */
.section h2::before {
  content: "";
  display: block;
  width: 4.5rem;
  height: 0.5rem;
  margin-bottom: var(--space-4);
  background: var(--accent);
  clip-path: var(--clip-hero-polygon);
}

/* The full-bleed bands get their content box from padding rather than from a
   `max-width` + auto-margin wrapper, so they land on exactly the same left
   edge as the narrow sections. The old sheet constrained children with
   `.section-alt > * { margin-inline: auto }`, which re-centred any child
   narrower than the measure — so the "What it costs" and "Tell us about your
   league" ledes sat indented while every other section's sat on the rail. A
   1280px render showed it plainly. */
.section-alt {
  max-width: none;
  padding-inline: max(var(--gutter), calc((100% - var(--max)) / 2 + var(--gutter)));
  background: var(--bg-alt);
  border-block: 1px solid var(--line);
}

.section-lede {
  max-width: var(--measure);
  margin-bottom: var(--space-7);
  color: var(--ink-dim);
}

/* `#proof` runs two ledes in sequence: one block of prose, not two beats. The
   first one's section-sized trailing gap is pulled back to a paragraph's gap
   (--space-4, the base `p` margin) by a negative margin-top on the second, so
   no `:has()` is needed and the collapsed margin is 3rem + (-2rem) = 1rem. */
.section-lede + .section-lede {
  margin-top: calc(var(--space-4) - var(--space-7));
}

.fineprint {
  color: var(--ink-faint);
  font-size: var(--text-xs);
}

/* ══ icons ═══════════════════════════════════════════════════════════════
   One icon style, drawn for this page: a 24px grid, stroke only, no fill,
   straight segments and 45-degree cuts — the clipped-corner geometry again.
   They inherit the heading's colour context and carry no meaning of their
   own: every one is aria-hidden in the markup. */
.icon {
  width: 1.25rem;
  height: 1.25rem;
  margin-right: var(--space-2);
  vertical-align: -0.2em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: square;
  stroke-linejoin: miter;
  color: var(--accent-text);
}

/* ══ cards ═══════════════════════════════════════════════════════════════ */

.grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
}
.grid-3 {
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}

.card {
  padding: var(--space-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  clip-path: var(--clip-card-polygon);
}

.card h3 {
  color: var(--ink);
}
.card p {
  margin: 0;
  color: var(--ink-dim);
  font-size: var(--text-sm);
}
.card-tight {
  padding: var(--space-5) var(--space-4);
}

/* The "who it is for" cards carry their image slot above the heading; the
   frame is the same `.shot` component the tour uses. */
.who-card .shot {
  margin: 0 0 var(--space-5);
}

/* ══ steps ════════════════════════════════════════════════════════════════ */

.steps {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

.steps li {
  position: relative;
  padding: var(--space-5) var(--space-5) var(--space-5) 3.75rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  counter-increment: step;
  clip-path: var(--clip-card-polygon);
}

.steps li::before {
  content: counter(step);
  position: absolute;
  left: 1.4rem;
  top: var(--space-5);
  display: grid;
  place-items: center;
  width: 1.6rem;
  height: 1.6rem;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
  font-size: var(--text-sm);
  clip-path: var(--clip-sm-polygon);
}

.steps p {
  margin: 0;
  color: var(--ink-dim);
  font-size: var(--text-sm);
}

/* ══ product tour ════════════════════════════════════════════════════════
   Six stops, alternating copy-left / copy-right on a desktop and one
   column on a phone. The phone-framed shots stay narrow at every width —
   a 9/16 frame stretched to a desktop column reads as a stretched phone,
   so they centre in their track instead. */

.tour {
  display: grid;
  gap: clamp(var(--space-7), 8vw, var(--space-9));
}

.tour-stop {
  display: grid;
  gap: var(--space-5);
}

.tour-kicker {
  margin: 0 0 var(--space-2);
  color: var(--accent-text);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}

.tour-copy h3 {
  margin-bottom: var(--space-3);
}

.tour-copy p:not(.tour-kicker) {
  margin: 0;
  color: var(--ink-dim);
}

@media (min-width: 48rem) {
  .tour-stop {
    grid-template-columns: 1fr 1.15fr;
    gap: var(--space-7);
    align-items: center;
  }

  .tour-stop:nth-of-type(even) .tour-copy {
    order: 2;
  }

  .tour-stop .shot-phone {
    justify-self: center;
  }
}

/* ══ game chips ══════════════════════════════════════════════════════════ */

.games {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-6);
  padding: 0;
  list-style: none;
}

.games li {
  padding: var(--space-2) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  clip-path: var(--clip-sm-polygon);
}

/* ══ price ═══════════════════════════════════════════════════════════════ */

.price {
  max-width: 32rem;
  margin-bottom: var(--space-4);
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  clip-path: var(--clip-card-polygon);
}

.price-figure {
  margin: 0 0 var(--space-2);
  font-family: var(--font-display);
  font-stretch: 87.5%;
  font-size: clamp(2.25rem, 5vw, 3rem);
  font-weight: 700;
  line-height: var(--leading-tight);
  letter-spacing: var(--track-head);
  text-transform: uppercase;
}

.price-unit {
  color: var(--ink-faint);
  font-size: var(--text-xs);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}

.price-note {
  margin: 0;
  color: var(--ink-dim);
  font-size: var(--text-sm);
}

/* ══ faq ═════════════════════════════════════════════════════════════════ */

.faq-item {
  padding: var(--space-1) 0;
  border-bottom: 1px solid var(--line);
}

.faq-item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: var(--tap);
  padding: var(--space-4) 0;
  cursor: pointer;
  list-style: none;
  font-weight: 700;
  letter-spacing: var(--track-head);
  text-transform: uppercase;
  transition: color var(--motion-fast) var(--ease);
}

.faq-item summary:hover {
  color: var(--accent-text);
}

.faq-item summary::-webkit-details-marker {
  display: none;
}

.faq-item summary::after {
  content: "+";
  flex: none;
  color: var(--accent-text);
  font-weight: 700;
}

.faq-item[open] summary::after {
  content: "\2212";
}

/* The accent edge that appears when an answer opens. */
.faq-item[open] {
  padding-left: var(--space-4);
  border-left: 3px solid var(--accent);
}

.faq-item p {
  max-width: var(--measure);
  margin: 0 0 var(--space-4);
  color: var(--ink-dim);
}

/* ══ enquiry form ═════════════════════════════════════════════════════════
   The Netlify wiring is untouched: form name, method, action, accept-charset,
   data-netlify, the honeypot name and every field name are the ones the
   dashboard is already keyed on. Only the painting changed.

   Inputs are deliberately not corner-cut. `angled-corners` is applied to
   cards, buttons and section slabs in both products, and an input is a
   focusable control: clipping it would clip its focus ring. Flat edge here. */

.lead-form {
  display: grid;
  gap: var(--space-4);
  max-width: 34rem;
  margin: var(--space-7) 0 0;
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-align: left;
  clip-path: var(--clip-card-polygon);
}

.field {
  display: grid;
  gap: var(--space-2);
}

.lead-form label {
  color: var(--ink);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}

.lead-form input,
.lead-form textarea {
  width: 100%;
  min-height: var(--tap);
  padding: var(--space-3) var(--space-4);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  color: var(--ink);
  font: inherit;
  letter-spacing: var(--track-input);
  transition: border-color var(--motion-fast) var(--ease);
}

.lead-form textarea {
  min-height: 6rem;
  resize: vertical;
}

.lead-form input:hover,
.lead-form textarea:hover {
  border-color: var(--ink-dim);
}

.lead-form input::placeholder,
.lead-form textarea::placeholder {
  color: var(--ink-faint);
  opacity: 1;
}

.lead-form .btn {
  justify-self: start;
}

.lead-form .fineprint {
  margin: 0;
}

/* Honeypot: off-screen but still fillable by bots, and out of the
   accessibility tree. Never `display: none` — bots skip that. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ══ cta ═════════════════════════════════════════════════════════════════
   Left-aligned like every other section. The old sheet centred this one and
   nothing else; the accent tab above the heading already carries the beat, and
   a centred close on a left-railed page reads as two different layouts. */

.lead-form {
  margin: var(--space-7) 0 0;
}

/* ── the close: "what happens after you send" beside the form ────────────
   LEA-307. The aside answers the question a bare form leaves — "and then
   what?" — using only sentences the page already claimed: the reply, the
   working site to look at, the figure in writing. No timing promise is
   made anywhere, because none can be measured. */
.cta-grid {
  display: grid;
  gap: var(--space-7);
}

@media (min-width: 48rem) {
  .cta-grid {
    grid-template-columns: 1fr 1.25fr;
    gap: var(--space-8);
    align-items: start;
  }

  .cta-grid .lead-form {
    margin: 0;
  }
}

.cta-aside h3 {
  margin-bottom: var(--space-4);
}

.cta-steps {
  display: grid;
  gap: var(--space-3);
  margin: 0;
  padding-left: 1.25rem;
  color: var(--ink-dim);
}

.cta-steps li::marker {
  color: var(--accent-text);
  font-weight: 700;
}

.cta .fineprint {
  max-width: 34rem;
}

/* ══ footer ══════════════════════════════════════════════════════════════ */

.site-foot {
  padding: var(--space-8) var(--gutter) var(--space-6);
  background: var(--bg-alt);
  border-top: 1px solid var(--line);
}

.foot-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
  max-width: var(--max);
  margin: 0 auto var(--space-6);
}

.foot-grid a {
  display: flex;
  align-items: center;
  min-height: var(--tap);
  min-width: var(--tap);
  color: var(--ink-dim);
  font-size: var(--text-sm);
  text-decoration: none;
  transition: color var(--motion-fast) var(--ease);
}

.foot-grid a:hover {
  color: var(--accent-text);
}

.foot-line {
  max-width: 22rem;
  color: var(--ink-faint);
  font-size: var(--text-sm);
}

.foot-head {
  margin-bottom: var(--space-2);
  color: var(--ink-faint);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}

.foot-legal {
  max-width: var(--max);
  margin: 0 auto;
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
  color: var(--ink-faint);
  font-size: var(--text-xs);
}

/* ══ narrow viewports ═══════════════════════════════════════════════════
   Rule 10 and site-design-tokens.md §7: one column under 640px. `auto-fit`
   alone is not enough — a measured 616px viewport still fitted two 276px card
   columns, which is a product grid on a phone.

   This block has to come AFTER the grid rules it overrides. A media query adds
   no specificity, so an identical selector declared earlier in the file loses.
   That was a real bug in the first draft of this file and the 616px render is
   what caught it. */

@media (max-width: 40rem) {
  .grid,
  .grid-3,
  .steps,
  .foot-grid {
    grid-template-columns: 1fr;
  }
}

/* ══ motion ══════════════════════════════════════════════════════════════ */

@keyframes hero-drift {
  from {
    transform: perspective(340px) rotateX(64deg) scale(1);
  }
  to {
    transform: perspective(340px) rotateX(64deg) scale(1.07);
  }
}

@keyframes rise-in {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ══ scroll entrances ═══════════════════════════════════════════════════
   LEA-307. CSS scroll-driven animations: `animation-timeline: view()` runs
   the league app's own `rise-in` once as each block crosses into the
   viewport — entrance only, no loop, nothing autoplays. Progressive by
   construction: Chrome 115+ gets the motion, every other engine ignores
   the `@supports` block and the page renders in place; the
   `prefers-reduced-motion: no-preference` wrapper keeps the guard rule 9
   requires. The hero is deliberately absent from this list — see the note
   in the hero block. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .card,
    .steps li,
    .tour-stop,
    .who-card {
      animation: rise-in both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }
  }
}

/* Every rule that moves more than once, and the one-shot entrance for good
   measure. site-design-tokens.md §4: no exceptions, no per-brand override. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  .btn,
  .hero-inner > *,
  .hero-floor,
  .faq-item summary,
  .site-head nav a,
  .foot-grid a,
  .lead-form input,
  .lead-form textarea {
    animation: none;
    transition: none;
  }
  .btn:hover {
    transform: none;
  }
  .skip-link {
    transition: none;
  }
}

/* A second floor for anyone who asks for it: the decorative hairline becomes a
   real boundary. `--line-strong`'s measured ratios are in the report. */
@media (prefers-contrast: more) {
  :root {
    --line: var(--line-strong);
  }
}

/* ══ analytics consent bar (LEA-402) ═════════════════════════════════════
   One row at every width, the same shape as the product's banner. Created by
   the inline script in index.html only for a visitor who has not chosen. */
.consent-bar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 50;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--gutter);
  background: var(--bg-alt);
  border-top: 1px solid var(--line);
}

.consent-text {
  flex: 1;
  min-width: 0;
  margin: 0;
  color: var(--ink-dim);
  font-size: var(--text-sm);
}

.consent-btn {
  flex-shrink: 0;
  min-height: 44px;
  padding: 0 var(--space-4);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
}

.consent-btn:focus-visible {
  outline: 2px solid var(--accent-text);
  outline-offset: 2px;
}

.consent-accept {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}


/* ══ LEA-402 "next level" ═════════════════════════════════════════════════
   The stage, the live ticker, "make it yours", device frames and the sticky
   call to action. app.js drives the motion; every piece reads as a finished
   still with reduced motion or no script. */

.hero-aside {
  margin: var(--space-4) 0 0;
  color: var(--ink-faint);
  font-size: var(--text-sm);
}
.hero-aside a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-underline-offset: 4px;
}

/* ── the stage ─────────────────────────────────────────────────────────── */

.stage {
  --dc-bg: #2b2d31;
  --dc-ink: #dbdee1;
  --dc-dim: #949ba4;
  --dc-blurple: #5865f2;
  --brand: #5aa9f0;
  position: relative;
  min-height: 38rem;
  margin-top: var(--space-6);
  isolation: isolate;
}

.stage-art {
  position: absolute;
  inset: 8% -6% 6% 10%;
  z-index: -1;
  overflow: hidden;
  opacity: 0.38;
  filter: saturate(1.2) blur(1px);
  mask-image: radial-gradient(closest-side, #000 55%, transparent);
  -webkit-mask-image: radial-gradient(closest-side, #000 55%, transparent);
}
.stage-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.stage::before {
  content: "";
  position: absolute;
  inset: 20% 10% 10% 20%;
  z-index: -1;
  background: radial-gradient(closest-side, rgba(232, 33, 63, 0.28), transparent);
  filter: blur(30px);
}

.stage-discord,
.stage-site {
  position: absolute;
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(0, 0, 0, 0.6);
}

/* Discord keeps its own colours on purpose: recognising the app is the point. */
.stage-discord {
  top: 0;
  left: 0;
  width: min(23rem, 88%);
  padding-bottom: 0.75rem;
  background: rgba(43, 45, 49, 0.96);
  border-radius: 10px;
  color: var(--dc-ink);
  font-family: "gg sans", "Segoe UI", system-ui, sans-serif;
  letter-spacing: 0;
  transform: rotate(-1.5deg);
}
.dc-head {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.35);
  font-weight: 700;
  font-size: 0.9rem;
}
.dc-hash { color: var(--dc-dim); font-size: 1.15rem; }
.dc-sub {
  margin-left: 0.4rem;
  overflow: hidden;
  color: var(--dc-dim);
  font-weight: 400;
  font-size: 0.75rem;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.dc-feed {
  margin: 0;
  padding: 0.4rem 0.9rem 0;
  list-style: none;
}
.dc-msg {
  display: flex;
  gap: 0.65rem;
  padding: 0.45rem 0;
  font-size: 0.86rem;
  line-height: 1.35;
  transition: opacity 0.45s ease, transform 0.45s ease;
}
.stage.is-ready .dc-msg:not(.is-in) { opacity: 0; transform: translateY(8px); }
.dc-avatar {
  flex: 0 0 2.1rem;
  height: 2.1rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: #4e5058;
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
}
.dc-avatar-bot { background: var(--accent); }
.dc-avatar-a { background: #3ba55d; }
.dc-name { margin: 0; font-weight: 600; color: #f2f3f5; font-size: 0.84rem; }
.dc-tag {
  margin-left: 0.25rem;
  padding: 0 0.25rem;
  border-radius: 3px;
  background: var(--dc-blurple);
  color: #fff;
  font-size: 0.6rem;
  vertical-align: 1px;
}
.dc-text { margin: 0.1rem 0 0; color: var(--dc-ink); }
.dc-text b { color: #fff; }
.dc-cmd { color: #c9cdfb; background: rgba(88, 101, 242, 0.3); padding: 0 0.2rem; border-radius: 3px; }
.dc-card {
  margin-top: 0.35rem;
  padding: 0.55rem 0.7rem;
  border-left: 4px solid var(--accent);
  border-radius: 4px;
  background: #232428;
}
.dc-card-title { margin: 0; color: #fff; font-weight: 700; font-size: 0.86rem; }
.dc-card-sub { margin: 0.15rem 0 0.45rem; color: var(--dc-dim); font-size: 0.78rem; }
.dc-buttons { display: flex; gap: 0.4rem; margin: 0; }
.dc-btn {
  padding: 0.28rem 0.7rem;
  border-radius: 3px;
  background: #4e5058;
  color: #fff;
  font-size: 0.74rem;
  font-weight: 600;
  transition: transform 0.15s ease, box-shadow 0.2s ease;
}
.dc-btn-ok { background: #248046; }
.dc-confirmed {
  display: none;
  margin: 0.45rem 0 0;
  color: #3ba55d;
  font-size: 0.78rem;
  font-weight: 700;
}
.dc-card.is-pressing .dc-btn-ok { transform: scale(0.94); box-shadow: 0 0 0 3px rgba(59, 165, 93, 0.45); }
.dc-card.is-confirmed .dc-buttons { display: none; }
.dc-card.is-confirmed .dc-confirmed { display: block; }

/* The league site wears the client brand colour, not ours. */
.stage-site {
  right: 0;
  bottom: 0;
  width: min(21rem, 84%);
  padding: 0 0 0.8rem;
  background: rgba(10, 12, 16, 0.96);
  border-radius: 8px;
  transform: rotate(1.2deg);
}
.site-bar {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.5rem 0.7rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.site-dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: #3a3d44; }
.site-url { margin-left: 0.5rem; color: var(--ink-faint); font-size: 0.68rem; letter-spacing: 0.04em; }
.site-title {
  margin: 0.75rem 0.9rem 0.5rem;
  color: #fff;
  font-family: var(--font-display);
  font-size: 0.95rem;
  letter-spacing: var(--track-head);
  text-transform: uppercase;
}
.table {
  position: relative;
  height: calc(4 * 2.35rem);
  margin: 0 0.9rem;
  padding: 0;
  list-style: none;
}
.row {
  --i: 0;
  position: absolute;
  inset: 0 0 auto 0;
  height: 2.1rem;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0 0.7rem;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.03);
  transform: translateY(calc(var(--i) * 2.35rem));
  transition: transform 0.9s cubic-bezier(0.2, 0.8, 0.2, 1), background 0.6s ease, border-color 0.6s ease;
  font-size: 0.85rem;
}
.row:nth-child(2) { --i: 1; }
.row:nth-child(3) { --i: 2; }
.row:nth-child(4) { --i: 3; }
.pos { width: 1rem; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.who { flex: 1; color: var(--ink); font-weight: 600; }
.pts { color: var(--brand); font-family: var(--font-display); font-weight: 700; font-variant-numeric: tabular-nums; }
.row-hero.is-up {
  z-index: 1;
  border-color: rgba(90, 169, 240, 0.6);
  background: rgba(90, 169, 240, 0.14);
}
.row-hero.is-flash .pts { animation: pts-pop 0.9s ease; }
@keyframes pts-pop {
  0% { transform: scale(1); }
  35% { transform: scale(1.6); text-shadow: 0 0 14px var(--brand); }
  100% { transform: scale(1); }
}
.site-updated {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0.6rem 0.9rem 0;
  color: var(--ink-faint);
  font-size: 0.72rem;
  transition: opacity 0.6s ease 0.6s;
}
.stage.is-ready:not(.is-done) .site-updated { opacity: 0; }

.live-dot {
  width: 0.5rem;
  height: 0.5rem;
  flex: none;
  border-radius: 50%;
  background: #3ba55d;
  box-shadow: 0 0 0 0 rgba(59, 165, 93, 0.6);
  animation: live-pulse 2s infinite;
}
@keyframes live-pulse {
  70% { box-shadow: 0 0 0 0.45rem rgba(59, 165, 93, 0); }
  100% { box-shadow: 0 0 0 0 rgba(59, 165, 93, 0); }
}

@media (max-width: 47.99rem) {
  .stage { min-height: 40rem; margin-top: 3rem; }
  .stage-discord { width: 94%; transform: none; }
  .stage-site { width: 88%; transform: none; }
  .stage-art { inset: 10% -10% 0 -10%; }
}

/* ── live ticker ───────────────────────────────────────────────────────── */

.ticker {
  display: flex;
  align-items: stretch;
  overflow: hidden;
  border-block: 1px solid var(--line);
  background: #0a0a0a;
}
.ticker[hidden] { display: none; }
.ticker-badge {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0 1rem;
  border-right: 1px solid var(--line);
  background: var(--bg-alt);
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--text-xs);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
}
.ticker-badge:hover { color: var(--accent-text); }
.ticker-track {
  flex: 1;
  overflow-x: auto;
  scrollbar-width: none;
  mask-image: linear-gradient(90deg, transparent, #000 4%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 92%, transparent);
}
.ticker-track::-webkit-scrollbar { display: none; }
.ticker-list {
  display: flex;
  width: max-content;
  margin: 0;
  padding: 0;
  list-style: none;
}
.ticker.is-moving .ticker-list { animation: ticker-run 60s linear infinite; }
.ticker.is-moving:hover .ticker-list,
.ticker.is-moving:focus-within .ticker-list { animation-play-state: paused; }
@keyframes ticker-run { to { transform: translateX(-50%); } }
.ticker-list a {
  display: flex;
  align-items: baseline;
  gap: 0.55rem;
  padding: 0.85rem 1.4rem;
  border-right: 1px solid rgba(255, 255, 255, 0.05);
  color: var(--ink-dim);
  font-size: var(--text-sm);
  text-decoration: none;
  white-space: nowrap;
}
.ticker-list a:hover .t-win { color: var(--accent-text); }
.t-game { color: var(--ink-faint); font-size: var(--text-xs); letter-spacing: var(--track-micro); text-transform: uppercase; }
.t-win { color: var(--ink); font-weight: 700; }
.t-score { color: var(--accent-text); font-family: var(--font-display); font-weight: 700; font-variant-numeric: tabular-nums; }
.t-ago { color: var(--ink-faint); font-size: var(--text-xs); }
@media (max-width: 30rem) { .ticker-badge { font-size: 0.68rem; padding: 0 0.7rem; } }

/* ── make it yours ─────────────────────────────────────────────────────── */

.yours-grid { display: grid; gap: 3rem; align-items: center; }
@media (min-width: 56rem) {
  .yours-grid { grid-template-columns: 1fr 1.1fr; }
}
.yours-controls { margin: 1.5rem 0; }
.yours-label {
  display: block;
  margin: 0 0 0.5rem;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--text-xs);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}
.yours-input {
  width: 100%;
  max-width: 22rem;
  min-height: 48px;
  margin-bottom: 1.5rem;
  padding: 0 0.9rem;
  border: 1px solid var(--line-strong);
  background: #000;
  color: var(--ink);
  font: inherit;
  font-size: 1.0625rem;
}
.yours-input:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
.yours-swatches { display: flex; flex-wrap: wrap; gap: 0.7rem; }
.swatch {
  width: 44px;
  height: 44px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 50%;
  background: var(--sw);
  box-shadow: inset 0 0 0 3px #000;
  cursor: pointer;
  transition: transform 0.15s ease, border-color 0.15s ease;
}
.swatch:hover { transform: scale(1.08); }
.swatch[aria-checked="true"] { border-color: #fff; }
.swatch:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

.mock {
  --brand: #5aa9f0;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  background: #0b0d12;
  box-shadow: 0 40px 90px -30px var(--brand), 0 30px 60px -20px #000;
  transition: box-shadow 0.5s ease;
}
.mock.is-swapping { animation: mock-swap 0.5s ease; }
@keyframes mock-swap { 40% { filter: brightness(1.25); } }
.mock-bar { display: flex; align-items: center; gap: 0.3rem; padding: 0.55rem 0.8rem; background: #14171d; }
.mock-url { margin-left: 0.6rem; color: var(--ink-faint); font-size: 0.72rem; }
.mock-head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.8rem 1.1rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.mock-mark {
  width: 1.9rem;
  height: 1.9rem;
  display: grid;
  place-items: center;
  background: var(--brand);
  color: #000;
  font-family: var(--font-display);
  font-weight: 700;
  clip-path: var(--clip-sm-polygon);
  transition: background 0.4s ease;
}
.mock-name { color: #fff; font-family: var(--font-display); font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.mock-nav { margin-left: auto; color: var(--ink-faint); font-size: 0.72rem; }
@media (max-width: 30rem) { .mock-nav { display: none; } }
.mock-hero {
  position: relative;
  padding: 1.6rem 1.1rem 1.4rem;
  overflow: hidden;
}
.mock-hero::before {
  content: "";
  position: absolute;
  inset: -40% -20% auto 30%;
  height: 160%;
  background: radial-gradient(closest-side, var(--brand), transparent);
  opacity: 0.22;
  transition: background 0.5s ease;
}
.mock-hero > * { position: relative; }
.mock-eyebrow { margin: 0; color: var(--brand); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; }
.mock-title {
  margin: 0.4rem 0 1rem;
  color: #fff;
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 3vw, 1.8rem);
  font-weight: 700;
  line-height: 1.1;
  text-transform: uppercase;
  overflow-wrap: anywhere;
}
.mock-cta {
  display: inline-block;
  padding: 0.6rem 1.1rem;
  background: var(--brand);
  color: #000;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  clip-path: var(--clip-sm-polygon);
  transition: background 0.4s ease;
}
.mock-table { margin: 0; padding: 0.4rem 1.1rem 1.1rem; list-style: none; }
.mock-table li {
  display: flex;
  gap: 0.8rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  color: var(--ink-dim);
  font-size: 0.85rem;
}
.mock-table li span:nth-child(2) { flex: 1; color: var(--ink); }
.mock-table b { color: var(--brand); font-family: var(--font-display); transition: color 0.4s ease; }

/* ── device frames on the tour ─────────────────────────────────────────── */

.tour .shot-desktop .shot-img,
.tour .shot-phone .shot-img {
  display: block;
  box-shadow: 0 30px 70px -25px rgba(0, 0, 0, 0.95), 0 0 60px -30px rgba(232, 33, 63, 0.45);
}
.tour .shot-desktop .shot-img {
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-top: 22px solid #16181d;
  border-radius: 8px;
}
.tour .shot-phone .shot-img {
  border: 9px solid #16181d;
  border-radius: 28px;
}
@media (prefers-reduced-motion: no-preference) and (hover: hover) {
  .tour .shot-img { transition: transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1); }
  .tour .shot-desktop:hover .shot-img { transform: perspective(1200px) rotateY(-4deg) translateY(-4px); }
  .tour .shot-phone:hover .shot-img { transform: perspective(1200px) rotateY(4deg) translateY(-4px); }
}

/* ── sticky call to action, phones only ────────────────────────────────── */

.sticky-cta { display: none; }
@media (max-width: 47.99rem) {
  .sticky-cta {
    position: fixed;
    inset: auto 1rem 1rem 1rem;
    z-index: 40;
    display: block;
    padding: 0.95rem;
    background: var(--accent);
    color: var(--accent-ink);
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: 700;
    letter-spacing: var(--track-micro);
    text-align: center;
    text-decoration: none;
    text-transform: uppercase;
    clip-path: var(--clip-sm-polygon);
    transform: translateY(160%);
    transition: transform 0.35s ease;
  }
  .sticky-cta.is-shown { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .live-dot { animation: none; }
  .row, .dc-msg, .site-updated, .sticky-cta, .mock, .mock-mark, .mock-cta { transition: none; }
  .row-hero.is-flash .pts, .mock.is-swapping { animation: none; }
}


/* ── game logos ────────────────────────────────────────────────────────── */
.games-logos {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  padding: 0;
  border: 1px solid var(--line);
  background: var(--line);
  list-style: none;
}
@media (min-width: 48rem) { .games-logos { grid-template-columns: repeat(5, 1fr); } }
.games-logos li {
  display: grid;
  place-items: center;
  min-height: 7.5rem;
  padding: 1.25rem;
  border: 0;
  background: var(--bg);
  clip-path: none;
}
.games-logos li:last-child:nth-child(odd) { grid-column: 1 / -1; }
@media (min-width: 48rem) { .games-logos li:last-child:nth-child(odd) { grid-column: auto; } }
.games-logos img {
  width: auto;
  max-width: 100%;
  max-height: 3.25rem;
  filter: grayscale(0.35) brightness(0.9);
  transition: filter 0.3s ease, transform 0.3s ease;
}
.games-logos li:hover img { filter: none; transform: scale(1.04); }


/* Swatch colours live here: the CSP (style-src 'self') blocks inline style
   attributes, so a style="--sw:..." on the button never applies. */
.swatch:nth-child(1) { --sw: #5aa9f0; }
.swatch:nth-child(2) { --sw: #e8213f; }
.swatch:nth-child(3) { --sw: #2fbf71; }
.swatch:nth-child(4) { --sw: #f2b33d; }
.swatch:nth-child(5) { --sw: #a77bff; }
.ext { margin-left: 0.45em; }
/* The card title already says who it is for; the small label repeated it. */
#who .shot-label { display: none; }


/* ══ LEA-402 round two ════════════════════════════════════════════════════
   Hero copy that narrates the stage, the step rail, scroll entrances,
   game logos at their own proportions, the pricing cards and the contact
   section. */

/* ── hero ──────────────────────────────────────────────────────────────── */

.hero-grid { align-items: center; }
.h1-accent {
  color: transparent;
  background: linear-gradient(90deg, #fff 0%, #ffb3be 45%, var(--accent-hi) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  text-shadow: none;
}
@supports not (background-clip: text) {
  .h1-accent { color: var(--accent-hi); }
}

.hero-show { display: flex; flex-direction: column; gap: 1.1rem; }
.hero-show .stage { margin-top: 0; }

.stage-steps {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: none;
}
.stage-steps li {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding-top: 0.7rem;
  color: var(--ink-faint);
  font-size: 0.78rem;
  line-height: 1.3;
  transition: color 0.4s ease;
}
/* The progress bar above each step fills as the stage reaches it. */
.stage-steps li::before,
.stage-steps li::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  height: 3px;
}
.stage-steps li::before { right: 0; background: var(--line); }
.stage-steps li::after {
  width: 0;
  background: var(--accent);
  box-shadow: 0 0 10px var(--glow-ring);
  transition: width 0.5s ease;
}
.stage-steps .step-n {
  color: var(--ink-faint);
  font-family: var(--font-display);
  font-size: 0.7rem;
  letter-spacing: var(--track-micro);
}
.stage-steps li.is-active,
.stage-steps li.is-done { color: var(--ink); }
.stage-steps li.is-active::after { width: 100%; transition-duration: 1.2s; }
.stage-steps li.is-done::after { width: 100%; }
.stage-steps li.is-active .step-n,
.stage-steps li.is-done .step-n { color: var(--accent-text); }

@media (max-width: 30rem) {
  .stage-steps { grid-template-columns: repeat(2, 1fr); row-gap: 0.9rem; }
}

/* ── scroll entrances (app.js adds .has-entrances, then .is-in) ────────── */

/* The view-timeline rise from before stands down where script runs this. */
.has-entrances .card,
.has-entrances .steps li,
.has-entrances .tour-stop,
.has-entrances .who-card { animation: none; }

.has-entrances .tour-stop .shot,
.has-entrances .tour-stop .tour-copy,
.has-entrances .plan,
.has-entrances #included .card,
.has-entrances #how .steps li,
.has-entrances .who-card,
.has-entrances .games-logos li,
.has-entrances .yours-grid > * {
  transition:
    opacity 0.8s cubic-bezier(0.2, 0.8, 0.2, 1),
    transform 0.9s cubic-bezier(0.2, 0.8, 0.2, 1);
  transition-delay: calc(var(--stagger, 0) * 90ms);
}

/* Cards and logos rise and settle, one after another. */
.has-entrances .plan:not(.is-in),
.has-entrances #included .card:not(.is-in),
.has-entrances #how .steps li:not(.is-in),
.has-entrances .who-card:not(.is-in),
.has-entrances .games-logos li:not(.is-in),
.has-entrances .yours-grid > :not(.is-in) {
  opacity: 0;
  transform: translateY(28px) scale(0.98);
}

/* The tour: the screenshot slides in from its own side with a little tilt,
   the copy rises behind it, and a light passes across the screen once. */
.has-entrances .tour-stop:not(.is-in) .tour-copy { opacity: 0; transform: translateY(24px); }
.has-entrances .tour-stop .tour-copy { transition-delay: 0.15s; }
.has-entrances .tour-stop:not(.is-in) .shot { opacity: 0; transform: translateX(70px) rotate(2deg) scale(0.96); }
.has-entrances .tour-stop:nth-child(even):not(.is-in) .shot { transform: translateX(-70px) rotate(-2deg) scale(0.96); }
.tour-stop .shot { position: relative; }
.tour-stop .shot picture { position: relative; display: block; overflow: hidden; border-radius: 8px; }
.tour-stop .shot-phone picture { border-radius: 28px; }
.has-entrances .tour-stop .shot picture::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 35%, rgba(255, 255, 255, 0.16) 50%, transparent 65%);
  transform: translateX(-120%);
  pointer-events: none;
}
.has-entrances .tour-stop.is-in .shot picture::after { animation: shot-sweep 1.4s ease 0.55s 1 both; }
@keyframes shot-sweep { to { transform: translateX(120%); } }

/* The kicker draws its own underline as the stop arrives. */
.tour-kicker { position: relative; display: inline-block; }
.has-entrances .tour-kicker::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -0.3rem;
  height: 2px;
  width: 0;
  background: var(--accent);
  transition: width 0.7s ease 0.5s;
}
.has-entrances .tour-stop.is-in .tour-kicker::after { width: 2.5rem; }

/* ── game logos at their own proportions ───────────────────────────────── */

.games-logos li { padding: 1.4rem 1.6rem; }
.games-logos img {
  display: block;
  width: 100%;
  height: 3.1rem;
  max-width: none;
  max-height: none;
  object-fit: contain;
}

/* ── pricing ───────────────────────────────────────────────────────────── */

.plans {
  display: grid;
  gap: 1.5rem;
  margin-top: 2.5rem;
}
@media (min-width: 52rem) {
  .plans { grid-template-columns: 1fr 1fr; align-items: stretch; }
}
.plan {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 2rem 2rem 1.8rem;
  border: 1px solid var(--line-strong);
  background: linear-gradient(180deg, #151515, #0d0d0d);
}
.plan-founding {
  border-color: var(--accent);
  background:
    radial-gradient(120% 80% at 100% 0%, rgba(232, 33, 63, 0.22), transparent 55%),
    linear-gradient(180deg, #1a0b0e, #0d0d0d);
  box-shadow: 0 0 0 1px rgba(232, 33, 63, 0.35), 0 30px 80px -30px rgba(232, 33, 63, 0.55);
}
.plan-flag {
  align-self: flex-start;
  margin: 0 0 1.1rem;
  padding: 0.35rem 0.75rem;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  clip-path: var(--clip-sm-polygon);
}
.plan-name {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: var(--track-head);
  text-transform: uppercase;
}
.plan-price {
  margin: 0.6rem 0 0.2rem;
  color: #fff;
  font-family: var(--font-display);
  font-size: clamp(3rem, 7vw, 4.25rem);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.plan-founding .plan-price { text-shadow: 0 0 30px rgba(255, 59, 87, 0.45); }
.plan-cur { margin-right: 0.08em; font-size: 0.5em; vertical-align: 0.65em; color: var(--ink-dim); }
.plan-per { margin-left: 0.25em; color: var(--ink-dim); font-size: 0.26em; letter-spacing: var(--track-head); text-transform: uppercase; }
.plan-alt { margin: 0 0 1.2rem; color: var(--ink-dim); font-size: var(--text-sm); }
.plan-alt s { color: var(--ink-faint); }
.plan-setup {
  margin: 0 0 1.1rem;
  padding: 0.85rem 1rem;
  border-left: 2px solid var(--line-strong);
  background: rgba(255, 255, 255, 0.03);
  color: var(--ink-dim);
  font-size: var(--text-sm);
}
.plan-founding .plan-setup { border-left-color: var(--accent); }
.plan-setup b { color: var(--ink); }
.plan-list {
  flex: 1;
  margin: 0 0 1.6rem;
  padding: 0;
  list-style: none;
}
.plan-list li {
  position: relative;
  padding: 0.5rem 0 0.5rem 1.7rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  color: var(--ink);
  font-size: var(--text-sm);
}
.plan-list li::before {
  content: "";
  position: absolute;
  left: 0.15rem;
  top: 0.95rem;
  width: 0.6rem;
  height: 0.35rem;
  border-left: 2px solid var(--accent-text);
  border-bottom: 2px solid var(--accent-text);
  transform: translateY(-50%) rotate(-45deg);
}
.plan-cta { align-self: stretch; justify-content: center; text-align: center; }
.plan-small { margin: 0.8rem 0 0; color: var(--ink-faint); font-size: var(--text-xs); text-align: center; }
.pricing-note { margin-top: 1.5rem; }

/* ── contact ───────────────────────────────────────────────────────────── */

.cta-promise {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 1.8rem;
  padding: 0.55rem 0.9rem;
  border: 1px solid var(--line);
  background: var(--bg-alt);
  color: var(--ink);
  font-size: var(--text-sm);
}
.cta-timeline {
  position: relative;
  margin: 0 0 1.8rem;
  padding: 0;
  list-style: none;
  counter-reset: cta;
}
.cta-timeline::before {
  content: "";
  position: absolute;
  left: 1.05rem;
  top: 1.2rem;
  bottom: 1.2rem;
  width: 2px;
  background: linear-gradient(var(--accent), rgba(232, 33, 63, 0.1));
}
.cta-timeline li {
  position: relative;
  padding: 0 0 1.5rem 3.4rem;
  color: var(--ink-dim);
  font-size: var(--text-sm);
  line-height: var(--leading-body);
  counter-increment: cta;
}
.cta-timeline li::before {
  content: counter(cta);
  position: absolute;
  left: 0;
  top: -0.1rem;
  width: 2.2rem;
  height: 2.2rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--accent);
  background: var(--bg);
  color: var(--accent-text);
  font-family: var(--font-display);
  font-weight: 700;
  clip-path: var(--clip-sm-polygon);
}
.cta-timeline b {
  display: block;
  margin-bottom: 0.2rem;
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--text-base);
  letter-spacing: 0.04em;
}
.cta-direct { color: var(--ink-faint); font-size: var(--text-sm); }
.cta-direct a { color: var(--ink); text-decoration-color: var(--accent); text-underline-offset: 4px; }

/* The form: a lit edge, inputs that answer focus. */
.lead-form {
  position: relative;
  background: linear-gradient(180deg, #161616, #0e0e0e);
  box-shadow: 0 0 0 1px rgba(232, 33, 63, 0.25), 0 40px 90px -40px rgba(232, 33, 63, 0.45);
}
.lead-form::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
}
.lead-form input,
.lead-form textarea {
  transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.lead-form input:focus,
.lead-form textarea:focus {
  border-color: var(--accent);
  background: #050505;
  box-shadow: 0 0 0 3px rgba(232, 33, 63, 0.18);
  outline: none;
}
.lead-form .btn { width: 100%; justify-content: center; }

@media (prefers-reduced-motion: reduce) {
  .stage-steps li, .stage-steps li::after { transition: none; }
}


/* ── tour callouts: one chip per screenshot, pointing at what matters ──── */
.shot-callout {
  position: absolute;
  z-index: 2;
  top: 18%;
  right: -1.2rem;
  max-width: 15rem;
  padding: 0.55rem 0.85rem;
  border: 1px solid rgba(232, 33, 63, 0.55);
  background: rgba(12, 12, 12, 0.92);
  box-shadow: 0 14px 40px -12px rgba(0, 0, 0, 0.9), 0 0 24px -8px rgba(232, 33, 63, 0.6);
  color: var(--ink);
  font-size: 0.8rem;
  font-weight: 700;
  line-height: 1.3;
  backdrop-filter: blur(6px);
  clip-path: var(--clip-sm-polygon);
}
.tour-stop:nth-child(even) .shot-callout { right: auto; left: -1.2rem; }
.shot-phone .shot-callout { top: auto; bottom: 16%; }
@media (max-width: 47.99rem) {
  .shot-callout { right: 0.5rem; top: auto; bottom: 2.5rem; }
  .tour-stop:nth-child(even) .shot-callout { left: 0.5rem; }
}
.has-entrances .tour-stop .shot-callout {
  opacity: 0;
  transform: translateY(10px) scale(0.9);
  transition: opacity 0.5s ease 1s, transform 0.6s cubic-bezier(0.2, 1.4, 0.4, 1) 1s;
}
.has-entrances .tour-stop.is-in .shot-callout {
  opacity: 1;
  transform: none;
  animation: callout-float 5s ease-in-out 2s infinite;
}
@keyframes callout-float {
  50% { transform: translateY(-6px); }
}
@media (prefers-reduced-motion: reduce) {
  .shot-callout { animation: none; }
}
