/* tokens.css */
/* Design tokens — mirrored from the app's own skin, not invented here.

   The app's running UI is LIGHT: `lib/stream/skin.dart` sets a #F5F5F5 page,
   white cards, #1F1F1F ink and a #C62E2E accent. The store posters and the
   Nightstand surface are the DARK half of `lib/theme/tokens.dart` — #0A0A0F
   obsidian under an oxblood glow. This site uses both, in that order: a light
   reading ground with near-black bands where the product turns nocturnal.

   Rule: no BRAND colour may be hardcoded below this file. Use the token, or an
   rgb(var(--*-rgb) / n%) variant of one. Plain `#fff` is allowed where it means
   paper or "text on a coloured ground" and carries no brand meaning.

   IMPORTANT: Contrast: the app's --inkMuted (#5C5C5C) is fine at arm's length on a
   phone; on a web page at 14px it is 7.0:1 and safe, but the RED is not. The
   accent #C62E2E is 5.0:1 on white — fine for text — and 1.6:1 on the obsidian
   ground, which is why the dark bands use --accent-on-dark (#E86A6A, the app's
   own S.accentOnDark) for anything that carries meaning. Never put #C62E2E
   text on --ground. */
:root {
  /* Palette — light half (the app's own running skin) */
  --accent: #c62e2e;
  --accent-pressed: #a82424;
  --accent-deep: #7c1a1a;
  --accent-lift: #e86a6a;
  /* Text-safe red on light. #C62E2E is already 5.0:1 on white; this darker
     sibling is for red text on the --surface tint, where the ratio drops. */
  --accent-text: #a82424;
  --accent-dim: #f9eaea;

  --ink: #1f1f1f;
  --ink-soft: #3d3d3d;
  --muted: #5c5c5c;

  --surface: #f0eeee;
  --surface-alt: #e8e5e5;
  --hairline: #d8d5d5;
  --card: #ffffff;
  --canvas: #ffffff;
  --on-accent: #ffffff;

  /* Palette — dark half (lib/theme/tokens.dart: caseCanvas, oxblood*, brick) */
  --ground: #0a0a0f;
  --ground-floor: #05050a;
  --ground-warm: #140a0d;
  --ground-lift: #1a161c;
  --oxblood-deep: #4a0e12;
  --oxblood-mid: #9b1b2e;
  /* The floor for any red that carries meaning on the dark ground. */
  --brick: #e23d52;
  --accent-on-dark: #e86a6a;

  /* Verification ladder. Colour is REINFORCEMENT here, never the carrier —
     every label on this site pairs the hue with a word, exactly as the app's
     `verification.dart` requires. */
  --v-corroborated: #2f6b4f;
  --v-corroborated-ink: #275a42;
  --v-single: #6e7b8c;
  --v-single-ink: #4a5563;
  --v-developing: #9a6800;
  --v-developing-ink: #7a5200;
  --v-unverified: #a8541f;
  --v-unverified-ink: #8a4318;
  --v-disputed: #a82424;
  --v-disputed-ink: #8c1c1c;
  /* The two labels that are statements of provenance rather than steps on the
     ladder. They are quieter ON PURPOSE — but bare `.vlabel` made them read as
     unstyled, so the quietness now has its own colour rather than none. */
  --v-factcheck: #4a5f8a;
  --v-factcheck-ink: #3a4b6e;
  --v-social: #6b6b72;
  --v-social-ink: #55555c;

  /* Channel triplets so alpha variants stop re-typing the hex. */
  --accent-rgb: 198 46 46;
  --oxblood-mid-rgb: 155 27 46;
  --v-single-rgb: 110 123 140;
  --v-developing-rgb: 154 104 0;
  --v-unverified-rgb: 168 84 31;
  --v-disputed-rgb: 168 36 36;
  --v-factcheck-rgb: 74 95 138;
  --v-social-rgb: 107 107 114;
  --ink-rgb: 31 31 31;
  --ground-rgb: 10 10 15;
  --hairline-rgb: 216 213 213;

  /* Alpha helpers for use on the dark ground */
  /* Alphas follow the app's own floors (T.aTextMuted 0.88, aTextSubtle 0.72,
     aRimDecorative 0.16, aRimControl 0.35) — not Atmoglass's lower ones. */
  --white-06: rgba(255, 255, 255, 0.06);
  --white-10: rgba(255, 255, 255, 0.1);
  --white-16: rgba(255, 255, 255, 0.16);
  --white-24: rgba(255, 255, 255, 0.24);
  --white-35: rgba(255, 255, 255, 0.35);
  --white-64: rgba(255, 255, 255, 0.64);
  --white-72: rgba(255, 255, 255, 0.72);
  --white-88: rgba(255, 255, 255, 0.88);

  /* Two semantic pairs the components need. `--ok` is the corroborated green —
     it means "included / confirmed" and nothing else; `--amber` is developing.
     Both have a darker sibling for text, because the fill hues land at 2–3:1. */
  --ok: #2f6b4f;
  --ok-text: #275a42;
  --ok-rgb: 47 107 79;
  /* The corroborated green is a fill; on obsidian it needs the lifted sibling,
     the same swap the app makes with S.accentOnDark. */
  --ok-on-dark: #8fd0b0;
  --amber: #c98a3c;
  --amber-text: #7a5200;
  --tint-blush: #f7dede;
  --tint-error: #ffd9cd;

  /* Device chrome — the handset bezel drawn in CSS. Near-black, so a light app
     capture reads as the brightest thing in the frame. */
  --device-hi: #3a3438;
  --device-lo: #121115;
  --device-mid: #26232a;
  --device-glass: #0a0a0f;

  /* Type — Plus Jakarta Sans, the typeface the app ships (pubspec.yaml). */
  --font: 'PlusJakartaSans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --fs-display: clamp(2.5rem, 1.5rem + 3.4vw, 4.2rem);
  --fs-h1: clamp(2.15rem, 1.4rem + 2.8vw, 3.6rem);
  --fs-h2: clamp(1.72rem, 1.3rem + 1.7vw, 2.6rem);
  --fs-h3: clamp(1.15rem, 1.05rem + 0.4vw, 1.4rem);
  /* Card-level headings. There were five different values doing this one job
     (1.05 / 1.05 / 1.08 / 1.1 / 1.4rem), which is why two identical-looking
     grids felt subtly out of step. */
  --fs-h4: 1.06rem;
  --fs-lead: clamp(1.05rem, 0.98rem + 0.4vw, 1.25rem);
  --fs-body: 1rem;
  --fs-small: 0.875rem;
  --fs-eyebrow: 0.72rem;
  --lh-tight: 1.04;
  --lh-head: 1.15;
  --lh-body: 1.65;
  --track-eyebrow: 0.13em;

  /* Space */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;
  --section-y: clamp(72px, 8vw, 128px);
  --gutter: clamp(20px, 4vw, 40px);
  --measure: 62ch;
  --container: 1180px;
  --container-narrow: 760px;

  /* Geometry — the app's T.rCard / rGlass / rChip, in CSS. */
  --r-pill: 999px;
  --r-card: 24px;
  --r-chip: 12px;
  --r-icon: 14px;
  --r-sheet: 28px;
  --r-photo: 20px;

  /* Elevation */
  --shadow-sm: 0 2px 8px rgba(31, 31, 31, 0.06);
  --shadow-card: 0 8px 20px rgba(31, 31, 31, 0.09);
  --shadow-lift: 0 20px 48px rgba(31, 31, 31, 0.16);
  --shadow-accent: 0 12px 30px rgba(198, 46, 46, 0.28);
  --ring: 0 0 0 3px rgba(198, 46, 46, 0.3);

  /* Motion — the app's T.dFast/dBase/dSlow and T.cStandard. */
  --ease: cubic-bezier(0.2, 0, 0, 1);
  --ease-out-back: cubic-bezier(0.34, 1.3, 0.64, 1);
  --dur-fast: 160ms;
  --dur: 300ms;
  --dur-slow: 480ms;

  /* Layers */
  --z-nav: 60;
  --z-banner: 70;
  --z-modal: 90;
}

/* Film grain on every dark panel. An oxblood radial over near-black bands
   banding is visible on a wide display without it. */
:root {
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.42'/%3E%3C/svg%3E");
}

/* base.css */
/* Plus Jakarta Sans — the app's own typeface (pubspec.yaml), subset to the
   Latin ranges by scripts/assets.py. Static instances, not the variable file,
   for the same reason the app ships statics: a weight that renders differently
   per platform is not a typeface. */
@font-face {
  font-family: 'PlusJakartaSans';
  src: url('/assets/fonts/PlusJakartaSans-Regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'PlusJakartaSans';
  src: url('/assets/fonts/PlusJakartaSans-Medium.woff2') format('woff2');
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: 'PlusJakartaSans';
  src: url('/assets/fonts/PlusJakartaSans-SemiBold.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: 'PlusJakartaSans';
  src: url('/assets/fonts/PlusJakartaSans-Bold.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

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

/* Components set their own display, so [hidden] has to win outright. */
[hidden] {
  display: none !important;
}

html {
  /* One committed light brand ground — declared so a dark-OS visitor does not
     get UA-darkened range sliders and checkboxes on white cards. */
  color-scheme: light;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 96px;
}

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1,
h2,
h3,
h4 {
  margin: 0 0 var(--s-4);
  font-weight: 700;
  line-height: var(--lh-head);
  letter-spacing: -0.018em;
  text-wrap: balance;
}

h1 {
  font-size: var(--fs-h1);
  line-height: var(--lh-tight);
}
h2 {
  font-size: var(--fs-h2);
}
h3 {
  font-size: var(--fs-h3);
  letter-spacing: -0.01em;
}

p {
  margin: 0 0 var(--s-4);
  text-wrap: pretty;
}

p:last-child {
  margin-bottom: 0;
}

a {
  color: var(--accent-text);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color var(--dur-fast) var(--ease);
}

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

figure {
  margin: 0;
}

img,
svg,
video {
  max-width: 100%;
  height: auto;
  display: block;
}

ul,
ol {
  margin: 0 0 var(--s-4);
  padding-left: 1.2em;
}

li {
  margin-bottom: var(--s-2);
}

strong {
  font-weight: 600;
}

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 6px;
}

.on-accent :focus-visible,
.hero :focus-visible {
  outline-color: #fff;
}

::selection {
  background: var(--accent);
  color: #fff;
}

.skip-link {
  position: absolute;
  left: 50%;
  top: 0;
  translate: -50% -120%;
  z-index: var(--z-modal);
  padding: var(--s-3) var(--s-5);
  background: var(--ink);
  color: #fff;
  border-radius: 0 0 var(--r-chip) var(--r-chip);
  text-decoration: none;
  transition: translate var(--dur) var(--ease);
}

.skip-link:focus {
  translate: -50% 0;
  color: #fff;
}

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

/* layout.css */
.container {
  width: min(100% - var(--gutter) * 2, var(--container));
  margin-inline: auto;
}

.container--narrow {
  width: min(100% - var(--gutter) * 2, var(--container-narrow));
}

.section {
  position: relative;
  padding-block: var(--section-y);
}

.section--tight {
  padding-block: clamp(48px, 5vw, 80px);
}

/* Two sections on the same ground leave a ~240px seam that reads as a
   rendering fault. Both sides must match — halving the top padding where the
   colour actually changes just puts a headline hard against the join. */
.section:not(.section--blush):not(.section--accent)
  + .section:not(.section--blush):not(.section--accent),
.section--blush + .section--blush,
.section--accent + .section--accent {
  padding-top: calc(var(--section-y) * 0.5);
}

.section--blush {
  background: var(--surface);
}

/* The noir band. Obsidian ground with the oxblood glow rising from one
   corner — the same construction as the store posters (store/shots.json), so
   the site and the App Store listing read as one product. */
.section--accent {
  background:
    radial-gradient(80% 62% at 82% 4%, rgb(var(--oxblood-mid-rgb) / 55%) 0%, transparent 62%),
    linear-gradient(168deg, var(--ground-warm) 0%, var(--ground) 58%, var(--ground-floor) 100%);
  color: var(--on-accent);
  isolation: isolate;
}

.section--accent::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image: var(--grain);
  opacity: 0.16;
  mix-blend-mode: overlay;
  pointer-events: none;
  z-index: -1;
}

.section--accent h2,
.section--accent h3 {
  color: var(--on-accent);
}

.section--accent p {
  color: var(--white-88);
}

.section-head {
  max-width: 640px;
  margin-bottom: clamp(40px, 5vw, 64px);
}

.section-head--center {
  margin-inline: auto;
  text-align: center;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: var(--s-3);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--accent-text);
}

.eyebrow::before {
  content: '';
  width: 22px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
}

.section-head--center .eyebrow::before {
  display: none;
}

.on-accent .eyebrow,
.section--accent .eyebrow {
  color: var(--white-88);
}

.lead {
  font-size: var(--fs-lead);
  color: var(--ink-soft);
  max-width: var(--measure);
}

.section--accent .lead {
  color: var(--white-88);
}

/* An accent hue that carries meaning on the dark ground must be the lifted
   sibling — #C62E2E is 1.6:1 on obsidian. */
.section--accent a:not(.btn):not(.store-badge) {
  color: var(--accent-on-dark);
}

.section-head--center .lead {
  margin-inline: auto;
}

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(32px, 6vw, 88px);
  align-items: center;
}

.split--wide-left {
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
}

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

/* components.css */
/* ---------- Buttons ---------- */
.btn {
  --btn-bg: var(--accent);
  --btn-fg: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 52px;
  padding: 0 var(--s-6);
  border: 0;
  border-radius: var(--r-pill);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: 0.01em;
  text-decoration: none;
  box-shadow: var(--shadow-accent);
  transition:
    transform var(--dur-fast) var(--ease),
    box-shadow var(--dur) var(--ease),
    background var(--dur-fast) var(--ease);
}

.btn:hover {
  --btn-bg: var(--accent-pressed);
  color: var(--btn-fg);
  transform: translateY(-2px);
  box-shadow: 0 16px 36px rgb(var(--accent-rgb) / 34%);
}

.btn:active {
  transform: translateY(0);
}

.btn:disabled,
.btn[aria-disabled='true'] {
  opacity: 0.55;
  box-shadow: none;
  transform: none;
  cursor: progress;
}

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  box-shadow: inset 0 0 0 1.5px var(--hairline);
}

.btn--ghost:hover {
  --btn-bg: transparent;
  --btn-fg: var(--accent);
  box-shadow: inset 0 0 0 1.5px var(--accent);
}

.btn--on-accent {
  --btn-bg: #fff;
  --btn-fg: var(--accent);
  box-shadow: 0 14px 34px rgb(var(--ground-rgb) / 45%);
}

.btn--on-accent:hover {
  --btn-bg: #fff;
  --btn-fg: var(--accent-pressed);
}

.btn--outline-light {
  --btn-bg: transparent;
  --btn-fg: #fff;
  box-shadow: inset 0 0 0 1.5px var(--white-24);
}

.btn--outline-light:hover {
  --btn-bg: var(--white-10);
  --btn-fg: #fff;
  box-shadow: inset 0 0 0 1.5px var(--white-72);
}

.btn--sm {
  min-height: 42px;
  padding: 0 var(--s-5);
  font-size: var(--fs-small);
}

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

/* Apple's badge has a fixed shape we are not allowed to restyle, so anything
   standing next to it takes the badge's geometry rather than the site's pill.
   Everywhere else the pill radius still applies. */
.hero__actions .btn {
  min-height: 58px;
  border-radius: 11px;
}

/* ---------- Store badge ---------- */
.store-badge {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  min-height: 58px;
  padding: 0 22px 0 18px;
  border-radius: 11px;
  /* Apple's badge is black, not our ink — it is their asset, in their colour. */
  background: #000;
  color: #fff;
  text-decoration: none;
  box-shadow: var(--shadow-lift);
  transition:
    transform var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease);
}

.store-badge:hover {
  color: #fff;
  transform: translateY(-2px);
  background: var(--ground);
}

.store-badge__mark {
  width: 28px;
  height: 28px;
  flex: none;
  margin-top: -2px;
}

.store-badge__text {
  display: grid;
  line-height: 1.15;
  text-align: left;
}

.store-badge__text small {
  font-size: 0.68rem;
  font-weight: 400;
  letter-spacing: 0.01em;
  opacity: 0.92;
}

.store-badge__text span {
  font-size: 1.28rem;
  font-weight: 500;
  letter-spacing: -0.02em;
  margin-top: -2px;
}

.store-badge--light {
  background: #fff;
  color: var(--ink);
}

.store-badge--light:hover {
  background: #fff;
  color: var(--ink);
}

/* ---------- Cards ---------- */
.card {
  position: relative;
  padding: clamp(20px, 2.4vw, 30px);
  background: var(--card);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  border: 1px solid rgb(var(--hairline-rgb) / 50%);
}

.card--flat {
  box-shadow: none;
  border-color: var(--hairline);
}

.card__icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  margin-bottom: var(--s-4);
  border-radius: var(--r-icon);
  background: var(--surface-alt);
  color: var(--accent-text);
}

.card__icon svg {
  width: 26px;
  height: 26px;
}

.card h3 {
  margin-bottom: var(--s-2);
}

.card p {
  color: var(--ink-soft);
  font-size: 0.96rem;
}

/* ---------- Chips / pills ---------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 7px 14px;
  border-radius: var(--r-pill);
  background: var(--surface-alt);
  color: var(--ink);
  font-size: var(--fs-small);
  font-weight: 500;
}

.chip svg {
  width: 16px;
  height: 16px;
  color: var(--accent-text);
}

.chip--light {
  background: var(--white-16);
  color: #fff;
  border: 1px solid var(--white-24);
}

.chip--light svg {
  color: #fff;
}

/* ---------- Accordion ---------- */
.accordion {
  border-top: 1px solid var(--hairline);
}

.accordion__item {
  border-bottom: 1px solid var(--hairline);
}

.accordion__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  width: 100%;
  padding: var(--s-5) 0;
  background: none;
  border: 0;
  text-align: left;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--ink);
  transition: color var(--dur-fast) var(--ease);
}

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

.accordion__sign {
  position: relative;
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-sm);
  transition:
    background var(--dur) var(--ease),
    rotate var(--dur) var(--ease);
}

.accordion__sign::before,
.accordion__sign::after {
  content: '';
  position: absolute;
  inset: 50% 30%;
  border-top: 2px solid var(--accent);
  translate: 0 -1px;
}

.accordion__sign::after {
  rotate: 90deg;
  transition: opacity var(--dur) var(--ease);
}

.accordion__trigger[aria-expanded='true'] .accordion__sign {
  background: var(--accent);
  rotate: 180deg;
}

.accordion__trigger[aria-expanded='true'] .accordion__sign::before,
.accordion__trigger[aria-expanded='true'] .accordion__sign::after {
  border-color: #fff;
}

.accordion__trigger[aria-expanded='true'] .accordion__sign::after {
  opacity: 0;
}

.accordion__panel {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--dur) var(--ease);
}

/* Only collapse once JS is there to open it again — otherwise a no-JS visitor
   gets an FAQ that cannot be read. */
.js .accordion__panel {
  grid-template-rows: 0fr;
}

.js .accordion__panel[data-open='true'] {
  grid-template-rows: 1fr;
}

.accordion__panel > div {
  overflow: hidden;
}

.accordion__panel p {
  max-width: var(--measure);
  padding-bottom: var(--s-5);
  color: var(--ink-soft);
}

/* ---------- Marketing shots ---------- */
/*
 * Replaces .device + screens.css (2026-08-05). The site used to REDRAW each
 * phone screen in CSS — 469 lines of it — which kept the hero crisp at any DPI
 * but meant the site could not follow the app: the drawn case file was still
 * showing Latest / Evidence / Theories / Documents months after the app
 * collapsed to Overview / Timeline / Sources, and no amount of regenerating
 * screenshots could change it, because the site displayed none.
 *
 * The renders in marketing/renders/ carry the device, its lighting and its
 * shadow, so there is no frame to draw here — only sizing and the drop shadow
 * that seats it on the page. Nothing crops or masks the image: these scenes
 * are lit to the same near-black the page uses, so their own ground blends in.
 */
.shot {
  display: block;
  width: 100%;
  height: auto;
  /* FADE THE EDGES, or the scene prints its own rectangle.
     The renders are lit to a near-black ground and the page runs a dark red
     gradient behind them — close, but not the same colour, so the image box
     was visible as a hard-edged rectangle in the hero. A few percent of fade
     on each side dissolves the join. The crops carry ~4% of slack around the
     outermost element so this never cuts a floating chip in half. */
  -webkit-mask-image:
    linear-gradient(to right, transparent 0, #000 14%, #000 92%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 10%, #000 90%, transparent 100%);
  mask-image:
    linear-gradient(to right, transparent 0, #000 14%, #000 92%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 10%, #000 90%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
  /* Not a box-shadow: the render has no rectangular edge to cast one from —
     the phone is at an angle inside a transparent-looking dark field, and a
     box shadow would print a hard rectangle around the whole scene. */
}

.shot--hero {
  max-width: 720px;
  margin-inline: auto;
}

.shot--lift {
  max-width: 380px;
  margin-inline: auto;
}

/*
 * A DARK SCENE ON A LIGHT SECTION IS A CARD, NOT A CUTOUT.
 *
 * The edge fade above works because the hero and the Nightstand band are
 * near-black: the render's own ground is the same colour as the page, so the
 * boundary genuinely disappears. On the white sections it did the opposite —
 * a dark rectangle with soft edges reads as a smudge someone forgot to mask.
 * There, the scene gets a deliberate obsidian card with the app's own corner
 * radius, which is both honest about the edge and better looking than
 * pretending there isn't one.
 */
.shot--card {
  border-radius: 26px;
  background: #0a0a0b;
  overflow: hidden;
  -webkit-mask-image: none;
  mask-image: none;
  box-shadow:
    0 28px 56px rgb(0 0 0 / 22%),
    0 2px 6px rgb(0 0 0 / 8%);
}

/* Per-platform store surfaces. Play is live before the App Store, so the Play
   badge is in every page and only steps aside on iPhone; the notify CTA and
   "coming to the App Store" copy are for iPhone visitors alone. Desktop sees
   the Play badge — a laptop visitor may well own an Android phone. */
html[data-platform="ios"] [data-android-only] { display: none !important; }
html[data-platform="android"] [data-ios-only] { display: none !important; }

/* nav.css */
.nav {
  position: fixed;
  inset: 0 0 auto;
  z-index: var(--z-nav);
  padding-block: var(--s-4);
  transition:
    background var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    padding var(--dur) var(--ease);
}

.nav__inner {
  display: flex;
  align-items: center;
  gap: var(--s-5);
}

.nav__brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-right: auto;
  color: #fff;
  font-weight: 700;
  font-size: 1.06rem;
  letter-spacing: -0.02em;
  text-decoration: none;
}

.nav__brand img {
  width: 36px;
  height: 36px;
  border-radius: 10px;
}

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(14px, 1.8vw, 28px);
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav__links li {
  margin: 0;
}

.nav__links a {
  position: relative;
  color: var(--white-88);
  font-size: 0.92rem;
  font-weight: 500;
  text-decoration: none;
  padding-block: 6px;
}

.nav__links a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  scale: 0 1;
  transform-origin: left;
  transition: scale var(--dur) var(--ease);
}

.nav__links a:hover,
.nav__links a[aria-current='true'] {
  color: #fff;
}

.nav__links a:hover::after,
.nav__links a[aria-current='true']::after {
  scale: 1 1;
}

.nav__toggle {
  display: none;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: var(--white-16);
  color: #fff;
}

.nav__toggle span {
  display: block;
  width: 18px;
  height: 2px;
  margin: 4px auto;
  border-radius: 2px;
  background: currentColor;
  transition:
    translate var(--dur) var(--ease),
    rotate var(--dur) var(--ease),
    opacity var(--dur-fast) var(--ease);
}

.nav__toggle[aria-expanded='true'] span:nth-child(1) {
  translate: 0 6px;
  rotate: 45deg;
}
.nav__toggle[aria-expanded='true'] span:nth-child(2) {
  opacity: 0;
}
.nav__toggle[aria-expanded='true'] span:nth-child(3) {
  translate: 0 -6px;
  rotate: -45deg;
}

/* Scrolled / inner-page state */
.nav.is-solid {
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: saturate(1.4) blur(14px);
  box-shadow: 0 1px 0 var(--hairline);
}

.nav.is-solid .nav__brand {
  color: var(--ink);
}

.nav.is-solid .nav__links a {
  color: var(--ink-soft);
}

.nav.is-solid .nav__links a:hover,
.nav.is-solid .nav__links a[aria-current='true'] {
  color: var(--accent);
}

.nav.is-solid .nav__toggle {
  background: var(--surface-alt);
  color: var(--ink);
}

/* Mobile drawer */
.nav__drawer {
  display: none;
}

@media (max-width: 960px) {
  .nav__links {
    display: none;
  }

  /* IMPORTANT: the CTA stays. Hiding it left the install bar as the only
     persistent action on a phone — and that bar dismisses itself permanently
     on one tap, so a mobile visitor could end up with no CTA in the viewport
     at all. It just gets tighter. */
  .nav__cta {
    padding: 8px 14px;
    font-size: 0.82rem;
  }

  .nav__toggle {
    display: block;
  }

  .nav__drawer {
    display: grid;
    position: fixed;
    inset: 76px var(--gutter) auto;
    z-index: var(--z-nav);
    gap: var(--s-2);
    padding: var(--s-5);
    border-radius: var(--r-sheet);
    background: #fff;
    box-shadow: var(--shadow-lift);
    opacity: 0;
    translate: 0 -12px;
    /* visibility, not just pointer-events: a closed drawer must leave the tab
       order too, or a keyboard user hits eight invisible stops. */
    visibility: hidden;
    transition:
      opacity var(--dur) var(--ease),
      translate var(--dur) var(--ease),
      visibility 0s linear var(--dur);
  }

  .nav__drawer[data-open='true'] {
    opacity: 1;
    translate: 0 0;
    visibility: visible;
    transition-delay: 0s;
  }

  .nav__drawer a {
    padding: var(--s-3) var(--s-2);
    color: var(--ink);
    font-weight: 500;
    text-decoration: none;
    border-radius: 10px;
  }

  .nav__drawer a:hover {
    background: var(--surface);
    color: var(--accent);
  }

  .nav__drawer .btn {
    margin-top: var(--s-2);
  }
}

/* At the narrowest widths the brand word, the CTA and the burger stop fitting
   on one line. The mark alone still identifies the page. */
@media (max-width: 400px) {
  .nav__brand span {
    display: none;
  }
}

/* hero.css */
.hero {
  position: relative;
  isolation: isolate;
  padding-top: clamp(128px, 15vh, 176px);
  padding-bottom: clamp(72px, 9vw, 120px);
  overflow: hidden;
  /* Obsidian ground, oxblood glow rising behind the devices in the top right
     where no text sits. Everything the copy occupies stays near-black, so white
     body text clears 15:1 rather than fighting a mid-red. This is the same
     construction as the App Store posters (store/shots.json). */
  background:
    radial-gradient(64% 52% at 86% 2%, rgb(var(--oxblood-mid-rgb) / 62%) 0%, transparent 64%),
    radial-gradient(110% 100% at -8% 92%, var(--oxblood-deep) 0%, transparent 66%),
    linear-gradient(163deg, var(--ground-warm) 0%, var(--ground) 46%, var(--ground-floor) 100%);
  color: #fff;
}

.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: var(--grain);
  opacity: 0.2;
  mix-blend-mode: overlay;
  pointer-events: none;
}

/* The wire motif: faint horizontal rules, like a feed still coming in. Drawn,
   not imported — there is no photograph this product is allowed to put here.
   Rule 3 forbids crime-scene, reconstruction and not-convicted imagery, and a
   stock "detective" photo would be exactly the tabloid register the app avoids. */
.hero__wire {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  opacity: 0.5;
  background-image:
    repeating-linear-gradient(
      180deg,
      rgba(255, 255, 255, 0.045) 0px,
      rgba(255, 255, 255, 0.045) 1px,
      transparent 1px,
      transparent 46px
    );
  mask-image: radial-gradient(120% 90% at 50% 30%, #000 20%, transparent 78%);
}

/* Three redacted "lines" sliding across the rules — the feed, moving. */
.hero__wire i {
  position: absolute;
  display: block;
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, transparent, var(--oxblood-mid), transparent);
  opacity: 0.7;
  animation: wire-run 9s var(--ease) infinite;
}

.hero__wire i:nth-child(1) {
  top: 22%;
  left: -30%;
  width: 34%;
}
.hero__wire i:nth-child(2) {
  top: 54%;
  left: -30%;
  width: 22%;
  animation-delay: -3.4s;
}
.hero__wire i:nth-child(3) {
  top: 78%;
  left: -30%;
  width: 28%;
  animation-delay: -6.2s;
}

@keyframes wire-run {
  0% {
    translate: 0 0;
    opacity: 0;
  }
  12% {
    opacity: 0.7;
  }
  88% {
    opacity: 0.7;
  }
  100% {
    translate: 460% 0;
    opacity: 0;
  }
}

/* Areas rather than DOM order: the copy reads title → lead → CTA on a phone,
   while the devices still sit in the right column on a desktop. */
.hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  grid-template-areas:
    'title stage'
    'copy  stage';
  grid-template-rows: auto auto;
  column-gap: clamp(32px, 5vw, 72px);
  align-content: center;
}

.hero__title {
  grid-area: title;
}

/* Says what the thing IS, above the line that says why it is different. The
   headline was doing philosophy while the page never once said "app". */
.hero__kicker {
  margin: 0 0 var(--s-4);
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--accent-on-dark);
}
.hero__copy {
  grid-area: copy;
}
.hero__stage {
  grid-area: stage;
}

.hero h1 {
  max-width: 18ch;
  font-size: var(--fs-display);
  letter-spacing: -0.032em;
  margin-bottom: var(--s-5);
  text-wrap: balance;
}

.hero h1 em {
  display: block;
  font-style: normal;
  color: var(--accent-on-dark);
}

.hero__lead {
  max-width: 46ch;
  font-size: var(--fs-lead);
  color: var(--white-88);
  margin-bottom: var(--s-4);
}

.hero__lead strong {
  color: var(--on-accent);
  font-weight: 600;
}

.hero__lead--second {
  margin-bottom: var(--s-6);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-4);
  margin-bottom: var(--s-5);
}

.hero__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
  font-size: var(--fs-small);
  color: var(--white-88);
}

.hero__meta span {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.hero__meta svg {
  width: 15px;
  height: 15px;
}

/* Devices + floating proof chips */
.hero__stage {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 500px;
}

/* IMPORTANT: the handsets do NOT float. Six infinite animations in a hero is
   tonally wrong for a product whose whole pitch is sober restraint, and it is a
   permanent compositor cost on a phone. The two chips keep a slow drift; the
   devices — which are the product — sit still. */
.hero__stage .device {
  max-width: 292px;
  rotate: -3deg;
}

/* The second handset sits behind and to the right, the way the Nightstand sits
   behind the case file. It is decoration — hidden from assistive tech in the
   markup — so it may be dropped entirely on narrow screens. */
.hero__stage .device--behind {
  position: absolute;
  top: 8%;
  right: -12%;
  z-index: -1;
  max-width: 214px;
  rotate: 7deg;
  opacity: 0.85;
}

.hero__chip {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.96);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
  font-size: 0.85rem;
  line-height: 1.25;
  white-space: nowrap;
}

.hero__chip b {
  display: block;
  font-size: 1.02rem;
  letter-spacing: -0.01em;
}

.hero__chip small {
  color: var(--muted);
  font-size: 0.72rem;
}

.hero__chip svg {
  width: 26px;
  height: 26px;
  flex: none;
}

.hero__chip--sourced {
  top: 4%;
  left: -9%;
  animation: hero-float 14s var(--ease) -2.6s infinite;
}

.hero__chip--sourced svg {
  color: var(--v-corroborated-ink);
}

.hero__chip--night {
  bottom: 10%;
  left: -6%;
  animation: hero-float 14s var(--ease) -7.3s infinite;
}

.hero__chip--night svg {
  color: var(--v-developing-ink);
}

@keyframes hero-float {
  0%,
  100% {
    translate: 0 0;
  }
  50% {
    translate: 0 -6px;
  }
}

/* Trust strip riding the bottom of the hero */
.hero__trust {
  margin-top: clamp(48px, 6vw, 72px);
  padding-top: var(--s-5);
  border-top: 1px solid var(--white-16);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4) clamp(20px, 4vw, 56px);
  align-items: center;
}

.hero__trust p {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  margin: 0;
  color: var(--white-88);
  font-size: 0.9rem;
  font-weight: 500;
}

.hero__trust svg {
  width: 18px;
  height: 18px;
  flex: none;
  opacity: 0.9;
}

/* sections.css */
/* ---------- Steps ---------- */
.steps {
  counter-reset: step;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 40px);
}

.step {
  position: relative;
  counter-increment: step;
  padding-top: var(--s-6);
}

.step::before {
  content: counter(step, decimal-leading-zero);
  position: absolute;
  top: 0;
  left: 0;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--accent-text);
}

.step::after {
  content: '';
  position: absolute;
  top: 8px;
  left: 34px;
  right: -20px;
  height: 1px;
  background: linear-gradient(90deg, var(--hairline), transparent);
}

.step:last-child::after {
  display: none;
}

.step h3 {
  margin-bottom: var(--s-2);
}

.step p {
  color: var(--ink-soft);
  max-width: 34ch;
}

/* ---------- Full-bleed photograph ----------
   The one place on the page where a photo is the argument rather than a
   thumbnail. Text sits in the dark left third; the scrim is graded so the
   headline clears 4.5:1 wherever the image lands. */
.fullbleed {
  position: relative;
  isolation: isolate;
  display: grid;
  min-height: clamp(420px, 56vw, 640px);
  overflow: hidden;
  background: var(--ink);
}

.fullbleed img {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 60% 50%;
}

/* Positioned, not just a grid item: the image above is absolutely positioned,
   so a static pseudo-element paints *underneath* it and the scrim does nothing. */
.fullbleed::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(
      100deg,
      rgb(var(--ink-rgb) / 94%) 0%,
      rgb(var(--ink-rgb) / 86%) 34%,
      rgb(var(--ink-rgb) / 62%) 62%,
      rgb(var(--ink-rgb) / 30%) 88%
    ),
    linear-gradient(0deg, rgb(var(--ink-rgb) / 45%), rgb(var(--ink-rgb) / 18%) 55%);
}

/* The only person in the garden frame is at the far left of the plate; hold
   that edge through the cover-crop at every width. */
#why img {
  object-position: 8% 50%;
}

.fullbleed__body {
  position: relative;
  grid-area: 1 / 1;
  z-index: 2;
  display: grid;
  align-content: center;
  padding-block: var(--s-8);
}

.fullbleed__body > div {
  max-width: 46ch;
  color: var(--on-accent);
}

.fullbleed h2 {
  color: var(--on-accent);
  font-size: clamp(1.9rem, 1.1rem + 2.9vw, 4rem);
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.fullbleed p {
  color: var(--white-88);
  font-size: var(--fs-lead);
  margin: 0;
}

.fullbleed .eyebrow {
  color: var(--on-accent);
}

/* The date the photograph carries — set in type rather than burned into the
   raster, so it scales, translates and can be read out. */
.fullbleed__stamp {
  margin-top: var(--s-5);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--tint-blush);
}

/* ---------- Feature grid ---------- */
.features {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 24px);
}

.feature {
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2.4vw, 30px);
  border-radius: var(--r-card);
  background: var(--card);
  border: 1px solid var(--hairline);
  transition:
    transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}

.feature:hover {
  transform: translateY(-4px);
  border-color: transparent;
  box-shadow: var(--shadow-lift);
}

.feature__icon {
  background: #fff;
}

.feature__icon {
  display: grid;
  place-items: center;
  width: 50px;
  height: 50px;
  margin-bottom: var(--s-4);
  border-radius: var(--r-icon);
  background: var(--surface-alt);
  color: var(--accent-text);
}

.feature__icon svg {
  width: 24px;
  height: 24px;
}

.feature h3 {
  margin-bottom: var(--s-2);
  font-size: 1.08rem;
}

.feature p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

/* One card carries the idea the whole product rests on, so it gets the room.
   Two columns of a three-column grid keeps the rows square. */
.feature--wide {
  grid-column: span 2;
  background: var(--surface-alt);
  border-color: transparent;
}

.feature.feature--wide h3 {
  font-size: clamp(1.35rem, 1.1rem + 1vw, 1.85rem);
  line-height: 1.15;
  max-width: 24ch;
}

.feature.feature--wide p {
  font-size: 1.05rem;
  max-width: 46ch;
}

.feature--wide .feature__icon {
  background: #fff;
}

/* The rest of the capability list, demoted to one line — it belongs on the page
   but it is not why anyone installs this. */
.features__note {
  max-width: 62ch;
  margin: clamp(28px, 3vw, 40px) auto 0;
  text-align: center;
  font-size: 0.94rem;
  color: var(--muted);
}

/* ---------- Screens rail ---------- */
.screens {
  display: flex;
  gap: clamp(20px, 3vw, 40px);
  overflow-x: auto;
  /* Break out of the container to the full viewport, so the rail reads as a
     rail and physically separates the two card grids around it. */
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  padding: var(--s-2) max(var(--gutter), calc(50vw - var(--container) / 2)) var(--s-7);
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  /* `safe` so that when the rail does overflow the first item is still
     reachable rather than clipped off the start. */
  justify-content: safe center;
}

.screens::-webkit-scrollbar {
  display: none;
}

.screens__item {
  flex: 0 0 auto;
  width: min(72vw, 264px);
  scroll-snap-align: center;
  text-align: center;
}

.screens__item .device {
  margin-inline: auto;
  margin-bottom: var(--s-4);
  transition: transform var(--dur) var(--ease);
}

.screens__item:hover .device {
  transform: translateY(-8px);
}

.screens__item p {
  margin: 0;
  min-height: 2.6em;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--ink-soft);
}

.rail-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
}

.rail-btn {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border: 0;
  border-radius: 50%;
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--shadow-card);
  transition:
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}

.rail-btn:hover:not(:disabled) {
  background: var(--accent);
  color: #fff;
}

.rail-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.rail-btn svg {
  width: 18px;
  height: 18px;
}

.rail-dots {
  display: flex;
  gap: 7px;
}

/* 8px dots inside a 26px hit area — WCAG 2.2 target size without the visual
   weight of a 26px dot. */
.rail-dots button {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  background: none;
  display: grid;
  place-items: center;
}

.rail-dots button::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--hairline);
  transition:
    width var(--dur) var(--ease),
    background var(--dur) var(--ease);
}

.rail-dots button[aria-current='true']::before {
  width: 22px;
  background: var(--accent);
}

/* ---------- Stat band ---------- */
.stats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(16px, 3vw, 40px);
  text-align: center;
}

.stat b {
  display: block;
  font-size: clamp(2.6rem, 1.4rem + 3.6vw, 4.2rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* Direct child only — the counter <span> lives inside <b> and must not pick up
   the label rule, which is what shrank the numbers to caption size. */
.stat > span {
  display: block;
  margin-top: var(--s-3);
  font-size: 0.9rem;
  color: var(--on-accent);
  max-width: 22ch;
  margin-inline: auto;
}

.stat b span {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  display: inline;
  margin: 0;
}

/* ---------- Promise list (privacy) ---------- */
.promises {
  display: grid;
  gap: var(--s-4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.promises li {
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: var(--s-4);
  margin: 0;
  align-items: start;
}

.promises svg {
  width: 26px;
  height: 26px;
  color: var(--ok-text);
  margin-top: 2px;
}

.promises b {
  display: block;
  margin-bottom: 2px;
}

.promises p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

/* Permissions: set on the ground with hairline rows rather than inside yet
   another bordered card — the page already has enough of those. */
.permissions h3 {
  padding-bottom: var(--s-4);
  border-bottom: 2px solid var(--ink);
  font-size: 1.05rem;
}

.permissions dl {
  margin: 0;
}

.permissions dl > div {
  padding: var(--s-4) 0;
  border-bottom: 1px solid var(--hairline);
}

.permissions dt {
  font-weight: 600;
  margin-bottom: 2px;
}

.permissions dd {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.94rem;
}

/* ---------- Roadmap ---------- */
.roadmap {
  display: grid;
  gap: var(--s-4);
}

.roadmap__item {
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: var(--s-5);
  align-items: baseline;
  padding: var(--s-5) 0;
  border-top: 1px solid var(--hairline);
}

.roadmap__item:last-child {
  border-bottom: 1px solid var(--hairline);
}

.roadmap__when {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-text);
}

.roadmap__item p {
  margin: var(--s-1) 0 0;
  color: var(--ink-soft);
  max-width: var(--measure);
}

/* A short note in a human voice. No card, no icon, no border — the only thing
   marking it is a rule and a hanging quote mark. */
.download {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: clamp(32px, 5vw, 72px);
  align-items: center;
}

.qr-card {
  display: grid;
  justify-items: center;
  gap: var(--s-4);
  padding: var(--s-6);
  border-radius: var(--r-sheet);
  background: rgba(255, 255, 255, 0.96);
  color: var(--ink);
  box-shadow: var(--shadow-lift);
  text-align: center;
}

.qr-card img {
  width: 168px;
  height: 168px;
  border-radius: 12px;
}

.qr-card p {
  margin: 0;
  font-size: 0.9rem;
  color: var(--ink-soft);
}

.copy-link {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  width: 100%;
  padding: 10px 10px 10px 16px;
  border-radius: var(--r-pill);
  background: var(--surface);
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* Pre-launch stand-in for the QR card: what the visitor actually wants to know
   is what happens after they hand over an address. */
.next-card {
  container-type: inline-size;
  padding: clamp(24px, 3vw, 34px);
  border-radius: var(--r-sheet);
  background: var(--white-16);
  border: 1px solid var(--white-24);
  backdrop-filter: blur(12px);
}

.next-card h3 {
  margin-bottom: var(--s-4);
  color: #fff;
  font-size: 1.1rem;
}

.next-card ol {
  margin: 0 0 var(--s-4);
  padding-left: 1.2em;
  color: #fff;
}

.next-card li {
  margin-bottom: var(--s-3);
  font-size: 0.94rem;
}

.next-card li::marker {
  color: rgba(255, 255, 255, 0.6);
  font-weight: 700;
}

.next-card p {
  margin: 0;
  font-size: 0.86rem;
  color: var(--white-88);
}

.next-card a {
  color: #fff;
}

.copy-link code {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: inherit;
}

/* ---------- The sourcing ladder ---------- */
/* A definition list in everything but tag name: label on the left, plain-English
   meaning on the right. Two columns rather than seven cards, because the point
   is that they are ONE ladder and read top to bottom. */
.ladder {
  display: grid;
  gap: 0;
  list-style: none;
  padding: 0;
  max-width: 880px;
  margin-inline: auto;
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  background: var(--card);
  overflow: hidden;
}

.ladder li {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: var(--s-4) var(--s-5);
  align-items: center;
  margin: 0;
  padding: var(--s-4) clamp(18px, 2.4vw, 28px);
  border-top: 1px solid var(--hairline);
}

.ladder li:first-child {
  border-top: 0;
}

.ladder li p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

.ladder__row {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: var(--s-4) var(--s-5);
  align-items: center;
  padding: var(--s-4) clamp(18px, 2.4vw, 28px);
  border-top: 1px solid var(--hairline);
}

.ladder__row:first-child {
  border-top: 0;
}

.ladder__row p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

.ladder__note {
  max-width: 62ch;
  margin: var(--s-6) auto 0;
  text-align: center;
  font-size: var(--fs-small);
  color: var(--muted);
}

/* ---------- Card grid (the case-file destinations) ---------- */
.cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 1.8vw, 24px);
}

/* The "what is not here" tile. Deliberately quieter than the five real
   destinations — it is a statement, not a feature we are selling. */
.card--quiet {
  background: var(--surface);
  border-style: dashed;
}

.card--quiet .card__icon {
  background: transparent;
  border: 1px solid var(--hairline);
  color: var(--muted);
}

/* A device that is the subject of its column rather than a floating hero prop. */
.device--lift {
  max-width: 280px;
  margin-inline: auto;
  box-shadow: var(--shadow-lift);
  border-radius: 42px;
}

/* ---------- Flat detail shots ---------- */
/* A capture shown as itself, without phone chrome. The two "zoom" store panels
   are scaled-up crops, so they are not phone-shaped and must never go inside a
   .device — the frame would stretch them. Fading the bottom edge says "this
   continues" instead of pretending the screen ends there. */
.shot {
  position: relative;
  margin: 0;
  border-radius: var(--r-card);
  overflow: hidden;
  background: var(--card);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-card);
}

/* No cropping here. The crop that matters happens in scripts/assets.py, which
   takes the part of the screen the surrounding section is actually about — a
   CSS aspect-ratio on top of that just hid the content twice. */
.shot img {
  display: block;
  width: 100%;
  height: auto;
}

.shot figcaption {
  padding: var(--s-4) clamp(16px, 2vw, 22px);
  border-top: 1px solid var(--hairline);
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--muted);
}

.shot--wide {
  max-width: 760px;
  margin: clamp(40px, 5vw, 64px) auto 0;
}

/* IMPORTANT: the promises list is used on BOTH grounds. Its defaults are for
   the light page — `--ink-soft` body copy on the obsidian band renders at about
   1.4:1, which is invisible rather than subtle. */
.section--accent .promises b {
  color: #fff;
}

.section--accent .promises p {
  color: var(--white-88);
}

/* The corroborated green is a fill colour; on obsidian it needs the lifted
   sibling, the same swap the app makes with S.accentOnDark. */
.section--accent .promises svg {
  color: var(--ok-on-dark);
}

/* ---------- Design-review fixes ---------- */

/* IMPORTANT: `.split { align-items: center }` is only right when the two columns
   are within roughly 120px of each other. In #standards the right column runs
   ~340px longer, so centring opened the section on a blank top-left quadrant. */
#standards .split {
  align-items: start;
}

/* The crop in scripts/assets.py deliberately ends mid-card — that IS the
   content continuing. Say so with a fade instead of leaving a stub of empty
   card sitting above the caption, which reads as a broken image. */
.shot img {
  -webkit-mask-image: linear-gradient(180deg, #000 84%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 84%, transparent 100%);
}

/* One value for card-level headings. See --fs-h4. */
.feature h3,
.card h3,
.permissions h3,
.next-card h3 {
  font-size: var(--fs-h4);
}

/* A hover lift on an <article> with nothing to click advertises an interaction
   that does not exist. Feedback stays, the promise goes. */
.feature:hover {
  transform: none;
  box-shadow: var(--shadow-sm);
  border-color: var(--muted);
}

/* ---------- Section heads: two shapes, not one ---------- */
/* Nine consecutive centred three-line paragraphs is the rhythm that makes an
   otherwise specific page feel machine-assembled — and centred multi-line body
   copy is the weaker read of the two anyway. */
.section-head--split {
  display: grid;
  grid-template-columns: minmax(0, 0.55fr) minmax(0, 0.45fr);
  gap: clamp(24px, 4vw, 64px);
  align-items: end;
  max-width: none;
  text-align: left;
}

.section-head--split .lead {
  margin: 0;
}

/* ---------- The case-file destinations ---------- */
/* #file is literally about a sticky row of five tabs, so it is drawn as one
   rather than as a second copy of the .features grid. */
/* start, not center: with the note removed the left column is shorter than the
   phone, and centring it opened a hole between the heading and the list. */
.destinations {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.78fr);
  gap: clamp(32px, 5vw, 72px);
  align-items: start;
}

.destinations__list {
  display: grid;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.destinations__list li {
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr);
  gap: var(--s-4);
  margin: 0;
  padding: var(--s-4) 0;
  border-top: 1px solid var(--hairline);
}

.destinations__list li:first-child {
  border-top: 0;
}

.destinations__list b {
  font-size: var(--fs-h4);
}

.destinations__list p {
  margin: 0;
  font-size: 0.95rem;
  color: var(--ink-soft);
}

/* ---------- The catalogue ---------- */
.catalogue {
  display: grid;
  gap: clamp(28px, 3vw, 44px);
}

.catalogue__group summary h3 {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
  padding-bottom: var(--s-3);
  border-bottom: 2px solid var(--ink);
  font-size: var(--fs-h4);
}

.catalogue__group summary h3::after {
  content: '';
  width: 9px;
  height: 9px;
  margin-left: auto;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  rotate: 45deg;
  translate: 0 -2px;
  transition: rotate var(--dur-fast) var(--ease);
}

.catalogue__group[open] summary h3::after {
  rotate: -135deg;
  translate: 0 2px;
}

.catalogue__group summary h3 span {
  font-weight: 500;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.catalogue__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--s-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The app's own case card, minus the artwork — a marketing page has no
   business hotlinking a publisher's thumbnail out of its context, and no
   picture at all is the only version that cannot put a face beside a charge. */
.case-card {
  display: grid;
  align-content: start;
  gap: 6px;
  margin: 0;
  padding: var(--s-4);
  border-radius: var(--r-chip);
  background: var(--card);
  border: 1px solid var(--hairline);
}

.case-card__posture {
  margin: 0;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Red is reserved for a live accusation, so the colour distinguishes rather
   than decorates. */
.case-card__posture[data-posture='pretrial'],
.case-card__posture[data-posture='charged'],
.case-card__posture[data-posture='developing'],
.case-card__posture[data-posture='appeal'] {
  color: var(--accent-text);
}

.case-card__name {
  margin: 0;
  font-size: 0.98rem;
  font-weight: 600;
  line-height: 1.32;
  color: var(--ink);
}

.case-card__meta {
  margin: 2px 0 0;
  font-size: 0.78rem;
  color: var(--muted);
}





.catalogue__group > summary {
  list-style: none;
  cursor: pointer;
}

.catalogue__group > summary::-webkit-details-marker {
  display: none;
}

.catalogue__jump {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-2);
  margin: 0 0 clamp(28px, 3vw, 40px);
  padding: 0;
  list-style: none;
}

.catalogue__jump li {
  margin: 0;
}

.catalogue__jump a {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 8px 16px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  background: var(--card);
  color: var(--ink);
  font-size: 0.86rem;
  font-weight: 600;
  text-decoration: none;
}

.catalogue__gate {
  margin: calc(var(--s-3) * -1) 0 var(--s-4);
  font-size: 0.86rem;
  color: var(--muted);
}

.catalogue__note {
  max-width: 70ch;
  margin: clamp(32px, 4vw, 48px) auto 0;
  text-align: center;
  font-size: var(--fs-small);
  color: var(--muted);
}

/* The catalogue's own capture point — the intent peak on this page is the
   instant a reader finds their case, not the band 6,000px further down. */
.catalogue__cta {
  max-width: 640px;
  margin: clamp(32px, 4vw, 48px) auto 0;
  padding: clamp(22px, 3vw, 32px);
  border-radius: var(--r-card);
  background: var(--card);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-sm);
  container-type: inline-size;
}

.catalogue__cta h3 {
  margin-bottom: var(--s-2);
  font-size: var(--fs-h4);
}

.catalogue__cta > p {
  font-size: 0.92rem;
  color: var(--ink-soft);
  margin-bottom: var(--s-4);
}

/* ---------- The ladder's worked example ---------- */
/* Drawn, not captured. The screenshot that used to sit here was accurate and
   that was the problem: 725 of the 927 items in the shipped bundle are
   unranked sources, so the two rows it showed were a tabloid aggregator and an
   AI-written blog — both SINGLE-SOURCED, under a caption promising an ordering
   by primary record, of which the bundle contains none. These rows show the
   range the table above actually promises. */
.ladder__demo {
  max-width: 760px;
  margin: clamp(36px, 4vw, 56px) auto 0;
  padding: clamp(20px, 2.6vw, 30px);
  border-radius: var(--r-card);
  background: var(--card);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-sm);
}

.ladder__demo-head {
  margin: 0 0 var(--s-4);
  padding-bottom: var(--s-3);
  border-bottom: 2px solid var(--ink);
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}

.ladder__rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

.ladder__rows li {
  margin: 0;
  padding: var(--s-4) 0;
  border-bottom: 1px solid var(--hairline);
}

.ladder__row-date {
  margin: 0 0 2px;
  font-size: 0.8rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.ladder__row-title {
  margin: 0 0 var(--s-3);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
}

.ladder__row-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  margin: 0;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

.ladder__demo-note {
  margin: var(--s-4) 0 0;
  font-size: var(--fs-small);
  color: var(--muted);
}

/* ---------- The hero's three value props ---------- */
/* Replaces the old trust strip. Those four lines were reassurance; these three
   are what the app is for, and every section below the fold is one of them
   expanded. */
.hero__props {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 44px);
  margin: clamp(48px, 6vw, 72px) 0 0;
  padding: var(--s-6) 0 0;
  border-top: 1px solid var(--white-16);
  list-style: none;
}

.hero__props li {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  grid-template-areas: 'icon title' '. body';
  column-gap: var(--s-3);
  row-gap: 2px;
  margin: 0;
}

.hero__props svg {
  grid-area: icon;
  width: 22px;
  height: 22px;
  margin-top: 2px;
  color: var(--accent-on-dark);
}

.hero__props b {
  grid-area: title;
  font-size: 1rem;
  color: #fff;
}

.hero__props span {
  grid-area: body;
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--white-72);
}

/* ---------- The four editorial rules ---------- */
.rules {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 36px) clamp(28px, 4vw, 64px);
}

.rules > div {
  padding-top: var(--s-4);
  border-top: 2px solid var(--ink);
}

.rules b {
  display: block;
  margin-bottom: 4px;
  font-size: var(--fs-h4);
}

.rules p {
  margin: 0;
  font-size: 0.94rem;
  color: var(--ink-soft);
}


/* ---------- The live wire ---------- */
/* Real feed items, from the same file the app reads. No imagery: a marketing
   page that put a face next to a charge would break the one rule this product
   cannot bend. */
.wire {
  display: grid;
  gap: var(--s-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.wire__item {
  display: grid;
  gap: 4px;
  margin: 0;
  padding: var(--s-5);
  border-radius: var(--r-card);
  background: var(--white-06);
  border: 1px solid var(--white-16);
}

.wire__case {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: 0;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--accent-on-dark);
}

.wire__case::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent-on-dark);
  flex: none;
}

.wire__posture {
  margin: 0;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--white-72);
}

.wire__what {
  margin: 2px 0 6px;
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.15rem);
  font-weight: 600;
  line-height: 1.35;
  color: #fff;
}

.wire__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  margin: 0;
  font-size: 0.84rem;
  color: var(--white-72);
}

.wire__meta time {
  font-variant-numeric: tabular-nums;
}

.wire__foot {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: var(--s-5) 0 0;
  font-size: var(--fs-small);
  color: var(--white-64);
}

.wire__foot::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent-on-dark);
  animation: wire-pulse 2.4s var(--ease) infinite;
}

@keyframes wire-pulse {
  0%,
  100% {
    opacity: 0.35;
  }
  50% {
    opacity: 1;
  }
}

/* ---------- "Moved in the last thirty days" ---------- */
/* The site's single strongest claim, so it is a table of real rows rather than
   a sentence: a year on the left, a case in the middle, a count on the right.
   Reading 1937 next to "11 updates" does the work no adjective can. */
.moved {
  max-width: 780px;
  margin: 0 auto clamp(40px, 5vw, 64px);
  padding: clamp(20px, 2.6vw, 28px) clamp(20px, 3vw, 32px);
  border-radius: var(--r-card);
  background: var(--card);
  border: 1px solid var(--hairline);
}

.moved__head {
  margin: 0 0 var(--s-3);
  padding-bottom: var(--s-3);
  border-bottom: 2px solid var(--ink);
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}

.moved__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.moved__list li {
  display: grid;
  grid-template-columns: 9.5rem minmax(0, 1fr) auto;
  gap: var(--s-4);
  align-items: baseline;
  margin: 0;
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--hairline);
}

.moved__list li:last-child {
  border-bottom: 0;
}

.moved__since {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.moved__name {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.3;
}

.moved__count {
  margin: 0;
  font-size: 0.9rem;
  color: var(--accent-text);
  white-space: nowrap;
}

.moved__count b {
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
}

.moved__foot {
  margin: var(--s-4) 0 0;
  font-size: 0.78rem;
  color: var(--muted);
}

/* The three alert promises sit in a row under the table rather than a column
   beside a phone — the table is the hero of this section now. */
.promises--row {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 40px);
}

/* The second group in the movement table: the busiest cases right now. It
   carries the posture instead of a year, because rule 4 applies here too — any
   surface that names a case names where it stands, at the same weight. */
.moved__head--second {
  margin-top: var(--s-6);
}

.moved__posture {
  margin: 0;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  align-self: center;
}

.moved__posture[data-posture='pretrial'],
.moved__posture[data-posture='charged'],
.moved__posture[data-posture='developing'],
.moved__posture[data-posture='appeal'] {
  color: var(--accent-text);
}

/* demo.css */
/* ---------- Try-it: the sourcing ladder ---------- */
.try {
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  grid-template-areas:
    'deck copy'
    'deck after';
  grid-template-rows: auto 1fr;
  gap: clamp(32px, 5vw, 72px);
  align-content: start;
}

.try > :nth-child(1) {
  grid-area: deck;
  align-self: center;
}
/* Both right-hand blocks stack from the top. They used to meet in the middle,
   which was fine when the copy was six lines and left a hole once it was two. */
.try > :nth-child(2) {
  grid-area: copy;
  align-self: start;
}
.try__after {
  grid-area: after;
  align-self: start;
}

.deck {
  position: relative;
  width: min(100%, 400px);
  margin-inline: auto;
  aspect-ratio: 4 / 3.85;
}

.deck__card {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: clamp(20px, 2.6vw, 28px);
  border-radius: var(--r-card);
  overflow: hidden;
  background: var(--card);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-lift);
  cursor: grab;
  user-select: none;
  will-change: transform;
  /* pan-y, not none: horizontal drags reach us while the browser keeps
     vertical scrolling. Owning the vertical axis too would make the deck —
     which is most of a phone screen — a place the page cannot be scrolled
     past. The third answer stays on its own button, which is how the copy
     describes it on touch. */
  touch-action: pan-y;
  transition:
    transform var(--dur) var(--ease),
    opacity var(--dur) var(--ease);
}

.deck__card[data-dragging='true'] {
  cursor: grabbing;
  transition: none;
}

.deck__kind {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  align-self: flex-start;
  padding: 5px 12px;
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
}

.deck__claim {
  margin: 0;
  font-size: clamp(1.1rem, 0.98rem + 0.6vw, 1.38rem);
  font-weight: 600;
  line-height: 1.32;
  letter-spacing: -0.015em;
  text-wrap: pretty;
}

/* The empty middle of the card is now the point: an unfilled label slot, so the
   dead space says "nothing has been attached to this yet" instead of reading as
   a layout accident. */
.deck__slot {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  margin-top: var(--s-2);
  padding: 6px 14px;
  border: 1px dashed var(--hairline);
  border-radius: var(--r-pill);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}

.deck__sources {
  margin: auto 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 6px;
  border-top: 1px solid var(--hairline);
  padding-top: var(--s-4);
}

.deck__sources li {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: 0;
  font-size: 0.86rem;
  color: var(--ink-soft);
}

.deck__sources svg {
  width: 15px;
  height: 15px;
  flex: none;
  color: var(--muted);
}

/* The drag stamps. Two only — the third answer is button-driven, exactly as in
   the app, where "developing" is never a swipe. */
.deck__stamp {
  position: absolute;
  top: var(--s-5);
  padding: 8px 16px;
  border-radius: var(--r-pill);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fff;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
  pointer-events: none;
}

.deck__stamp--corroborated {
  right: var(--s-5);
  background: var(--ok);
  rotate: 8deg;
}

.deck__stamp--single {
  left: var(--s-5);
  background: var(--v-single-ink);
  rotate: -8deg;
}

/* The reveal: the label the app would actually attach, and why. It covers the
   card rather than replacing it, so the claim stays readable underneath the
   verdict — that pairing is the whole point of the exercise. */
.deck__reveal {
  position: absolute;
  inset: auto 0 0;
  display: grid;
  gap: var(--s-2);
  padding: clamp(18px, 2.4vw, 24px);
  background: var(--surface);
  border-top: 1px solid var(--hairline);
  translate: 0 100%;
  transition: translate var(--dur) var(--ease);
}

.deck__card[data-revealed='true'] .deck__reveal {
  translate: 0 0;
}

.deck__verdict {
  margin: 0;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--ink);
}

.deck__why {
  margin: 0;
  font-size: 0.86rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

.deck__empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  gap: var(--s-3);
  padding: var(--s-6);
  border-radius: var(--r-card);
  border: 1.5px dashed var(--hairline);
  background: var(--card);
  text-align: center;
}

.deck__empty b {
  font-size: 1.3rem;
}

.deck__empty p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
}

/* ---------- The label chips (shared with the ladder section) ---------- */
.vlabel {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px 5px 9px;
  border-radius: var(--r-pill);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  /* Colour is REINFORCEMENT, never the carrier: every chip on this site is a
     word plus a mark, and reads correctly in greyscale. `verification.dart`
     makes the same rule in the app. */
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--hairline);
}

.vlabel svg {
  width: 14px;
  height: 14px;
  flex: none;
}

.vlabel--corroborated {
  color: var(--v-corroborated-ink);
  border-color: rgb(var(--ok-rgb) / 32%);
  background: rgb(var(--ok-rgb) / 9%);
}
.vlabel--single {
  color: var(--v-single-ink);
  border-color: rgb(var(--v-single-rgb) / 36%);
  background: rgb(var(--v-single-rgb) / 10%);
}
.vlabel--developing {
  color: var(--v-developing-ink);
  border-color: rgb(var(--v-developing-rgb) / 32%);
  background: rgb(var(--v-developing-rgb) / 9%);
}
.vlabel--unverified {
  color: var(--v-unverified-ink);
  border-color: rgb(var(--v-unverified-rgb) / 32%);
  background: rgb(var(--v-unverified-rgb) / 9%);
}
.vlabel--disputed {
  color: var(--v-disputed-ink);
  border-color: rgb(var(--v-disputed-rgb) / 32%);
  background: rgb(var(--v-disputed-rgb) / 8%);
}

/* Quieter than the five ladder steps on purpose — these two are statements of
   provenance, not positions on it. Bare `.vlabel` read as unstyled instead. */
.vlabel--factcheck {
  color: var(--v-factcheck-ink);
  border-color: rgb(var(--v-factcheck-rgb) / 32%);
  background: rgb(var(--v-factcheck-rgb) / 9%);
}
.vlabel--social {
  color: var(--v-social-ink);
  border-color: rgb(var(--v-social-rgb) / 36%);
  background: rgb(var(--v-social-rgb) / 9%);
}

/* On the noir bands the light fills disappear; give them a rim and a lifted ink. */
.section--accent .vlabel,
.on-accent .vlabel {
  background: var(--white-10);
  border-color: var(--white-24);
  color: #fff;
}

/* ---------- Controls ---------- */
/* Without JS the buttons cannot do anything, so they are not offered. */
.deck-controls,
.deck-hint {
  display: none;
}

.js .deck-hint {
  display: block;
}

.deck-nojs {
  margin-top: var(--s-5);
  text-align: center;
  font-size: var(--fs-small);
  color: var(--muted);
}

.js .deck-nojs {
  display: none;
}

/* IMPORTANT: a grid, not a wrapping flex row. Three answer pills plus undo do
   not fit 400px, so "Corroborated" orphaned onto its own second line and the
   core interaction looked misassembled. The grid also preserves ladder order. */
.js .deck-controls {
  display: grid;
  grid-template-columns: 44px repeat(3, minmax(0, 1fr));
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-6);
}

/* Word buttons, not icons. An icon-only "corroborated" would be a colour-coded
   claim about sourcing, which is the exact failure mode this app is built
   against. */
.deck-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 11px 10px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  background: var(--card);
  box-shadow: var(--shadow-sm);
  color: var(--ink);
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
  transition:
    transform var(--dur-fast) var(--ease-out-back),
    box-shadow var(--dur) var(--ease);
}

.deck-btn:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: var(--shadow-card);
}

.deck-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.deck-btn svg {
  width: 16px;
  height: 16px;
  flex: none;
}

.deck-btn--corroborated {
  color: var(--v-corroborated-ink);
  border-color: rgb(var(--ok-rgb) / 40%);
}

.deck-btn--single {
  color: var(--v-single-ink);
  border-color: rgb(var(--v-single-rgb) / 44%);
}

.deck-btn--developing {
  color: var(--v-developing-ink);
  border-color: rgb(var(--v-developing-rgb) / 40%);
}

.deck-btn--undo {
  width: 44px;
  height: 44px;
  padding: 0;
  justify-content: center;
  color: var(--muted);
}

@media (hover: none) and (pointer: coarse) {
  .deck-hint__keys {
    display: none;
  }
}

.deck-hint {
  margin-top: var(--s-4);
  text-align: center;
  font-size: var(--fs-small);
  color: var(--muted);
}

/* Not a score. The page says in #standards that nothing about a case is ever
   scored, and a running "3 out of 5" in the demo would have been the site
   breaking its own rule for a progress counter. What it shows instead is the
   comparison that is the actual payoff: what you said, what the app attaches. */
.tally {
  margin: 0 0 var(--s-5);
  padding-bottom: var(--s-5);
  border-bottom: 1px solid var(--hairline);
  font-size: 0.95rem;
  color: var(--ink-soft);
}

.tally b {
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.try__capture {
  padding: clamp(20px, 2.4vw, 28px);
  border-radius: var(--r-card);
  background: var(--card);
  border: 1px solid var(--hairline);
}

.try__capture > p {
  font-size: 0.98rem;
  color: var(--ink-soft);
  margin-bottom: var(--s-4);
}

.form--inline {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s-3);
  align-items: start;
}

.form--inline .form__status,
.form--inline .hp {
  grid-column: 1 / -1;
}

.form--inline .btn {
  white-space: nowrap;
}

/* ---------- "What does free actually cover?" ---------- */
.estimator {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
  padding: clamp(26px, 3.4vw, 44px);
  border-radius: var(--r-sheet);
  background: var(--card);
  box-shadow: var(--shadow-card);
  border: 1px solid var(--hairline);
}

.field {
  margin-bottom: var(--s-5);
}

.field:last-child {
  margin-bottom: 0;
}

.field__label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-3);
  margin-bottom: var(--s-3);
  font-size: 0.92rem;
  font-weight: 500;
}

.field__value {
  font-weight: 700;
  color: var(--accent-text);
  font-variant-numeric: tabular-nums;
}

/* IMPORTANT: --surface on the white card is 1.16:1 — the track was invisible
   and the control had no affordance until you found the thumb. The filled
   portion also reads the value at a glance; estimator.js sets --fill. */
input[type='range'] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 8px;
  border-radius: var(--r-pill);
  background:
    linear-gradient(var(--accent), var(--accent)) 0 / var(--fill, 12%) 100% no-repeat,
    var(--surface-alt);
  box-shadow: inset 0 0 0 1px var(--hairline);
  outline-offset: 6px;
}

input[type='range']::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--accent);
  border: 4px solid #fff;
  box-shadow: var(--shadow-accent);
  cursor: grab;
}

input[type='range']::-moz-range-thumb {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent);
  border: 4px solid #fff;
  cursor: grab;
}

/* A segmented control for the case's state. Radios, not buttons — the choice
   persists and has to be reachable by keyboard and announced as a group. */
.segmented {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  padding: 0;
  margin: 0;
  border: 0;
}

.segmented legend {
  padding: 0;
  margin-bottom: var(--s-3);
  font-size: 0.92rem;
  font-weight: 500;
}

.segmented label {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: 9px 16px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  background: var(--card);
  font-size: 0.86rem;
  font-weight: 500;
  cursor: pointer;
  transition:
    border-color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease);
}

.segmented input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.segmented label:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-dim);
  color: var(--accent-text);
  font-weight: 600;
}

.segmented label:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.estimate {
  display: grid;
  gap: var(--s-2);
  padding: clamp(24px, 3vw, 36px);
  border-radius: var(--r-card);
  background:
    radial-gradient(90% 70% at 84% 0%, rgb(var(--oxblood-mid-rgb) / 50%) 0%, transparent 62%),
    linear-gradient(155deg, var(--ground-warm) 0%, var(--ground) 100%);
  color: #fff;
  text-align: center;
}

.estimate b {
  font-size: clamp(2rem, 1.3rem + 2.3vw, 2.9rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  line-height: 1.05;
}

/* Direct child only — see the same note on .stat > span. */
.estimate > span {
  font-size: 0.95rem;
  color: var(--on-accent);
}

.estimate b span {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}

.estimate small {
  display: block;
  margin-top: var(--s-3);
  font-size: 0.78rem;
  color: var(--white-88);
  line-height: 1.55;
}

/* The free/premium split under the headline number. */
.estimate__split {
  display: grid;
  gap: var(--s-2);
  margin-top: var(--s-3);
  padding-top: var(--s-4);
  border-top: 1px solid var(--white-24);
  text-align: left;
}

.estimate__split p {
  display: flex;
  gap: var(--s-2);
  margin: 0;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--white-88);
}

.estimate__split svg {
  width: 15px;
  height: 15px;
  flex: none;
  margin-top: 3px;
}

.estimate__split [data-free] svg {
  color: var(--ok-on-dark);
}

.estimate__split [data-paid] svg {
  color: var(--accent-on-dark);
}

/* pricing.css */
.plans {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 24px);
  /* stretch, not start — otherwise .plan .btn { margin-top: auto } has no
     column height to push against and the three CTAs land at three heights. */
  align-items: stretch;
}

.plan {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(24px, 2.6vw, 34px);
  border-radius: var(--r-card);
  background: var(--card);
  border: 1px solid var(--hairline);
  transition:
    transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease);
}

.plan:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lift);
}

.plan--featured {
  border-color: transparent;
  box-shadow:
    0 0 0 2px var(--accent),
    var(--shadow-lift);
}

.plan__flag {
  position: absolute;
  top: -13px;
  left: 50%;
  translate: -50% 0;
  padding: 5px 14px;
  border-radius: var(--r-pill);
  background: var(--accent);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

.plan__name {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--s-3);
}

.plan__price {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  margin-bottom: var(--s-2);
}

.plan__price b {
  font-size: clamp(2rem, 1.5rem + 1.4vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
}

.plan__price span {
  font-size: 0.9rem;
  color: var(--muted);
}

.plan__note {
  margin-bottom: var(--s-5);
  font-size: 0.88rem;
  color: var(--ink-soft);
  min-height: 2.4em;
}

.plan__list {
  margin: 0 0 var(--s-6);
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--s-3);
}

.plan__list li {
  display: grid;
  grid-template-columns: 20px 1fr;
  gap: var(--s-3);
  margin: 0;
  font-size: 0.94rem;
  color: var(--ink-soft);
  align-items: start;
}

.plan__list svg {
  width: 18px;
  height: 18px;
  margin-top: 3px;
  color: var(--ok-text);
}

.plan__list li[data-off] svg {
  color: var(--muted);
}

.plan__list li[data-off] {
  color: var(--muted);
}

.plan .btn {
  margin-top: auto;
}

.plan__footnote {
  margin-top: var(--s-3);
  font-size: 0.76rem;
  color: var(--muted);
  text-align: center;
}

/* Never-paywalled promise sits directly under the plans, on purpose. */
.pledge {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: var(--s-5);
  align-items: center;
  margin-top: clamp(32px, 4vw, 48px);
  padding: clamp(22px, 2.6vw, 32px);
  border-radius: var(--r-card);
  background: var(--surface-alt);
}

.pledge__mark {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: #fff;
  color: var(--accent-text);
}

.pledge__mark svg {
  width: 28px;
  height: 28px;
}

.pledge b {
  display: block;
  margin-bottom: 2px;
}

.pledge p {
  margin: 0;
  color: var(--ink-soft);
  font-size: 0.95rem;
  max-width: var(--measure);
}

/* forms.css */
.form {
  display: grid;
  gap: var(--s-4);
}

.form__row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-4);
}

.form label {
  display: block;
  margin-bottom: var(--s-2);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--ink);
}

.form input[type='text'],
.form input[type='email'],
.form select,
.form textarea {
  width: 100%;
  padding: 14px 16px;
  border: 1px solid var(--hairline);
  border-radius: 14px;
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: 0.95rem;
  transition:
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}

.form textarea {
  min-height: 148px;
  resize: vertical;
}

.form input:focus,
.form select:focus,
.form textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.form input[aria-invalid='true'],
.form textarea[aria-invalid='true'] {
  border-color: var(--accent);
}

.form__error {
  margin-top: 6px;
  font-size: 0.8rem;
  color: var(--accent-text);
  min-height: 1em;
}

/* The waitlist form sits on the crimson band, so its chrome has to invert. */
.section--accent .form label {
  color: #fff;
}

.section--accent .form__error {
  color: var(--tint-error);
}

.section--accent .form input {
  border-color: transparent;
}

.section--accent .form input:focus {
  box-shadow: 0 0 0 3px var(--white-72);
}

.section--accent .form__status[data-state='error'] {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}

.section--accent .form__status[data-state='ok'] {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}

.section--accent .form__status[data-state='sending'] {
  background: rgba(255, 255, 255, 0.12);
  color: var(--white-88);
}

.form__consent {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: var(--s-3);
  align-items: start;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.form__consent input {
  width: 20px;
  height: 20px;
  margin-top: 2px;
  accent-color: var(--accent);
}

.form__consent[data-invalid] {
  color: var(--accent-text);
}

.form__consent[data-invalid] input {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

.form__status {
  padding: var(--s-4) var(--s-5);
  border-radius: 14px;
  font-size: 0.92rem;
  display: none;
}

.form__status[data-state='ok'] {
  display: block;
  background: rgb(var(--ok-rgb) / 12%);
  color: var(--ok-text);
}

.form__status[data-state='error'] {
  display: block;
  background: var(--surface-alt);
  color: var(--accent-pressed);
}

.form__status[data-state='sending'] {
  display: block;
  background: var(--surface);
  color: var(--ink-soft);
}

/* Honeypot — never shown, never focusable by tab order. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------- Cookie / analytics consent ---------- */
.consent {
  position: fixed;
  z-index: var(--z-banner);
  inset: auto var(--gutter) var(--gutter) auto;
  width: min(400px, calc(100vw - var(--gutter) * 2));
  padding: var(--s-5);
  border-radius: var(--r-sheet);
  background: #fff;
  box-shadow: var(--shadow-lift);
  border: 1px solid var(--hairline);
  translate: 0 140%;
  visibility: hidden;
  transition:
    translate var(--dur-slow) var(--ease),
    visibility 0s linear var(--dur-slow);
}

.consent[data-open='true'] {
  translate: 0 0;
  visibility: visible;
  transition-delay: 0s;
}

.consent p {
  font-size: 0.88rem;
  color: var(--ink-soft);
}

.consent__actions {
  display: flex;
  gap: var(--s-3);
}

.consent__actions .btn {
  flex: 1;
}

/* ---------- Smart install bar (mobile) ---------- */
.installbar {
  position: fixed;
  z-index: var(--z-banner);
  inset: auto 0 0;
  display: none;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--gutter);
  padding-bottom: calc(var(--s-3) + env(safe-area-inset-bottom));
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: blur(14px);
  box-shadow: 0 -1px 0 var(--hairline);
  translate: 0 110%;
  visibility: hidden;
  transition:
    translate var(--dur-slow) var(--ease),
    visibility 0s linear var(--dur-slow);
}

.installbar[data-open='true'] {
  translate: 0 0;
  visibility: visible;
  transition-delay: 0s;
}

.installbar img {
  width: 42px;
  height: 42px;
  border-radius: 11px;
}

.installbar div {
  flex: 1;
  min-width: 0;
  line-height: 1.25;
}

.installbar b {
  display: block;
  font-size: 0.92rem;
}

.installbar small {
  color: var(--muted);
  font-size: 0.75rem;
}

.installbar__close {
  border: 0;
  background: none;
  color: var(--muted);
  padding: var(--s-2);
  line-height: 0;
}

.installbar__close svg {
  width: 16px;
  height: 16px;
}

@media (max-width: 960px) {
  .installbar {
    display: flex;
  }
}

/* ---------- The pre-launch notify form ---------- */
/* Lives on the noir band, so it inherits nothing from the light-page form
   styles that assume a white card underneath. */
.notify {
  margin-top: var(--s-5);
}

/* IMPORTANT: the constraint is the CARD, not the viewport. A viewport media
   query left the field at 194px inside a 428px column on desktop — narrower
   than its own submit, and too narrow to see an ordinary address while typing
   it, on the site's only conversion. */
.notify__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-3);
  align-items: start;
}

.notify__row .btn {
  justify-self: start;
}

@container (min-width: 470px) {
  .notify__row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .notify__row .btn {
    justify-self: stretch;
  }
}

.notify__row .field-wrap {
  margin: 0;
}

.notify__row .btn {
  white-space: nowrap;
}

.notify__note {
  margin: var(--s-4) 0 0;
  font-size: 0.8rem;
  line-height: 1.55;
  color: var(--muted);
}

.section--accent .notify__note {
  color: var(--white-72);
}

.section--accent .notify input {
  background: var(--white-10);
  border-color: var(--white-24);
  color: #fff;
}

.section--accent .notify input::placeholder {
  color: var(--white-64);
}



/* prose.css */
/* Legal + support pages: inner pages get a light nav and a document layout. */
.page-head {
  padding-top: clamp(112px, 14vh, 156px);
  padding-bottom: clamp(32px, 5vw, 56px);
  background: var(--surface);
}

.page-head h1 {
  margin-bottom: var(--s-3);
}

.page-head p {
  color: var(--ink-soft);
  max-width: var(--measure);
}

.page-head .eyebrow {
  color: var(--accent-text);
}

.prose {
  max-width: 70ch;
}

.prose h2 {
  margin-top: clamp(40px, 5vw, 56px);
  font-size: clamp(1.3rem, 1.1rem + 0.7vw, 1.6rem);
}

.prose h2:first-child {
  margin-top: 0;
}

.prose h3 {
  margin-top: var(--s-6);
  font-size: 1.05rem;
}

.prose p,
.prose li {
  color: var(--ink-soft);
}

.prose ul {
  padding-left: 1.1em;
}

.prose li::marker {
  color: var(--accent-text);
}

.prose table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: var(--s-5);
  font-size: 0.92rem;
}

.prose th,
.prose td {
  padding: var(--s-3) var(--s-4);
  text-align: left;
  border-bottom: 1px solid var(--hairline);
  vertical-align: top;
}

.prose th {
  font-weight: 600;
  color: var(--ink);
  background: var(--surface);
}

.prose td {
  color: var(--ink-soft);
}

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

.callout {
  padding: var(--s-5);
  margin-bottom: var(--s-5);
  border-radius: var(--r-card);
  background: var(--surface-alt);
  border-left: 3px solid var(--accent);
}

.callout p {
  margin: 0;
  color: var(--ink);
  font-size: 0.95rem;
}

.toc {
  position: sticky;
  top: 110px;
  align-self: start;
  padding: var(--s-5);
  border-radius: var(--r-card);
  background: var(--surface);
  font-size: 0.9rem;
}

.toc b {
  display: block;
  margin-bottom: var(--s-3);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.toc ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--s-2);
}

.toc li {
  margin: 0;
}

.toc a {
  color: var(--ink-soft);
  text-decoration: none;
}

.toc a:hover {
  color: var(--accent-text);
}

.doc-layout {
  display: grid;
  grid-template-columns: 230px minmax(0, 72ch);
  justify-content: center;
  gap: clamp(32px, 5vw, 72px);
  align-items: start;
}

/* Press kit asset grid */
.assets {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--s-4);
}

.asset {
  display: grid;
  gap: var(--s-3);
  padding: var(--s-4);
  border-radius: var(--r-card);
  border: 1px solid var(--hairline);
  background: var(--card);
  text-align: center;
  font-size: 0.85rem;
  color: var(--ink-soft);
  text-decoration: none;
}

.asset:hover {
  border-color: var(--accent);
  color: var(--accent-text);
}

.asset__thumb {
  display: grid;
  place-items: center;
  padding: var(--s-4);
  border-radius: var(--r-chip);
  background: var(--surface);
  min-height: 120px;
}

.asset__thumb img {
  max-height: 120px;
  width: auto;
  border-radius: 8px;
}

/* 404 */
.notfound {
  display: grid;
  place-items: center;
  min-height: 72vh;
  text-align: center;
  padding-top: 100px;
}

.notfound__code {
  font-size: clamp(4rem, 2rem + 9vw, 8rem);
  font-weight: 700;
  letter-spacing: -0.05em;
  line-height: 1;
  color: var(--accent);
}

/* footer.css */
.footer {
  padding-block: clamp(56px, 7vw, 88px) var(--s-6);
  background: var(--ground-lift);
  border-top: 1px solid var(--white-10);
  color: rgba(255, 255, 255, 0.82);
  font-size: 0.92rem;
}

.footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) repeat(3, minmax(0, 0.9fr));
  gap: clamp(28px, 4vw, 56px);
  padding-bottom: clamp(32px, 4vw, 56px);
}

.footer__brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: #fff;
  font-weight: 700;
  font-size: 1.05rem;
  text-decoration: none;
  margin-bottom: var(--s-4);
}

.footer__brand img {
  width: 34px;
  height: 34px;
  border-radius: 10px;
}

.footer__blurb {
  max-width: 34ch;
  color: rgba(255, 255, 255, 0.74);
  font-size: 0.9rem;
}

.footer__heading {
  margin-bottom: var(--s-4);
  color: #fff;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.footer ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--s-3);
}

.footer li {
  margin: 0;
}

.footer a {
  color: rgba(255, 255, 255, 0.82);
  text-decoration: none;
}

.footer a:hover {
  color: #fff;
}

.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--s-3) var(--s-5);
  padding-top: var(--s-5);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  font-size: 0.82rem;
  color: rgba(255, 255, 255, 0.68);
}

.footer__bottom p {
  margin: 0;
}

.footer__legalnote {
  max-width: 62ch;
  margin-top: var(--s-3);
  font-size: 0.76rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.62);
}

/* motion.css */
/* Scroll-reveal. Elements start hidden only when JS is present, so a
   no-JS visitor gets the full page immediately. */
.js [data-reveal] {
  opacity: 0;
  translate: 0 18px;
  transition:
    opacity var(--dur-slow) var(--ease),
    translate var(--dur-slow) var(--ease);
}

.js [data-reveal].is-in {
  opacity: 1;
  translate: 0 0;
}

.js [data-reveal-stagger] > * {
  opacity: 0;
  translate: 0 18px;
  transition:
    opacity var(--dur-slow) var(--ease),
    translate var(--dur-slow) var(--ease);
  transition-delay: calc(var(--i, 0) * 70ms);
}

.js [data-reveal-stagger].is-in > * {
  opacity: 1;
  translate: 0 0;
}

/* Progress bar for the reading position on long pages. */
.scrollbar-progress {
  position: fixed;
  inset: 0 auto auto 0;
  z-index: var(--z-nav);
  height: 3px;
  width: 100%;
  transform-origin: left;
  scale: var(--progress, 0) 1;
  background: var(--accent);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  .js [data-reveal],
  .js [data-reveal-stagger] > * {
    opacity: 1;
    translate: none;
  }

  .hero__stage .device,
  .hero__chip {
    animation: none;
  }
}

/* responsive.css */
@media (max-width: 1080px) {
  .footer__top {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

/* Below this the stage is too narrow for the chips to sit beside the phone
   rather than on top of its controls. The trust strip carries the same facts. */
@media (max-width: 1100px) {
  .hero__chip {
    display: none;
  }

  .hero__props {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* At two columns the last card is orphaned; let it take the row. Scoped so it
   can never leak into the one-column layout, where a span-2 on a one-column
   grid makes the browser invent a second column and the section collapses. */
@media (min-width: 641px) and (max-width: 960px) {
  .feature:last-child:nth-child(even) {
    grid-column: span 2;
  }
}

@media (max-width: 960px) {
  .hero__inner,
  .split,
  .split--wide-left,
  .try,
  .download,
  .estimator {
    grid-template-columns: minmax(0, 1fr);
  }

  /* IMPORTANT: this is a ROW gap once the hero is one column, and .hero h1
     already carries its own margin-bottom. --s-8 on top of that put 88px of
     nothing between the headline and its lead, and pushed the CTA off every
     iPhone screen. */
  .hero__inner {
    row-gap: var(--s-5);
    column-gap: 0;
  }

  /* Single column, and the claim comes first — a marketing hero that opens with
     a phone mockup pushes the value proposition and the CTA below the fold. */
  .hero__inner {
    grid-template-areas:
      'title'
      'copy'
      'stage';
    grid-template-rows: auto;
  }

  .hero__stage {
    min-height: 0;
    margin-top: var(--s-6);
  }

  /* Explain the demo, then show it, then show what it did. The counters are the
     payoff and must sit below the deck, not above it where a swipe scrolls them
     off the top of the screen. */
  .try {
    display: flex;
    flex-direction: column;
    gap: var(--s-6);
  }

  .try > :nth-child(1) {
    order: 2;
  }
  .try > :nth-child(2) {
    order: 1;
  }
  .try__after {
    order: 3;
  }

  /* The install bar appears from here down, so the space it needs does too. */
  body[data-installbar='true'] .footer {
    padding-bottom: 96px;
  }

  body[data-installbar='true'] .consent {
    bottom: calc(var(--gutter) + 76px + env(safe-area-inset-bottom));
  }

  .hero__stage .device {
    max-width: 216px;
    rotate: 0deg;
  }

  .hero__chip--sourced {
    top: 2%;
    left: 0;
  }

  .hero__chip--night {
    bottom: 2%;
    left: 0;
  }

  /* The second handset is decoration and there is no room for it beside the
     first one once the hero is a single column. */
  .hero__stage .device--behind {
    display: none;
  }

  .doc-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .toc {
    position: static;
  }

  /* .steps stays at three: two columns always orphans the third. */
  .features,
  .plans,
  .cards {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .destinations,
  .section-head--split {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
  }

  .hero__props {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .feature--wide {
    grid-column: span 2;
  }



  .stats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-6);
  }

  .step::after {
    display: none;
  }
}

@media (max-width: 960px) {
  /* The horizontal scrim is sized for a desktop copy column that stops a third
     of the way across; from here down the paragraph is full width and runs
     straight into the clear band, so the scrim has to go vertical with it. */
  .fullbleed::after {
    background: linear-gradient(
      0deg,
      rgb(var(--ink-rgb) / 88%) 0%,
      rgb(var(--ink-rgb) / 74%) 55%,
      rgb(var(--ink-rgb) / 38%) 100%
    );
  }

  .fullbleed p {
    color: #fff;
  }

}

/* IMPORTANT: the grid that fixed the wrapping created an overflow. At 390px
   each answer track is 94px and "Single-sourced" measures 105px, so with
   white-space: nowrap the label painted OUTSIDE its own pill. The word has to
   be allowed to break — dropping it for an icon is not an option, because a
   colour-coded sourcing claim is the exact failure this product exists against. */
@media (max-width: 480px) {
  .deck-btn {
    white-space: normal;
    overflow-wrap: anywhere;
    text-align: center;
    line-height: 1.2;
    font-size: 0.75rem;
    padding: 9px 6px;
    gap: 4px;
    min-width: 0;
  }

  .deck-btn svg {
    width: 14px;
    height: 14px;
  }
}

@media (max-width: 640px) {
  :root {
    --fs-lead: 1.02rem;
  }

  /* Every 24px of hero padding is 24px the CTA moves down. */
  .hero {
    padding-top: 104px;
  }

  .destinations,
  .section-head--split,
  .hero__props,
  .rules,
  .chips,
  .promises--row {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The count moves under the name; a three-column row at 390px leaves the
     case name four words wide. */
  .moved__list li {
    grid-template-columns: minmax(0, 1fr);
    gap: 2px;
  }

  .moved__count {
    grid-column: 1;
  }

  .destinations__list li {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-1);
  }


  .features,
  .steps,
  .plans,
  .cards,
  .form__row,
  .form--inline {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The label chip needs its own line below ~640px or the meaning column is
     four words wide. */
  .ladder li,
  .ladder__row {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-2);
  }

  .feature--wide,
  .feature:last-child:nth-child(even) {
    grid-column: auto;
  }

  /* Let the next screenshot peek so the rail reads as a rail. */
  .screens__item {
    width: min(78vw, 264px);
  }

  .hero__actions {
    flex-direction: column;
    align-items: stretch;
  }

  .hero__actions .btn,
  .hero__actions .store-badge {
    width: 100%;
  }

  .hero__actions .store-badge {
    justify-content: center;
  }

  .roadmap__item {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-2);
  }

  .pledge {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-4);
  }

  .footer__top {
    grid-template-columns: minmax(0, 1fr);
  }

}

@media (prefers-contrast: more) {
  .card,
  .feature,
  .plan {
    border-color: var(--ink-soft);
  }

  .hero__lead,
  .section--accent p,
  .stat span {
    color: #fff;
  }
}

@media print {
  .nav,
  .installbar,
  .consent,
  .scrollbar-progress,
  .hero__wire,
  .device,
  .deck,
  .deck-controls,
  .fullbleed picture,
  .fullbleed img,
  .rail-controls {
    display: none !important;
  }

  .fullbleed {
    min-height: 0;
    background: none;
  }

  .fullbleed::after {
    display: none;
  }

  .fullbleed h2,
  .fullbleed p,
  .fullbleed .eyebrow,
  .fullbleed__stamp {
    color: var(--ink) !important;
  }

  .hero::after,
  .section--accent::after,
  .hero__chip {
    display: none !important;
  }

  .hero,
  .section--accent {
    background: none !important;
    color: var(--ink) !important;
  }
}
