/**
 * Design tokens — the single source of truth for color, type, spacing.
 *
 * Every component stylesheet consumes these custom properties; nothing
 * hard-codes a hex, a font stack, or a magic pixel value.
 *
 * GENERATED from packages/tokens/src/index.ts. Do not hand-edit —
 * run: npm run tokens:build --workspace @race-app/tokens
 *
 * Breakpoint note: CSS custom properties can't be used inside @media
 * queries, so the nav breakpoint (768px) is repeated in component CSS.
 * It is documented in the source above as the one sanctioned value.
 */

:root {
  /*
   * TELL THE BROWSER THE PAGE HAS TWO SIDES.
   *
   * Everything else here is a custom property that only OUR stylesheets read.
   * `color-scheme` is the one thing the browser reads for itself, and without
   * it the dark side of this scale stopped at the edge of what we paint:
   * form controls, select popups, scrollbars, spellcheck underlines and the
   * canvas the browser paints BEFORE any stylesheet has loaded all stayed
   * light-styled on a page whose tokens had gone dark. A dark app with a
   * white scrollbar and a white flash on every load is not a dark app.
   *
   * IT BELONGS HERE, beside the palette it agrees with, rather than in one
   * app's global.css: apps/web and apps/site both @import this file, and a
   * second copy is a second thing to forget when a third surface appears.
   *
   * `light dark` rather than a single value because this is the DEFAULT: the
   * scale follows `prefers-color-scheme` unless somebody has chosen, which
   * says the page renders correctly either way and lets the OS decide. An
   * explicit choice narrows it to one — see the `[data-theme]` rules under
   * the dark block, which have to move `color-scheme` too, or a page told to
   * be dark keeps light scrollbars and light form controls.
   *
   * NOT THE WHOLE FIX. A stylesheet is parsed too late to colour the
   * first paint, so each app's HTML also carries
   * `<meta name="color-scheme" content="light dark">`. This is the one that
   * governs the controls; that one governs the flash.
   */
  color-scheme: light dark;
  /* ------------------------------------------------------------------ */
  /* Color — mainly white, one confident accent                       */
  /* ------------------------------------------------------------------ */

  /*
   * Canvas & surfaces: near-white canvas so true-white cards sit on it.
   *
   * DARK IS NAVY, NOT GREY, and that is the whole of the theme change. The
   * three keep their ORDER — sunken < canvas < surface — because ~150
   * stylesheets rely on `:hover { background: var(--color-surface-sunken) }`
   * stepping away from a card in the same direction in both themes. Only
   * the hue and the depth moved.
   */
  --color-canvas: #f7f8f8;
  --color-surface: #ffffff;
  --color-surface-sunken: #f1f3f4;

  /*
   * Ink — a three-step ramp, RESPACED so all three steps clear WCAG AA
   * (4.5:1) on every surface AND stay visibly apart from each other.
   *
   * THE MEASUREMENTS THE VALUES COME FROM. Every ratio below was
   * computed, not judged by eye, against all three surfaces in both
   * themes. Read them before changing a value here.
   *
   *   BEFORE            canvas  surface  sunken
   *   light ink          16.73   17.80   15.99
   *   light secondary     5.58    5.93    5.33
   *   light faint         2.93    3.12    2.80   <- failed AA everywhere
   *   dark  ink          16.01   13.80   16.78
   *   dark  secondary     8.82    7.60    9.24
   *   dark  faint         5.07    4.37    5.32   <- ALSO failed, on surface
   *
   *   AFTER             canvas  surface  sunken
   *   light secondary     7.80    8.30    7.46
   *   light faint         4.87    5.18    4.65
   *   dark  faint         5.93    5.11    6.22
   *
   * THE WORST LIGHT SURFACE IS SUNKEN, NOT WHITE. #f1f3f4 is darker than
   * #ffffff, so a mid-grey ink has less contrast against it, and the old
   * faint at 2.80 there failed even the 3:1 large-text floor — there was
   * no type size at which it was compliant.
   *
   * WHY SECONDARY MOVED TOO, when only faint was failing. To reach 4.5
   * on sunken, faint has to land at ~4.5-5.0. Secondary sat at 5.33.
   * That is an 18% luminance gap — not a perceptible step — so fixing
   * faint alone would have left a scale that still claims three steps
   * and renders as two. A silently-flattened ramp is worse than the
   * contrast bug, because nothing shows it. The pair now sits about 2x
   * apart in luminance.
   *
   * THE DARK FAILURE WAS FOUND BY MEASURING, NOT BY TOOLING. An
   * Impeccable detect pass flagged the light values and said nothing
   * about dark, because it renders one theme. A clean detector run is
   * evidence about the theme it rendered and no other.
   */
  --color-ink: #16181a;
  --color-ink-secondary: #474f59;
  --color-ink-faint: #646e7a;
  --color-ink-inverse: #ffffff;

  /*
   * Scrims — the wash behind an overlay, and the one colour that must NOT
   * follow the theme. Every one of these was `color-mix(in srgb,
   * var(--color-ink) 42%, transparent)` written out at five call sites,
   * which was fine while ink was always near-black and became a WHITE VEIL
   * over the page the moment ink gained a dark value. A scrim is dark in
   * both themes because its job is to put the page in shadow, not to
   * invert with it.
   */
  --color-scrim: rgba(22, 24, 26, 0.42);
  --color-scrim-strong: rgba(22, 24, 26, 0.55); /* over a document, where the page behind must recede further */

  /* Hairlines — thin cool grays instead of boxes and shadows. */
  --color-hairline: #e5e8ea;
  --color-hairline-strong: #d3d8dc;

  /*
   * Accent — "flag blue" (#0b5bd7). One saturated cobalt used for actions,
   * active nav, and the live-telemetry language. Chosen because it has real
   * energy at small sizes (a 6px dot still reads), it's the one hue that
   * never occurs naturally in a desert landscape so it always means
   * "interface, not terrain", and it stays clear of all three semantic
   * status colors.
   */
  --color-accent: #0b5bd7;
  --color-accent-strong: #0846ad; /* hover / pressed */
  --color-accent-soft: #e9f0fc; /* tinted fills behind accent content */
  --color-accent-glow: rgba(11, 91, 215, 0.28); /* live pulse ring */

  /*
   * THE THEME'S LIGHT SOURCE — two washes the dark app is lit by, and the
   * reason its grounds are gradients rather than flat navy.
   *
   * TRANSPARENT IN LIGHT, ON PURPOSE. That is what makes this ONE rule
   * instead of two: the gradient geometry is written once, unconditionally,
   * and the theme supplies only the colours. A fully transparent wash
   * composites to nothing, so the light app is byte-for-byte the app it was
   * before dark mode got a look — no `@media` fork, no `[data-theme]` fork,
   * and no chance of the two drifting.
   *
   * They are rgba(...0) rather than the keyword `transparent` because these
   * cross to native as colours and a keyword does not parse there.
   */
  --color-glow-primary: rgba(11, 91, 215, 0); /* the high wash — top right of a ground */
  --color-glow-secondary: rgba(11, 91, 215, 0); /* the low wash — bottom left, wider and cooler */

  /* Semantic status — used only where status is real (jobs, timing, map). */
  --color-go: #17843f;
  --color-go-soft: #e6f4ea;
  --color-caution: #a86a00;
  --color-caution-soft: #faf0dc;
  --color-alert: #cf2f2a;
  --color-alert-soft: #fcebea;

  /* Focus ring */
  --color-focus: #0b5bd7;

  /*
   * Map terrain — the one place the palette warms up, so the stub map reads
   * as desert ground against the cool-white interface around it.
   */
  --color-terrain: #f2f0e8;
  --color-terrain-contour: #e6e2d4;
  --color-course: #c9c2ac;

  /* ------------------------------------------------------------------ */
  /* Type — Barlow Condensed (display) + Inter (body & data)          */
  /* ------------------------------------------------------------------ */

  --font-display: "Barlow Condensed", "Arial Narrow", system-ui, sans-serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: ui-monospace, monospace; /* values read character by character — filenames, URLs, raw names */

  /* Tabular figures for data columns — apply via `font-feature-settings`. */
  --font-feature-data: "tnum" 1, "zero" 0;

  /* Type scale (rem) + matched line heights */
  --text-xs: 0.75rem; /* 12 — captions, timestamps */
  --text-sm: 0.8125rem; /* 13 — secondary, table meta */
  --text-base: 0.9375rem; /* 15 — body */
  --text-lg: 1.0625rem; /* 17 — emphasized body */
  --text-xl: 1.25rem; /* 20 — card titles */
  --text-2xl: 1.625rem; /* 26 — screen titles */
  --text-3xl: 2.25rem; /* 36 — display numbers, PTT */

  --leading-tight: 1.2;
  --leading-normal: 1.5;

  --tracking-display: 0.01em;
  --tracking-caps: 0.08em; /* all-caps labels */

  /* ------------------------------------------------------------------ */
  /* Spacing — 4px base scale                                         */
  /* ------------------------------------------------------------------ */

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;

  /* ------------------------------------------------------------------ */
  /* Shape & elevation                                                */
  /* ------------------------------------------------------------------ */

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --radius-full: 999px;

  /* The only shadow in the app — floating layers (detail cards, PTT). */
  --shadow-float: 0 4px 16px rgba(22, 24, 26, 0.1);

  /* ------------------------------------------------------------------ */
  /* Layout                                                           */
  /* ------------------------------------------------------------------ */

  --nav-height-mobile: 64px;
  --sidebar-width: 236px;
  --content-max-width: 1120px;

  /* ------------------------------------------------------------------ */
  /* Motion                                                           */
  /* ------------------------------------------------------------------ */

  /*
   * MOTION TELLS YOU SOMETHING HAPPENED. It is not decoration here: this app is used one-handed, in daylight, in a moving truck, where a subtle animation is invisible and a slow one is worse than none at all. So the scale is short, and the RULES for which value applies when are in docs/motion.md — the numbers alone will not keep two platforms in step.
   *
   * THE DURATIONS ARE MEANINGS, not sizes. A state change on something already on screen is fast; something moving across it is base; something arriving takes longer than something leaving, because arriving has to be noticed and leaving only has to get out of the way.
   *
   * THERE ARE NO SPRING TOKENS, deliberately, and the reason matters for the iOS side. A spring is defined by response and damping, and its character is overshoot and settle — which is the one thing this app cannot afford, and which no CSS timing function reproduces. A spring on iOS beside a bezier on the web would be two motion languages wearing one set of names: exactly the drift this package exists to prevent. The easings below cross EXACTLY instead — `toNative` emits their four control points, and SwiftUI consumes them verbatim via `Animation.timingCurve(_:_:_:_:duration:)`. Same curve, same duration, same motion. If a spring is ever genuinely wanted, it belongs here as a token both platforms read, not as a value one platform invents.
   */

  --duration-fast: 120ms; /* state change on something already on screen */
  --duration-base: 200ms; /* something moving on screen */
  --duration-enter: 320ms; /* something arriving */
  --duration-exit: 180ms; /* something leaving */
  --duration-pulse: 1600ms; /* one cycle of an ongoing state */
  --duration-pulse-urgent: 900ms; /* an ongoing state that wants attention NOW */

  /* Curves. Paired with the duration of the same meaning — never mixed. */
  --ease-out: cubic-bezier(0.2, 0, 0, 1); /* state change */
  --ease-entrance: cubic-bezier(0.16, 1, 0.3, 1); /* arrival */
  --ease-exit: cubic-bezier(0.4, 0, 1, 1); /* dismissal */

  /* ------------------------------------------------------------------ */
  /* Semantic status — the dot language, resolved from the palette    */
  /* ------------------------------------------------------------------ */

  /*
   * One vocabulary for "what state is this thing in", so the status dot, the
   * job pills and the map badges cannot each answer differently. `ink` is the
   * foreground, `soft` the tinted fill behind it. Defined as var() references
   * rather than as hexes so a palette change carries here for free — and so
   * this block is exactly what tokens.json says it is.
   */
  --tone-live-ink: var(--color-accent);
  --tone-live-soft: var(--color-accent-soft);
  --tone-go-ink: var(--color-go);
  --tone-go-soft: var(--color-go-soft);
  --tone-caution-ink: var(--color-caution);
  --tone-caution-soft: var(--color-caution-soft);
  --tone-alert-ink: var(--color-alert);
  --tone-alert-soft: var(--color-alert-soft);
  --tone-idle-ink: var(--color-ink-faint);
  --tone-idle-soft: var(--color-surface-sunken);
}

/*
 * DARK MODE — the same tokens, re-derived rather than inverted.
 *
 * Only colour crosses: a 4px gap is 4px at midnight, so the spacing, type
 * and shape scales have no dark side and are absent here by design.
 *
 * THE INK RAMP IS REBUILT, NOT FLIPPED. Light runs ink (darkest) →
 * secondary → faint (lightest) against a near-white ground. Dark runs the
 * same three roles the other way against a near-black one, and the values
 * are chosen for contrast on THIS ground rather than by negating the light
 * hexes — negation is what produced a scale whose quietest token was its
 * only readable one.
 *
 * THE SURFACES KEEP THEIR ORDER: sunken < canvas < surface in both themes.
 * That is deliberate and it is the reason no existing rule inverts —
 * `:hover { background: var(--color-surface-sunken) }` still steps away
 * from the card in the same direction, so ~150 stylesheets nobody can
 * review by eye keep the meaning they were written with.
 *
 * THE STATUS COLOURS SERVE TWO ROLES AT ONCE and are tuned to a narrow
 * luminance band that satisfies both: white-on-fill ≥ 4.5 (they are
 * button backgrounds in 51 rule blocks) and colour-on-surface ≥ 3.0 (they
 * are also label text). Measured, not eyeballed — every ratio in this
 * block meets or beats what light mode already ships, including the pills,
 * where light's own floor is 3.93 and not 4.5.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-canvas: #0b1626;
    --color-surface: #132340;
    --color-surface-sunken: #071020;
    --color-ink: #eaf1ff;
    --color-ink-secondary: #a3b6d4;
    --color-ink-faint: #7e95b6;
    --color-ink-inverse: #ffffff;
    --color-scrim: rgba(0, 0, 0, 0.62);
    --color-scrim-strong: rgba(0, 0, 0, 0.72); /* over a document, where the page behind must recede further */
    --color-hairline: #1e2e48;
    --color-hairline-strong: #2c3f5e;
    --color-accent: #3970c6;
    --color-accent-strong: #2d5ca3; /* hover / pressed */
    --color-accent-soft: #081428; /* tinted fills behind accent content */
    --color-accent-glow: rgba(57, 112, 198, 0.45); /* live pulse ring */
    --color-glow-primary: rgba(59, 130, 246, 0.17); /* the high wash — top right of a ground */
    --color-glow-secondary: rgba(29, 78, 216, 0.15); /* the low wash — bottom left, wider and cooler */
    --color-go: #17823e;
    --color-go-soft: #061009;
    --color-caution: #94691e;
    --color-caution-soft: #120c02;
    --color-alert: #cd3c37;
    --color-alert-soft: #1a0607;
    --color-focus: #5b93e0;
    --color-terrain: #1c1a15;
    --color-terrain-contour: #2a2620;
    --color-course: #4d4838;
    --shadow-float: 0 6px 20px rgba(2, 8, 20, 0.62);
  }
}

/*
 * THE EXPLICIT CHOICE, which outranks the OS in both directions.
 *
 * `data-theme` is written on <html> by the app's theme toggle and read
 * back from localStorage before first paint. Absent — the default, and
 * what every embedded and public surface stays on — the media query
 * above is the whole story.
 *
 * `color-scheme` MOVES WITH IT. It is the one thing the browser reads for
 * itself, so a page told to be dark while the OS is light would keep
 * light scrollbars, light select popups and a white canvas behind
 * everything the app paints. Setting the tokens without it produces a
 * dark app in a light frame.
 */
[data-theme="dark"] {
  color-scheme: dark;
  --color-canvas: #0b1626;
  --color-surface: #132340;
  --color-surface-sunken: #071020;
  --color-ink: #eaf1ff;
  --color-ink-secondary: #a3b6d4;
  --color-ink-faint: #7e95b6;
  --color-ink-inverse: #ffffff;
  --color-scrim: rgba(0, 0, 0, 0.62);
  --color-scrim-strong: rgba(0, 0, 0, 0.72); /* over a document, where the page behind must recede further */
  --color-hairline: #1e2e48;
  --color-hairline-strong: #2c3f5e;
  --color-accent: #3970c6;
  --color-accent-strong: #2d5ca3; /* hover / pressed */
  --color-accent-soft: #081428; /* tinted fills behind accent content */
  --color-accent-glow: rgba(57, 112, 198, 0.45); /* live pulse ring */
  --color-glow-primary: rgba(59, 130, 246, 0.17); /* the high wash — top right of a ground */
  --color-glow-secondary: rgba(29, 78, 216, 0.15); /* the low wash — bottom left, wider and cooler */
  --color-go: #17823e;
  --color-go-soft: #061009;
  --color-caution: #94691e;
  --color-caution-soft: #120c02;
  --color-alert: #cd3c37;
  --color-alert-soft: #1a0607;
  --color-focus: #5b93e0;
  --color-terrain: #1c1a15;
  --color-terrain-contour: #2a2620;
  --color-course: #4d4838;
  --shadow-float: 0 6px 20px rgba(2, 8, 20, 0.62);
}

/*
 * Light is the base scale, so this sets no colours. It pins `color-scheme`
 * only, so the browser's own chrome follows the choice on a machine whose
 * OS is dark.
 */
[data-theme="light"] {
  color-scheme: light;
}

/*
 * EMBEDDED (`?embed=1`, marked on <html> by lib/embed.ts): the tab bar is not
 * rendered, so the space anything floating leaves clear of it is zero. Written
 * as a token override rather than as rules on each floating element — the toast
 * today, whatever floats tomorrow — so "how much room does the nav take" stays
 * one answer in one place.
 */
:root[data-embed="1"] {
  --nav-height-mobile: 0px;
  --text-base: 1.0625rem; /* 17 — SF `.body`, which is what sits beside it in the native shell */
  --leading-normal: 1.3;
}


/**
 * The public site's own rules. Everything above this in the built stylesheet is
 * packages/tokens — the same colour, type and spacing the app uses, so the site
 * and the product look like one thing without a second design system.
 *
 * Fonts are DELIBERATELY not self-hosted here. The app ships Barlow Condensed
 * and Inter as @fontsource packages; this page names them first and falls back
 * to the system stack, so a visitor who has used the app sees the real face and
 * one who has not sees a fast one immediately. A marketing page that blocks its
 * first paint on a webfont over one bar of signal has failed at the only thing
 * it had to do.
 *
 * WHICH MEANS THE LAYOUT CANNOT DEPEND ON THE CONDENSED FACE. Barlow Condensed
 * is a gift, not a given: most first-time visitors get Arial Narrow, and a fair
 * number get plain system-ui, which is not condensed at all. So every display
 * size is capped where a NON-condensed fallback still fits its column, and
 * measures are set in ch — a property of whatever font actually loaded.
 *
 * ---------------------------------------------------------------------------
 * # THE GRADIENTS
 *
 * The dark sections are the page's signature and they are BUILT, not filled. A
 * flat navy rectangle is what a dark section looks like when nobody decided
 * anything; each of these is a base colour with two radial washes over it — a
 * blue one high on one side, a cooler and wider one low on the other — so the
 * ground has depth and a light source, and the eye reads a lit space rather
 * than a block of paint.
 *
 * They are TOKENS (`--grad-hero`, `--grad-band`, `--grad-cta`) rather than
 * three colour stops restated per section, because the family has to stay a
 * family: the moment one section hand-rolls its own the page has two blues in
 * it and looks like two designs.
 *
 * THEY DO NOT FOLLOW THE COLOUR SCHEME, for the same reason --color-scrim does
 * not. They are not "the dark version of a surface", they are the brand's own
 * ground, and a hero that inverted at midnight would take the white text on it
 * with it. The LIGHT bands between them do follow the tokens, so the page reads
 * navy → surface → navy → surface → navy in either theme.
 *
 * # WHAT IS UNTOUCHED, AND SHOULD STAY THAT WAY
 *
 * The masthead, the brand mark, and the two auth buttons with their `.btn`
 * family. They are the furniture every page this site grows will wear. The
 * masthead is deliberately still a LIGHT bar above the dark hero: the logo is
 * dark ink on a white plate multiplied onto its background, so a navy masthead
 * would erase it, and re-cutting the logo is not this file's call to make.
 */

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

html {
  scroll-behavior: smooth;
  /* The masthead is sticky, so an anchor jump would otherwise land its target
     underneath it. 76px is the header at its tallest plus a little air. */
  scroll-padding-top: 76px;
}

body {
  margin: 0;
  background: var(--color-surface);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  line-height: 1.08;
  margin: 0;
  letter-spacing: var(--tracking-display);
}

p {
  margin: 0;
}

a {
  color: var(--color-accent);
}

img,
svg {
  max-width: 100%;
}

:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* --- The page's own tokens ----------------------------------------------- */

:root {
  /* The navy scale. Fixed in both themes — see the gradient note above. */
  --navy-950: #04080f;
  --navy-900: #071120;
  --navy-800: #0a1a2e;
  --navy-700: #0e2440;
  --navy-600: #123156;

  /* The blue that runs through the whole page. --color-accent is chosen for
     contrast against WHITE and is far too dark on navy, so the dark sections
     get their own two steps of it. */
  --blue: #3b82f6;
  --blue-bright: #60a5fa;
  --blue-deep: #1d4ed8;

  /* Ink on navy. */
  --on-dark: #eef4ff;
  --on-dark-2: #9fb3d0;
  --on-dark-3: #6d84a6;
  --hair-dark: rgba(147, 178, 224, 0.16);

  /*
   * THE THREE GROUNDS. Base navy, then a blue wash high and a wider cool wash
   * low. Percentages rather than pixels on the radial sizes, so the light
   * source keeps its position on a phone as well as on a desk monitor.
   */
  --grad-hero:
    radial-gradient(90% 70% at 78% 6%, rgba(59, 130, 246, 0.34), transparent 62%),
    radial-gradient(80% 80% at 4% 96%, rgba(29, 78, 216, 0.3), transparent 66%),
    linear-gradient(168deg, var(--navy-950) 0%, var(--navy-800) 46%, var(--navy-700) 100%);

  --grad-band:
    radial-gradient(70% 90% at 88% 0%, rgba(59, 130, 246, 0.24), transparent 60%),
    radial-gradient(80% 70% at 0% 100%, rgba(29, 78, 216, 0.22), transparent 64%),
    linear-gradient(180deg, var(--navy-900) 0%, var(--navy-800) 100%);

  --grad-cta:
    radial-gradient(80% 120% at 50% -10%, rgba(59, 130, 246, 0.3), transparent 60%),
    radial-gradient(70% 90% at 92% 108%, rgba(29, 78, 216, 0.28), transparent 62%),
    linear-gradient(180deg, var(--navy-800) 0%, var(--navy-950) 100%);

  /* One shadow for the floating things, and one glow for the lit ones. */
  --lift: 0 30px 70px rgba(2, 8, 20, 0.55);
  --glow: 0 0 60px rgba(59, 130, 246, 0.45);
}

/* The page's one shared container. Sections go full width; this centres their
   contents, so a band reaches the edge of the screen and its text does not. */
.wrap {
  max-width: 1140px;
  margin-inline: auto;
  padding-inline: var(--space-5);
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: var(--space-4);
  top: var(--space-4);
  z-index: 20;
  background: var(--color-surface);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
}

/* --- Masthead ------------------------------------------------------------ */

/* Sticky, because the two buttons on the right are what most people came for
   and they should never be a scroll away. */
.masthead {
  position: sticky;
  top: 0;
  z-index: 10;
  background: color-mix(in srgb, var(--color-surface) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--color-hairline);
}

.masthead__inner {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  max-width: 1140px;
  margin-inline: auto;
  padding: var(--space-3) var(--space-5);
}

.masthead__brand {
  display: inline-flex;
  margin-right: auto;
  /* Without this the flex row steals width from the logo before it wraps the
     buttons, and the mark ends up squashed to a smear at 360px. */
  flex: 0 0 auto;
}

.brand {
  display: block;
  width: 150px;
  height: auto;
  /* THE WHITE MATTE. The logo is a PNG on an opaque white plate, so on any
     surface that is not white it draws a pale rectangle. Multiply maps white to
     whatever is underneath and leaves the ink alone — the same treatment
     AppShell and AuthScreens already use in the app. Every surface it sits on
     here is white or near-white, so it degrades to a no-op rather than a box. */
  mix-blend-mode: multiply;
}

.masthead__nav {
  display: flex;
  gap: var(--space-5);
  font-size: 15px;
}

.masthead__link {
  color: var(--color-ink-secondary);
  text-decoration: none;
  font-weight: 500;
}

.masthead__link:hover {
  color: var(--color-ink);
}

.masthead__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 0 0 auto;
}

/* The header's label swap. Everything the narrow screen does not need is hidden
   with display, not visibility, so it takes no space in the flex row. */
.at-narrow {
  display: none;
}

@media (max-width: 560px) {
  .at-wide {
    display: none;
  }
  .at-narrow {
    display: inline;
  }
  .masthead__inner {
    padding-inline: var(--space-4);
    gap: var(--space-2);
  }
  .masthead__brand .brand {
    width: 118px;
  }
}

/* --- Buttons ------------------------------------------------------------- */

.btn {
  display: inline-block;
  padding: 0.7em 1.15em;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-hairline-strong);
  background: var(--color-surface);
  color: var(--color-ink);
  font-weight: 600;
  font-size: 15px;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out);
}

.btn:hover {
  border-color: var(--color-ink-faint);
  background: var(--color-canvas);
}

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

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

/* Sign in is a real destination but not the one being sold, so it reads as a
   link with a target rather than as a second button competing with Create an
   account. */
.btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--color-ink-secondary);
}

.btn--ghost:hover {
  background: var(--color-canvas);
  border-color: transparent;
  color: var(--color-ink);
}

.btn--invert {
  background: var(--color-surface);
  border-color: var(--color-surface);
  color: var(--color-ink);
}

.btn--invert:hover {
  background: var(--color-canvas);
  border-color: var(--color-canvas);
}

.btn--small {
  padding: 0.55em 0.9em;
}

.btn--large {
  font-size: 17px;
  padding: 0.85em 1.5em;
}

/* The secondary action where it stands on navy. Declared AFTER the family above
   so it wins on source order without a heavier selector — and so nothing above
   it has to change to make room for it. */
.btn--onDark {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(159, 179, 208, 0.38);
  color: var(--on-dark);
}

.btn--onDark:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(159, 179, 208, 0.65);
}

/* --- Shared furniture ---------------------------------------------------- */

.icon {
  display: block;
}

/* The small blue label above a heading. One class, two grounds. */
.kicker {
  font-size: 12.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--color-accent);
  margin-bottom: var(--space-3);
}

.kicker--onDark {
  color: var(--blue-bright);
}

.head {
  margin-bottom: clamp(var(--space-8), 4vw, var(--space-12));
  max-width: 62ch;
}

.head__title {
  font-size: clamp(28px, 3.8vw, 42px);
  font-weight: 700;
  /* Two of these put a phrase in blue, and an unbalanced break strands it on a
     line of its own where it reads as a mistake rather than as emphasis. */
  text-wrap: balance;
}

.head__lede {
  margin-top: var(--space-4);
  font-size: clamp(16px, 1.5vw, 18px);
  color: var(--color-ink-secondary);
  max-width: 58ch;
}

/* The one word a heading puts in blue. */
.accent {
  color: var(--color-accent);
}

.band {
  padding-block: clamp(3.5rem, 7vw, 6rem);
}

/* The quiet one — the limitations. A tint rather than a colour, so the section
   reads as an aside without becoming a warning box. */
.band--quiet {
  background: var(--color-canvas);
  border-block: 1px solid var(--color-hairline);
}

/* A dark band, with the family's own ground and inks. */
.band--dark {
  background: var(--grad-band);
  color: var(--on-dark);
}

.band--dark .kicker {
  color: var(--blue-bright);
}

.band--dark .head__lede {
  color: var(--on-dark-2);
}

/* --- Hero ---------------------------------------------------------------- */

.hero {
  background: var(--grad-hero);
  color: var(--on-dark);
  padding-block: clamp(3rem, 6vw, 5rem) clamp(3rem, 6vw, 4.5rem);
  overflow: hidden;
}

.hero__inner {
  display: grid;
  gap: clamp(3rem, 6vw, 4.5rem);
  align-items: center;
}

/* Two columns only when the device can be a device rather than a sliver. */
@media (min-width: 940px) {
  .hero__inner {
    grid-template-columns: 1.12fr 0.88fr;
  }
}

.hero__title {
  /* TWO LINES, AND THE FIRST OF THEM IS "RACE COMMUNICATION" — eighteen
     characters that have to hold one line, or the blue second line stops being
     the second line and the whole shape collapses. 50px is where a
     NON-condensed fallback still fits them in this column; Barlow Condensed,
     when a visitor has it, simply has room to spare. */
  font-size: clamp(38px, 4.4vw, 50px);
  font-weight: 700;
  text-transform: uppercase;
  line-height: 1;
  letter-spacing: 0.005em;
}

/* The second line, in blue. It is the whole reason the headline is two lines. */
.hero__accent {
  color: var(--blue-bright);
}

.hero__lede {
  margin-top: var(--space-5);
  font-size: clamp(16.5px, 1.6vw, 18.5px);
  color: var(--on-dark-2);
  max-width: 50ch;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-8);
}

.hero__note {
  margin-top: var(--space-5);
  font-size: 14px;
  color: var(--on-dark-3);
  max-width: 46ch;
}

/* On a phone the two buttons sit at different widths and leave a ragged edge
   down the fold. Full width squares them up and gives each a target a thumb
   cannot miss. */
@media (max-width: 560px) {
  .hero__actions {
    display: grid;
    gap: var(--space-3);
  }
  .hero__actions .btn {
    text-align: center;
  }
}

.hero__art {
  /* The device is the second thing read, never the reason somebody has to
     scroll to reach the buttons. */
  order: 1;
  display: flex;
  justify-content: center;
}

/* --- The three points under the hero ------------------------------------- */

.points {
  list-style: none;
  margin: clamp(3rem, 5vw, 4.5rem) 0 0;
  padding: clamp(var(--space-6), 3vw, var(--space-8)) 0 0;
  border-top: 1px solid var(--hair-dark);
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
}

.point__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-bottom: var(--space-4);
  border-radius: var(--radius-md);
  color: var(--blue-bright);
  background: rgba(59, 130, 246, 0.14);
  border: 1px solid rgba(96, 165, 250, 0.28);
}

.point__title {
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 700;
  margin-bottom: var(--space-2);
  color: var(--on-dark);
}

.point__body {
  font-size: 15px;
  color: var(--on-dark-2);
  max-width: 34ch;
}

/* --- The device ---------------------------------------------------------- */

/*
 * A PHONE BUILT FROM ELEMENTS. See the note in pages/index.ts for why it is not
 * a screenshot. Sized in one place — the phone's width — and everything inside
 * is relative to it, so the whole thing scales by changing one number.
 */
.device {
  position: relative;
  width: min(320px, 78vw);
}

/* The light behind it. Sits under the phone and is what makes the device look
   lit BY the section rather than pasted onto it. */
.device::before {
  content: "";
  position: absolute;
  inset: -18% -22%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(59, 130, 246, 0.4), transparent 72%);
  filter: blur(6px);
  z-index: 0;
}

.device__phone {
  position: relative;
  z-index: 1;
  padding: 10px;
  border-radius: 42px;
  background: linear-gradient(160deg, #2b3a52, #0d1725 60%);
  box-shadow: var(--lift);
}

.device__screen {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-3) var(--space-4);
  border-radius: 33px;
  background: linear-gradient(180deg, #0a1424 0%, #071020 100%);
  border: 1px solid rgba(147, 178, 224, 0.12);
}

.ui__status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: var(--space-3);
  font-size: 11px;
  font-weight: 700;
  color: var(--on-dark-2);
  font-feature-settings: var(--font-feature-data);
}

.ui__bars {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 10px;
}
.ui__bars i {
  width: 2.5px;
  background: var(--on-dark-2);
  border-radius: 1px;
}
.ui__bars i:nth-child(1) {
  height: 40%;
}
.ui__bars i:nth-child(2) {
  height: 60%;
}
.ui__bars i:nth-child(3) {
  height: 80%;
}
.ui__bars i:nth-child(4) {
  height: 100%;
}

/* Who is on the channel — the line the button belongs to. */
.ui__now {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: rgba(59, 130, 246, 0.1);
  border: 1px solid rgba(96, 165, 250, 0.22);
}

.ui__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background: #34d399;
  box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.18);
  flex: 0 0 auto;
}

.ui__nowText {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  margin-right: auto;
  min-width: 0;
}
.ui__nowText strong {
  font-size: 13px;
  color: var(--on-dark);
}
.ui__nowText small {
  font-size: 10.5px;
  color: var(--on-dark-3);
}

.ui__live {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--blue-bright);
}

/* THE BUTTON. */
.ptt {
  position: relative;
  display: grid;
  place-items: center;
  height: 168px;
  margin-block: var(--space-2);
}

.ptt__core {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 116px;
  height: 116px;
  border-radius: var(--radius-full);
  background: radial-gradient(circle at 50% 34%, #1b3f6e, #0b1c33);
  border: 2px solid var(--blue);
  box-shadow:
    inset 0 0 24px rgba(59, 130, 246, 0.34),
    var(--glow);
}

.ptt__label {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 700;
  line-height: 1.06;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--on-dark);
  text-align: center;
}

/* Three rings, ONE keyframe, staggered. Three separate animations would read as
   three lights blinking; one travelling outwards reads as a transmission. */
.ptt__ring {
  position: absolute;
  border-radius: var(--radius-full);
  border: 1.5px solid rgba(96, 165, 250, 0.5);
  width: 116px;
  height: 116px;
  animation: ptt-pulse 2800ms var(--ease-out) infinite;
}
.ptt__ring--b {
  animation-delay: 900ms;
}
.ptt__ring--c {
  animation-delay: 1800ms;
}

@keyframes ptt-pulse {
  0% {
    transform: scale(1);
    opacity: 0.55;
  }
  80%,
  100% {
    transform: scale(1.72);
    opacity: 0;
  }
}

.ui__heading {
  padding-inline: var(--space-2);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--on-dark-3);
}

.ui__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 6px;
}

.ui__row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 9px var(--space-3);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.035);
  border: 1px solid rgba(147, 178, 224, 0.08);
}

.ui__row--on {
  background: rgba(59, 130, 246, 0.14);
  border-color: rgba(96, 165, 250, 0.3);
}

.ui__rowDot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-full);
  background: var(--on-dark-3);
  flex: 0 0 auto;
}
.ui__row--on .ui__rowDot {
  background: #34d399;
}

.ui__rowName {
  font-size: 12px;
  font-weight: 600;
  color: var(--on-dark);
  margin-right: auto;
}

.ui__rowMeta {
  font-size: 10px;
  color: var(--on-dark-3);
}

/* --- Feature cards ------------------------------------------------------- */

.cards {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
}

.card {
  padding: clamp(var(--space-5), 2.5vw, var(--space-6));
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  transition:
    border-color var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out);
}

.card:hover {
  border-color: color-mix(in srgb, var(--color-accent) 45%, transparent);
  transform: translateY(-3px);
  box-shadow: 0 14px 34px rgba(11, 91, 215, 0.12);
}

.card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  background: var(--color-accent-soft);
  color: var(--color-accent);
  margin-bottom: var(--space-4);
}

.card__title {
  font-size: 21px;
  font-weight: 700;
  margin-bottom: var(--space-2);
}

.card__body {
  color: var(--color-ink-secondary);
  font-size: 15.5px;
}

/* --- Roles --------------------------------------------------------------- */

.roles {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
}

/*
 * A LIT PANEL WITH THE APP'S OWN GLYPH IN IT. The reference used photography;
 * there is none here, and a stock race photo is a picture of somebody else's
 * team. The glyph is watermark-sized behind the text and the gradient does the
 * rest — so the card is composed rather than borrowed.
 */
.role {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 232px;
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  border: 1px solid var(--hair-dark);
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(59, 130, 246, 0.28), transparent 60%),
    linear-gradient(180deg, #0d2039 0%, #06101f 100%);
  transition:
    border-color var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out);
}

.role:hover {
  border-color: rgba(96, 165, 250, 0.45);
  transform: translateY(-3px);
}

/* THE GLYPH CARRIES THE CARD, so it is drawn at the size a photograph would
   have been rather than at icon size — small and cornered, it read as a label
   on an empty panel. */
.role__glyph {
  position: absolute;
  top: 16%;
  left: var(--space-5);
  color: var(--blue-bright);
  opacity: 0.95;
  filter: drop-shadow(0 0 18px rgba(59, 130, 246, 0.55));
}
.role__glyph .icon {
  width: 46px;
  height: 46px;
}

/* The big soft wash behind the glyph — the thing standing in for a photograph's
   depth of field. */
.role::after {
  content: "";
  position: absolute;
  top: -30%;
  right: -30%;
  width: 75%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(59, 130, 246, 0.3), transparent 70%);
  pointer-events: none;
}

/* A scrim between the wash and the words. Without it the title sits directly on
   a radial gradient and its contrast changes across its own length. */
.role__text {
  position: relative;
  z-index: 1;
}

.role__text::before {
  content: "";
  position: absolute;
  inset: -60% -8% -30%;
  z-index: -1;
  background: linear-gradient(180deg, transparent, rgba(4, 10, 20, 0.72) 55%);
  pointer-events: none;
}

.role__title {
  font-size: 21px;
  font-weight: 700;
  color: var(--on-dark);
  margin-bottom: var(--space-2);
}

.role__body {
  font-size: 14.5px;
  color: var(--on-dark-2);
}

/* --- Platforms ----------------------------------------------------------- */

.platform {
  display: grid;
  gap: clamp(var(--space-8), 5vw, var(--space-12));
  align-items: center;
}

@media (min-width: 900px) {
  .platform {
    grid-template-columns: 1fr 1fr;
  }
}

.platform .head {
  margin-bottom: var(--space-6);
}

.platform__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.platform__item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-3) var(--space-5);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}

.platform__item strong {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 700;
}

.platform__item span {
  font-size: 13px;
  color: var(--color-ink-secondary);
}

/*
 * THREE SCREENS, SUGGESTED RATHER THAN DRAWN.
 *
 * They were plain rounded rectangles first, and three dark blobs in a row read
 * as a rendering failure rather than as devices. What makes a shape a screen is
 * not detail, it is the two things every screen has: a chrome bar across the
 * top and rows of something under it. Both are pseudo-elements — a hairline bar
 * and a repeating gradient — so the suggestion costs no markup and no request,
 * and it deliberately stops well short of pretending to be a screenshot.
 */
.platform__art {
  position: relative;
  height: clamp(230px, 30vw, 300px);
}

.slab {
  position: absolute;
  overflow: hidden;
  border-radius: 12px;
  background:
    radial-gradient(80% 60% at 30% 0%, rgba(59, 130, 246, 0.35), transparent 60%),
    linear-gradient(160deg, #10243f, #060f1c);
  border: 1px solid var(--hair-dark);
  box-shadow: var(--lift);
}

/* The chrome bar. */
.slab::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 13%;
  min-height: 9px;
  background: rgba(147, 178, 224, 0.1);
  border-bottom: 1px solid var(--hair-dark);
}

/* The rows under it. A repeating gradient rather than elements, and faint
   enough to read as content without ever resolving into words. */
.slab::after {
  content: "";
  position: absolute;
  inset: 22% 14% 16% 10%;
  background: repeating-linear-gradient(
    180deg,
    rgba(159, 179, 208, 0.22) 0,
    rgba(159, 179, 208, 0.22) 3px,
    transparent 3px,
    transparent 13px
  );
  -webkit-mask-image: linear-gradient(180deg, #000 40%, transparent);
  mask-image: linear-gradient(180deg, #000 40%, transparent);
}

/* The phone is too narrow for rows to read as anything but noise. */
.slab--phone::after {
  inset: 26% 22% 20% 22%;
}

.slab--desk {
  left: 6%;
  top: 12%;
  width: 74%;
  height: 66%;
}

.slab--tab {
  right: 4%;
  top: 0;
  width: 34%;
  height: 58%;
  border-radius: 14px;
}

.slab--phone {
  left: 0;
  bottom: 0;
  width: 20%;
  height: 52%;
  border-radius: 16px;
}

/* --- What it won't do ---------------------------------------------------- */

/*
 * Set as a notice, not as a footnote. The amber rule is the caution colour used
 * ONCE on the whole page, which is the only way a warning colour still means
 * anything by the time somebody reaches it.
 */
.limits {
  border-top: 2px solid var(--color-caution);
}

.limit {
  display: grid;
  gap: var(--space-2);
  padding-block: var(--space-6);
  border-bottom: 1px solid var(--color-hairline);
}

@media (min-width: 820px) {
  .limit {
    grid-template-columns: minmax(200px, 0.38fr) 1fr;
    gap: var(--space-6);
    align-items: start;
  }
}

.limit__head {
  font-size: clamp(18px, 1.9vw, 22px);
  font-weight: 600;
  color: var(--color-ink);
}

.limit__body {
  color: var(--color-ink-secondary);
  font-size: 15.5px;
  max-width: 66ch;
}

/* --- The ask ------------------------------------------------------------- */

.cta {
  background: var(--grad-cta);
  color: var(--on-dark);
  padding-block: clamp(3.5rem, 7vw, 5.5rem);
}

.cta__inner {
  display: grid;
  gap: clamp(var(--space-8), 5vw, var(--space-12));
  align-items: center;
}

@media (min-width: 880px) {
  .cta__inner {
    grid-template-columns: 1fr 0.9fr;
  }
}

.cta__title {
  font-size: clamp(30px, 4.4vw, 46px);
  font-weight: 700;
  text-transform: uppercase;
}

.cta__body {
  margin-block: var(--space-4) var(--space-6);
  color: var(--on-dark-2);
  max-width: 44ch;
}

/* The stat block's shape, holding facts instead of figures. */
.promises {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}

.promise {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid var(--hair-dark);
  background: rgba(255, 255, 255, 0.04);
}

.promise__title {
  font-family: var(--font-display);
  font-size: clamp(21px, 2.4vw, 26px);
  font-weight: 700;
  color: var(--blue-bright);
}

.promise__body {
  font-size: 14px;
  color: var(--on-dark-2);
}

/* --- Footer -------------------------------------------------------------- */

.footer {
  padding-block: var(--space-10);
}

.footer__inner {
  max-width: 1140px;
  margin-inline: auto;
  padding-inline: var(--space-5);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  justify-content: space-between;
  align-items: start;
}

.footer__brand .brand {
  width: 132px;
}

.footer__tagline {
  margin-top: var(--space-3);
  font-size: 14px;
  color: var(--color-ink-secondary);
  max-width: 34ch;
}

.footer__links {
  display: grid;
  gap: var(--space-2);
  font-size: 14px;
}

.footer__links a {
  text-decoration: none;
}

.footer__links a:hover {
  text-decoration: underline;
}

.footer__legal {
  max-width: 1140px;
  margin-inline: auto;
  padding-inline: var(--space-5);
  margin-top: var(--space-8);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-hairline);
  font-size: 13px;
  color: var(--color-ink-faint);
}

/* --- Motion -------------------------------------------------------------- */

/*
 * `animation: none`, NOT a duration of nothing. Zeroing the duration runs each
 * keyframe set to its END state, and the push-to-talk rings end at opacity 0 —
 * which would delete them from the drawing for exactly the readers who most
 * need it to still make sense. Switching the animation off leaves every element
 * at the value its own rules give it, and the page is simply still.
 */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation: none !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------------------------------------------------------------------------
   PROSE — the legal and support pages.

   The landing page is a sequence of full-bleed bands with their own gradients;
   these two are one column of text that somebody has to be able to READ, often
   on a phone, often while annoyed. So the rules here are about measure and
   rhythm rather than atmosphere, and they deliberately reuse `.band--quiet`
   underneath rather than inventing a third ground.

   THE MEASURE IS IN ch, NOT px, for the same reason the display sizes are:
   Barlow Condensed and Inter are named but not self-hosted, so the face that
   actually renders is usually the system stack. 68ch is a comfortable line in
   whichever one arrives; 640px is a comfortable line in exactly one of them.
   --------------------------------------------------------------------------- */

.prose-band {
  /* A long document wants air above the first line and a lot below the last,
     so the footer does not crowd the final sentence. */
  padding-block: var(--space-12) var(--space-12);
}

.prose {
  max-width: 68ch;
  color: var(--color-ink);
}

.prose__title {
  font-family: var(--font-display);
  font-size: clamp(2rem, 1.4rem + 2.6vw, 3rem);
  line-height: 1.05;
  margin: var(--space-2) 0 var(--space-4);
}

.prose__lede {
  font-size: 1.125rem;
  line-height: 1.55;
  color: var(--color-ink-secondary);
  margin: 0 0 var(--space-4);
}

.prose__meta {
  font-size: 0.875rem;
  color: var(--color-ink-faint);
  margin: 0 0 var(--space-8);
}

/* The table of contents.

   A plain wrapped list of links, not a sidebar: a sticky sidebar on a document
   this short is furniture competing with the text, and it has nowhere to go on
   a phone. `scroll-padding-top` on <html> already stops an anchor landing
   underneath the sticky masthead, so these jump correctly for free. */
.prose__toc {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-10);
  background: var(--color-surface);
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-lg);
}

.prose__toc a {
  font-size: 0.9375rem;
  color: var(--color-accent);
  text-decoration: none;
}

.prose__toc a:hover {
  text-decoration: underline;
}

.prose__section {
  /* Enough that two sections never read as one, without a rule between them. */
  margin-bottom: var(--space-10);
}

.prose__section h2 {
  font-family: var(--font-display);
  font-size: 1.75rem;
  line-height: 1.15;
  margin: 0 0 var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-hairline);
}

.prose__section h3 {
  font-size: 1.0625rem;
  font-weight: 650;
  margin: var(--space-6) 0 var(--space-2);
}

.prose__section p {
  line-height: 1.65;
  margin: 0 0 var(--space-4);
}

.prose__section ul {
  line-height: 1.65;
  margin: 0 0 var(--space-4);
  padding-left: 1.25rem;
}

.prose__section li {
  margin-bottom: var(--space-2);
}

.prose__section a {
  color: var(--color-accent);
}

.prose__section strong {
  font-weight: 650;
}

/* The address block on the support page.

   Its own card rather than a line in a paragraph, because Apple's guideline 1.2
   asks for PUBLISHED contact information and a reviewer should not have to hunt
   for it — and because the person reading this page usually arrived wanting
   exactly this one thing. */
.contact-card {
  padding: var(--space-5) var(--space-6);
  margin-bottom: var(--space-10);
  background: var(--color-accent-soft);
  border-radius: var(--radius-lg);
}

.contact-card__label {
  font-family: var(--font-display);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-ink-secondary);
  margin: 0 0 var(--space-1);
}

.contact-card__value {
  font-size: 1.25rem;
  margin: 0 0 var(--space-3);
}

.contact-card__value a {
  color: var(--color-accent);
  font-weight: 650;
  text-decoration: none;
}

.contact-card__value a:hover {
  text-decoration: underline;
}

.contact-card__note {
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--color-ink-secondary);
  margin: 0;
  /* Same reason as `.qa__a` — see the note there. This one is the line the
     detector was actually measuring: `.qa__a` was capped first and the finding
     did not move, which is what named this element rather than that one. */
  max-width: 64ch;
}

.qa {
  margin-bottom: var(--space-6);
}

.qa__q {
  font-size: 1.0625rem;
  font-weight: 650;
  margin: 0 0 var(--space-2);
}

.qa__a {
  line-height: 1.65;
  margin: 0;
  color: var(--color-ink-secondary);
  /* ITS OWN MEASURE, because `ch` is a font-size-relative unit and this text is
     not the size the container was measured at. `.prose` caps at 68ch computed
     against 1rem; an answer set at 0.9375rem inside that box fits about 72
     "0"-widths, and since average glyphs are narrower than a zero, roughly 85
     actual characters — past the ~80 where the eye starts losing its place
     tracking back. Measured at 1280px: 85 chars/line before this, and the
     paragraphs at full size were never the problem. */
  max-width: 64ch;
}

.qa__a a {
  color: var(--color-accent);
}
