/* recaudit.app — the whole site's stylesheet. No build step, no framework.
 *
 * ⚠️ EVERY COLOUR LIVES ON :root BELOW, AND NOWHERE ELSE IN THIS FILE OR IN
 * ANY HTML FILE. `landing/check-contrast.mjs` parses the :root block out of
 * this file and measures the pairs it was taught; a colour written inline in a
 * rule, or in a `style=` attribute, is measured by nobody. `src/theme/` is
 * exempt from the app's colour-literal gate because it IS the palette — this
 * block is the same thing for the website.
 *
 * Values are copied from `src/theme/tokens.ts` so the site and the app agree.
 * Each one names its token. Do not retune one here without retuning it there.
 *
 * ⚠️ THE EXCEPTION, AND IT IS THE ONE THING TO UNDERSTAND BEFORE EDITING THIS
 * FILE: a handful of properties below are COMPOSITES, not tokens — a
 * translucent token over a known ground, flattened to the opaque colour the
 * browser actually paints. Each is marked `COMPOSITE` and carries the
 * arithmetic that produced it. They exist because `rgba(255,255,255,0.09)` over
 * a purple is a LIGHTER ground, not a protective one, and a gate that measured
 * the token rather than the composite would be measuring a colour nobody sees.
 * Change the alpha and you must redo the arithmetic — nothing recomputes it.
 */

:root {
  /* ── Surfaces — colors.surface / .bg / .bgElevated / .chipFill, and the two
   * stops of gradients.screen (screenLocations = [0, 0.22]). */
  --surface:     #1E1F21;   /* colors.surface   — the page ground            */
  --groove:      #1B1C1E;   /* colors.bg        — the darker footer groove   */
  --elevated:    #242527;   /* colors.bgElevated— raised cards               */
  --chip:        #2F3033;   /* colors.chipFill  — the status pill            */
  --screen-top:  #253233;   /* gradients.screen[0] — LIGHTER stop            */
  --screen-bot:  #1E1F21;   /* gradients.screen[1]                           */

  /* ── The composer family — the app's purple→navy identity, added 2026-10-14.
   * `gradients.composer` in tokens.ts, pixel-sampled from the reference's
   * go-live composer. In the APP it is screen-scoped (the composer only); on
   * this SITE it is scoped the same way — the home page's hero field, and
   * nothing else. The legal documents keep `gradients.screen` above, which is
   * what the app's other 40 screens use. Sober pages, sober ground.
   *
   * ⚠️ `--violet` IS THE BRIGHTEST STOP AND IT IS BRIGHT. tokens.ts measures
   * `colors.text` at 4.80:1 on it, `textMuted` at 1.97:1 and `inkOnAccent` at
   * 2.91:1 — so most of this palette CANNOT sit on it undamped. Nothing on this
   * site paints it at full strength; it appears only inside `--bloom-violet`. */
  --violet-deep: #53276F;   /* gradients.composer[0]                         */
  --violet:      #7D48CF;   /* gradients.composer[1] — BRIGHTEST stop        */
  --indigo:      #3C469C;   /* gradients.composer[2]                         */
  --night:       #0D0724;   /* gradients.composer[3]                         */

  /* ── Ink */
  --text:        #EBECEE;   /* colors.text                                   */
  --muted:       #98989B;   /* colors.textMuted — DERIVED in tokens.ts, read
                               the note there before moving it              */
  /* colors.composerMuted. The secondary ink that EXISTS because `--muted`
   * cannot survive a violet ground — 2.90:1 on the hero field against a 4.5
   * body floor. Every piece of secondary text on the hero takes this. */
  --composer-ink: #DED6EC;

  /* ── Accent family */
  --accent:      #16DCCE;   /* colors.accent     — eyebrows, glyphs, the dot */
  --link:        #2DDEDF;   /* colors.accentSoft — links only                */
  --cta-from:    #26E3D7;   /* gradients.cta[0] / colors.createFrom          */
  --cta-to:      #08D5E2;   /* gradients.cta[1] / colors.createTo            */
  --ink-on-accent: #1E1F21; /* colors.inkOnAccent — DARK ink on a bright
                               accent fill. CLAUDE.md § "Filled accent
                               buttons use DARK INK". White here is 1.61:1. */

  /* ── Feature-tile fills. `colors.tile*` in tokens.ts, where they are the
   * app's own flat icon-tile fills for menu rows — this is the same role.
   * EVERY ONE TAKES DARK INK: each is a bright fill, and white on the darkest
   * of them is 3.33:1. `--tile-live` is `colors.danger` and is the one red on
   * the page, used where CLAUDE.md § "Red is still correct for semantics"
   * says red belongs — a recording indicator. */
  --tile-live:   #FF6690;   /* colors.danger   — going live = recording      */
  --tile-watch:  #2FDDD4;   /* colors.tileTeal                               */
  --tile-msg:    #2E8CFF;   /* colors.tileBlue                               */
  --tile-gifts:  #F8C250;   /* colors.tileAmber                              */
  --tile-levels: #EDBB6F;   /* colors.gold                                   */
  --tile-fams:   #F494C2;   /* colors.tilePink                               */

  /* ── Lines */
  --outline:     #84858A;   /* colors.outline — delineates a CONTROL         */
  --hairline:    #2A2C2E;   /* colors.border  — separates STRUCTURE only     */
  --focus:       #EBECEE;   /* colors.text, reused as the focus ring         */

  --white:       #FFFFFF;   /* measured against the CTA fill as a recorded
                               counter-example; not painted anywhere        */

  /* ── TRANSLUCENT LAYERS. Not inks and not grounds: each one is composited
   * before anything is measured, and the flattened result is the `COMPOSITE`
   * block below. Listed here so the alpha and the token are in one place. */
  --hero-base:    rgba(83, 39, 111, 0.95);   /* --violet-deep over --surface  */
  --hero-mid:     rgba(60, 70, 156, 0.62);   /* --indigo, the 34% knee        */
  --hero-fade:    rgba(13, 7, 36, 0.55);     /* --night, the 62% knee         */
  --bloom-violet: rgba(125, 72, 207, 0.24);  /* --violet, top-left bloom      */
  --bloom-teal:   rgba(22, 220, 206, 0.11);  /* --accent, top-right bloom     */
  --pill-scrim:   rgba(0, 0, 0, 0.30);       /* the hero pill DARKENS its
                                                ground — see the note below  */
  --card-wash:    rgba(83, 39, 111, 0.14);   /* --violet-deep over --elevated */
  --ring:         rgba(22, 220, 206, 0.30);  /* decorative hero rings         */
  --ring-faint:   rgba(125, 72, 207, 0.26);  /* decorative hero ring core     */
  --glow-cta:     rgba(8, 213, 226, 0.45);   /* decorative button glow        */
  --glow-violet:  rgba(83, 39, 111, 0.55);   /* decorative card hover glow    */
  --mask-ink:     #000000;                   /* ALPHA ONLY. Used in a
                                                mask-image, never painted.   */

  /* ── COMPOSITES. The grounds the browser actually paints, flattened.
   *
   * --hero-peak: the worst (lightest) point of the home page's hero field.
   *   --hero-base over --surface          = #50276B
   *   + --bloom-violet over that          = #5B2F83
   *   + --bloom-teal over that            = #53428B
   *   Conservative by construction: the two blooms are centred at opposite
   *   ends of the field and never actually reach full strength together.
   *   On it: --text 7.05 · --composer-ink 5.93 · --accent 4.83 · --link 5.01.
   *   ⛔ --muted is 2.90 and --outline 2.26 on this. NEITHER may appear on the
   *   hero; that is what --composer-ink and .btn--on-hero exist for.
   *
   * --hero-pill: --pill-scrim over --hero-peak = #3A2E61. The hero's status
   *   pill DARKENS its ground rather than lightening it — the same move
   *   `StreamComposerScreen`'s `cardOverPage` makes in the app, and for the
   *   same reason: white at 0.09 over a violet produces a LIGHTER ground on
   *   which nothing in this palette passes.
   *
   * --card-top: --card-wash over --elevated = #2B2531. The lighter stop of a
   *   feature card, so it is the worst case for --muted card body text (5.17).
   */
  --hero-peak:   #53428B;
  --hero-pill:   #3A2E61;
  --card-top:    #2B2531;

  /* Print. A legal document gets printed and signed off, and the dark page
   * would otherwise either waste a cartridge or be dropped to white by the
   * browser with the inks left where they are. Declared here so the print
   * pair is measured like every other. */
  --print-paper: #FFFFFF;
  --print-ink:   #000000;

  /* Non-colour tokens, from the same file. */
  --sp-xs: 4px;  --sp-sm: 8px;  --sp-md: 12px;
  --sp-lg: 16px; --sp-xl: 20px; --sp-xxl: 28px;     /* tokens.ts spacing */
  --r-sm: 10px; --r-md: 14px; --r-lg: 18px; --r-xl: 24px; --r-pill: 999px; /* radii */

  /* Type scale. One ramp, used by both the marketing page and the documents —
   * the documents inherit the scale and the surfaces, and none of the motion. */
  --t-eyebrow: 12px;
  --t-small:   14px;
  --t-body:    17px;
  --t-lede:    clamp(17px, 1.2vw + 13px, 21px);
  --t-h3:      19px;
  --t-h2:      clamp(26px, 3.4vw + 14px, 40px);
  --t-h1:      clamp(38px, 7.4vw + 12px, 72px);
  --t-doc-h1:  clamp(30px, 3vw + 18px, 44px);

  /* The app sets Poppins. A webfont would mean a third-party request from a
   * page whose whole subject is privacy, and self-hosting means committing
   * font binaries — so the site uses the reader's own UI font and keeps the
   * weights and tracking that Poppins is set at. See landing/README.md. */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
          Arial, "Noto Sans", sans-serif;
}

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

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

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--t-body);
  line-height: 1.6;
  color: var(--text);
  /* An explicit background, and the gradient's stops at the app's own
   * locations. `background-attachment: fixed` keeps the lighter stop at the
   * top of the viewport rather than the top of a 4000px legal document. */
  background-color: var(--surface);
  background-image: linear-gradient(180deg, var(--screen-top) 0%, var(--screen-bot) 22%);
  background-repeat: no-repeat;
  background-size: 100% 520px;
  -webkit-font-smoothing: antialiased;
}

/* ── THE HOME PAGE'S GROUND, and only the home page's ──────────────────────
 * `build.mjs` puts `class="home"` on the home page alone. The eleven legal
 * documents keep the body rule above: `gradients.screen`, flat, sober.
 *
 * Three layers, listed top-down the way CSS paints them. The flattened worst
 * case is --hero-peak; see its derivation on :root. */
body.home {
  background-image:
    radial-gradient(58% 48% at 88% 4%, var(--bloom-teal) 0%, transparent 72%),
    radial-gradient(68% 60% at 8% -6%, var(--bloom-violet) 0%, transparent 70%),
    linear-gradient(180deg,
      var(--hero-base) 0%,
      var(--hero-mid) 34%,
      var(--hero-fade) 62%,
      transparent 100%);
  background-size: 100% 940px;
}

img { max-width: 100%; height: auto; }

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

/* Colour is never the only cue for a link: body links stay underlined, and the
 * ones that are not (nav, buttons, card rows) are blocks with their own shape. */
.prose a, p a { text-underline-offset: 3px; }

:focus-visible {
  /* The ring is OFFSET so it is drawn on the page ground, never on a filled
   * button — on the CTA fill it would read 1.36:1. check-contrast.mjs records
   * that pair as the reason this offset exists. */
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

/* ── layout ────────────────────────────────────────────────────────────── */
.wrap {
  width: 100%;
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 var(--sp-lg);      /* 16px gutter at phone width */
}

.wrap--reading { max-width: 720px; }

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: var(--sp-lg);
  top: var(--sp-lg);
  z-index: 10;
  background: var(--elevated);
  color: var(--text);
  padding: var(--sp-sm) var(--sp-md);
  border-radius: var(--r-sm);
}

/* ── header ────────────────────────────────────────────────────────────── */
.site-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-lg);
  padding: var(--sp-xl) 0;
}

/* At 320px the brand lock-up plus three nav links do not fit on one line at
 * the desktop sizes. Measured, not assumed: 301px of content in a 288px
 * column. The header is the one place on the site where that is true. */
@media (max-width: 430px) {
  .site-head { gap: var(--sp-md); }
  .brand { font-size: 17px; gap: var(--sp-sm); }
  .brand__mark { width: 30px; height: 30px; }
  .site-nav { gap: var(--sp-md); font-size: 14px; }
  /* Two pills of different widths stacked on a narrow column read as ragged.
   * One column, both full width. */
  .actions { display: grid; grid-template-columns: 1fr; }
  .btn { width: 100%; }
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-md);
  color: var(--text);
  text-decoration: none;
  font-weight: 700;
  font-size: 19px;
  letter-spacing: -0.015em;
}

/* ⚠️ `assets/icon.png` is the app icon and it is RED AND BLACK ON WHITE — the
 * superseded #EB1922 identity, on a site whose recorded palette is teal. It is
 * left exactly as it is: inventing a replacement mark is not this file's job,
 * and recolouring an app icon with a CSS filter would produce a second,
 * undocumented identity. All this rule does is seat the white tile on the dark
 * ground instead of letting it float. See landing/README.md. */
.brand__mark {
  width: 34px;
  height: 34px;
  /* The shape a phone renders an app icon at. The PNG this points at now has
   * the same 22% baked into its alpha channel (`landing/round-icon.mjs`),
   * because a TAB FAVICON CANNOT BE STYLED FROM HERE and a tab rounded to one
   * curve beside a header rounded to another is worse than either alone. The
   * two numbers must move together; this one is the original and the generator
   * cites it. Keeping the clip costs nothing and keeps the header mark rounded
   * if the PNG is ever replaced with a square one. */
  border-radius: 22%;
  display: block;
}

.site-nav {
  display: flex;
  gap: var(--sp-xl);
  font-size: 15px;
}
.site-nav a {
  color: var(--link);
  text-decoration: none;
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
  transition: border-color 0.18s ease;
}
.site-nav a:hover { border-bottom-color: currentColor; }

/* ── hero ──────────────────────────────────────────────────────────────── */
.hero {
  padding: clamp(24px, 4vw, 44px) 0 clamp(24px, 3vw, 44px);
}

.hero__copy { min-width: 0; }

@media (min-width: 900px) {
  .hero {
    display: grid;
    grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
    gap: clamp(24px, 4vw, 56px);
    align-items: center;
  }
}

.eyebrow {
  margin: 0 0 var(--sp-md);
  color: var(--accent);
  font-size: var(--t-eyebrow);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

h1 {
  margin: 0 0 var(--sp-lg);
  font-size: var(--t-h1);
  line-height: 1.06;
  letter-spacing: -0.035em;
  font-weight: 800;
}

/* The one gradient-filled text on the site, on the h1's second line only.
 * ⚠️ FALLBACK FIRST: without `background-clip: text` the span keeps a solid
 * --accent and nothing disappears. The transparent fill is only ever applied
 * inside the @supports below. Both gradient stops are measured against
 * --hero-peak at the 3:1 LARGE-TEXT floor, which is the right floor here —
 * the span is never smaller than 38px. */
.grad { color: var(--accent); }

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .grad {
    background-image: linear-gradient(100deg, var(--cta-from) 0%, var(--cta-to) 62%, var(--link) 100%);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
  }
}

.lede {
  margin: 0 0 var(--sp-xxl);
  max-width: 34em;
  font-size: var(--t-lede);
  line-height: 1.55;
  color: var(--muted);
}

/* ⛔ ON THE HERO THE SECONDARY INK CHANGES, AND IT IS NOT A PREFERENCE.
 * --muted reads 2.90:1 on --hero-peak against a 4.5 body floor. */
.lede--hero { color: var(--composer-ink); max-width: 31em; }

/* ── the decorative broadcast rings ────────────────────────────────────────
 * Concentric rings and a core, drawn entirely from the palette. Decorative:
 * aria-hidden in the markup, below 900px it is not rendered at all, and it
 * carries no information the copy does not. It is NOT a screenshot and not a
 * depiction of any app screen. */
.hero__signal {
  display: none;
  justify-self: center;
  width: min(380px, 100%);
  aspect-ratio: 1;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%, var(--accent) 0 7px, transparent 7px),
    radial-gradient(circle at 50% 50%, var(--ring-faint) 0 18%, transparent 62%),
    repeating-radial-gradient(circle at 50% 50%,
      transparent 0 33px, var(--ring) 33px 34px, transparent 34px 35px);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, var(--mask-ink) 0 52%, transparent 80%);
          mask-image: radial-gradient(circle at 50% 50%, var(--mask-ink) 0 52%, transparent 80%);
}

/* ⚠️ THIS MEDIA QUERY MUST STAY BELOW THE RULE ABOVE. It was first written
 * beside the `.hero` grid rule, several hundred lines earlier, where the later
 * `display: none` won on source order alone and the composition never
 * rendered at any width. Same specificity, later wins. */
@media (min-width: 900px) {
  .hero__signal { display: block; }
}

/* ── the status pill, in place of store buttons ───────────────────────── */
.status {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-sm) var(--sp-lg);
  border-radius: var(--r-pill);
  background: var(--chip);
  color: var(--text);
  font-size: 15px;
  font-weight: 600;
}

/* On the hero the pill sits on violet, so it DARKENS its ground. --chip there
 * would be a grey plate on a purple field; --pill-scrim keeps the hue and
 * takes the ground down. Flattened worst case is --hero-pill. */
.status--hero { background: var(--pill-scrim); }

.status__dot {
  position: relative;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}

/* The halo. Decorative — the DOT is the indicator and is measured; this is a
 * ring that expands out of it. It is the ONLY perpetual animation on the site,
 * it exists on the hero pill alone, and it is what a "live" dot does. */
.status--hero .status__dot::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  opacity: 0;
}

/* ── buttons ───────────────────────────────────────────────────────────── */
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-md);
  margin-top: var(--sp-xl);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 50px;                      /* touch target */
  padding: 0 var(--sp-xxl);
  border-radius: var(--r-pill);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.005em;
  text-decoration: none;
  border: 1px solid transparent;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}

.btn--primary {
  background-image: linear-gradient(100deg, var(--cta-from), var(--cta-to));
  color: var(--ink-on-accent);           /* DARK ink. Never white here. */
  box-shadow: 0 12px 30px -14px var(--glow-cta);
}
.btn--primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 18px 40px -14px var(--glow-cta);
}

.btn--ghost {
  border-color: var(--outline);
  color: var(--text);
}
.btn--ghost:hover { border-color: var(--text); transform: translateY(-2px); }

/* ⛔ --outline IS 2.26:1 ON THE HERO FIELD and cannot delineate a control
 * there. This is the same button with the one ink in this palette that
 * survives a violet ground: --composer-ink reads 5.93:1 on --hero-peak,
 * against a 3:1 non-text floor. */
.btn--on-hero { border-color: var(--composer-ink); }
.btn--on-hero:hover { border-color: var(--text); }

/* ── sections ──────────────────────────────────────────────────────────── */
section { padding: clamp(36px, 4.6vw, 64px) 0; }

h2 {
  margin: 0 0 var(--sp-md);
  font-size: var(--t-h2);
  line-height: 1.12;
  letter-spacing: -0.03em;
  font-weight: 700;
}

.section-lede {
  margin: 0 0 var(--sp-xxl);
  max-width: 40em;
  color: var(--muted);
}

/* ── feature grid ──────────────────────────────────────────────────────── */
.grid {
  display: grid;
  gap: var(--sp-lg);
  /* `min(260px, 100%)` rather than a bare 260px: at 320px the track would
   * otherwise refuse to shrink and the page would scroll sideways. */
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}

.card {
  position: relative;
  min-width: 0;
  background-image: linear-gradient(155deg, var(--card-top) 0%, var(--elevated) 68%);
  border: 1px solid var(--hairline);
  border-radius: var(--r-xl);
  padding: var(--sp-xl);
  transition: transform 0.22s ease, border-color 0.22s ease, box-shadow 0.22s ease;
}
.card:hover {
  transform: translateY(-4px);
  border-color: var(--outline);
  box-shadow: 0 22px 44px -26px var(--glow-violet);
}

.card h3 {
  margin: 0 0 var(--sp-sm);
  font-size: var(--t-h3);
  line-height: 1.3;
  letter-spacing: -0.015em;
  font-weight: 600;
}

.card p { margin: 0; color: var(--muted); font-size: 15px; }

/* The glyph tile. Was a bare teal glyph on the flat card; it is now a filled
 * rounded tile, which is what the app does with the same six ideas
 * (`colors.tile*` are "flat icon-tile fills for menu rows"). Every tile is a
 * bright fill and every glyph is --ink-on-accent: the floor is 3:1 for a
 * non-text indicator and the worst of the six clears 4.96. */
.card__glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  margin-bottom: var(--sp-lg);
  border-radius: var(--r-md);
  color: var(--ink-on-accent);
  transition: transform 0.22s ease;
}
.card__glyph svg { display: block; }
.card:hover .card__glyph { transform: translateY(-1px) scale(1.04); }

.card__glyph--live     { background: var(--tile-live); }
.card__glyph--watch    { background: var(--tile-watch); }
.card__glyph--messages { background: var(--tile-msg); }
.card__glyph--gifts    { background: var(--tile-gifts); }
.card__glyph--levels   { background: var(--tile-levels); }
.card__glyph--families { background: var(--tile-fams); }

/* ── the availability panel ────────────────────────────────────────────── */
.panel {
  background-image: linear-gradient(155deg, var(--card-top) 0%, var(--elevated) 70%);
  border: 1px solid var(--hairline);
  border-radius: var(--r-xl);
  padding: clamp(24px, 4vw, 44px);
}
.panel .section-lede { margin-bottom: var(--sp-xl); }

/* ── the legal index: a list of document rows ─────────────────────────── */
.doc-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-md);
}

.doc-list a {
  display: block;
  padding: var(--sp-lg) var(--sp-xl);
  background-image: linear-gradient(155deg, var(--card-top) 0%, var(--elevated) 70%);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  text-decoration: none;
  color: var(--link);
  font-weight: 600;
  transition: border-color 0.18s ease, transform 0.18s ease;
}
.doc-list a:hover { border-color: var(--outline); transform: translateX(2px); }
.doc-list span { display: block; color: var(--muted); font-weight: 400; font-size: var(--t-small); margin-top: 2px; }

/* ── long-form documents ──────────────────────────────────────────────── */
.doc-head { padding: var(--sp-xxl) 0 var(--sp-xl); }
.doc-head h1 { font-size: var(--t-doc-h1); line-height: 1.1; margin-bottom: var(--sp-sm); }

.back {
  display: inline-block;
  margin-bottom: var(--sp-lg);
  font-size: 15px;
  color: var(--link);
  text-decoration: none;
}
.back:hover { text-decoration: underline; text-underline-offset: 3px; }

.prose { padding-bottom: 48px; }

.prose h2 {
  margin: var(--sp-xxl) 0 var(--sp-sm);
  font-size: 20px;
  line-height: 1.35;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.prose p { margin: 0 0 var(--sp-lg); }

.prose hr {
  border: 0;
  border-top: 1px solid var(--hairline);
  margin: var(--sp-xxl) 0;
}

.revision {
  color: var(--muted);
  font-size: var(--t-small);
  margin: 0;
}

/* ── footer ────────────────────────────────────────────────────────────── */
.site-foot {
  background: var(--groove);
  border-top: 1px solid var(--hairline);
  padding: 48px 0;
  margin-top: 48px;
}

.foot-cols {
  display: grid;
  gap: var(--sp-xxl);
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
}

.foot-cols h2 {
  font-size: var(--t-eyebrow);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--sp-md);
}

.foot-links { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-sm); }
.foot-links a { color: var(--link); text-decoration: none; font-size: 15px; }
.foot-links a:hover { text-decoration: underline; text-underline-offset: 3px; }

.colophon {
  margin: var(--sp-xxl) 0 0;
  color: var(--muted);
  font-size: var(--t-small);
}

/* ── MOTION ────────────────────────────────────────────────────────────────
 * Everything that moves is declared in this one block, and the whole block is
 * inside `prefers-reduced-motion: no-preference`. Read that literally: with
 * reduced motion requested, none of these rules exist — the page is not
 * "animated at 0ms", it is the same page without the animation properties, so
 * nothing can be left mid-fade or stuck at `opacity: 0`.
 *
 * ⛔ NO JAVASCRIPT. The scroll reveal is CSS scroll-driven animation
 * (`animation-timeline: view()`), behind @supports: a browser without it gets
 * the finished page, not a blank one. `_headers` can therefore keep
 * `default-src 'none'`.
 *
 * ⛔ AND NOTHING HERE RUNS ON THE LEGAL PAGES except the hover transitions
 * already declared above. A privacy policy that reveals itself as you scroll
 * is a worse privacy policy. */
@keyframes rise {
  from { opacity: 0; transform: translate3d(0, 18px, 0); }
  to   { opacity: 1; transform: none; }
}

@keyframes halo {
  0%        { transform: scale(0.55); opacity: 0.55; }
  70%, 100% { transform: scale(1.7);  opacity: 0; }
}

@media (prefers-reduced-motion: no-preference) {

  /* One-shot entrance, hero only. `both` so the backwards fill holds the
   * element hidden through its delay and the forwards fill holds it visible
   * afterwards — it ends visible and stays there. */
  .rise { animation: rise 0.62s cubic-bezier(0.2, 0.7, 0.3, 1) both; }
  .rise--1 { animation-delay: 0.04s; }
  .rise--2 { animation-delay: 0.10s; }
  .rise--3 { animation-delay: 0.18s; }
  .rise--4 { animation-delay: 0.26s; }
  .rise--5 { animation-delay: 0.34s; }

  /* The one perpetual animation on the site: the halo on the hero's status
   * dot. Semantic rather than decorative — it is what marks the dot as a live
   * state — small, central, and nowhere near the edge of the eye. */
  .status--hero .status__dot::after { animation: halo 2.6s ease-out infinite; }

  @supports (animation-timeline: view()) {
    .reveal {
      animation: rise 1s linear both;
      animation-timeline: view();
      animation-range: entry 6% cover 30%;
    }
  }
}

/* ── print: legal documents get printed and signed off ────────────────── */
@media print {
  body, body.home {
    background: var(--print-paper);
    background-image: none;
    color: var(--print-ink);
    font-size: 11pt;
  }
  .site-head, .site-foot, .back, .skip, .hero__signal { display: none; }
  a { color: var(--print-ink); }
  .revision { color: var(--print-ink); }
  .wrap { max-width: none; padding: 0; }
  .card, .panel, .doc-list a { background-image: none; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation: none !important;
    animation-timeline: auto !important;
    transition: none !important;
  }
}
