/* ==========================================================================
   Wildheart · wildheart.app · site stylesheet (v1)

   Tokens carried VERBATIM from the app's design system:
     ios/Modules/DesignSystem/Sources/DesignSystem/Generated/DesignTokens.swift
     ios/Modules/DesignSystem/Sources/DesignSystem/CAColor.swift  (userRoute oxblood)
     ios/Modules/DesignSystem/Sources/DesignSystem/CAFont.swift   (type roles)

   Deliberately a single committed DARK theme for v1 (GTM decision #19).
   Every background and foreground is painted explicitly; nothing inherits
   the host's theme. The light pair is documented at the bottom of this file
   for whenever the site ships a second mode, per the same decision.

   House rules baked in: no em-dash anywhere, middle-dot separators for
   facts, honest absence (a missing value is omitted, never zero-filled).
   ========================================================================== */

/* Fonts are NOT @imported here any more. An @import inside a stylesheet cannot
   start downloading until this file itself has been fetched and parsed, which
   serialises two round trips before the first glyph is requested. Each page now
   carries the preconnect pair plus the stylesheet <link> in its <head>, which
   starts the font CSS in parallel with this file. If you add a page, copy that
   three-line block; if you change the family list, change it on every page.
   Every stack below still degrades to a system face, so a blocked or failed
   font request costs styling, never content. */

:root {
  color-scheme: dark;

  /* --- Palette · dark (canonical, from DesignTokens.Color.Dark) --- */
  --bg-page:       #06101F;
  --bg-card:       #0A1426;
  --bg-elevated:   #0F1A30;
  --fg-1:          #F4EFE4;
  --fg-2:          rgba(244, 239, 228, 0.72);
  --fg-3:          rgba(244, 239, 228, 0.50);
  --fg-4:          rgba(244, 239, 228, 0.30);
  --border-soft:   rgba(255, 255, 255, 0.08);
  --border-medium: rgba(255, 255, 255, 0.14);
  --border-strong: rgba(255, 255, 255, 0.28);
  --brand:         #F2B441; /* mustard · brandFill */
  --on-brand:      #06101F; /* navy ink on mustard */
  --brand-pop:     #E04A1A; /* canyon · commit grammar only, never a marketing CTA */
  --green:         #6FBF8B;
  --water:         #7FB9E8;

  /* --- Stripe band motif (4 stops) --- */
  --pale-sun:      #F4D970;
  --mustard:       #F2B441;
  --orange:        #F28736;
  --canyon:        #E04A1A;

  /* --- User-route trace (CAColor.userRoute, dark) --- */
  --oxblood:       #B4635A;

  /* --- Roles, so one page can carry a light mode without forking rules ---
     Each one EQUALS what the rule it replaced hard-coded, so on every
     dark-only page (legal, support, 404, route) nothing renders differently.
     Only the landing page's light pair below gives them other values. */
  --brand-ink:     var(--brand);              /* tappable/accent TEXT in brand */
  --focus-ring:    var(--brand);
  --link-rule:     rgba(242, 180, 65, 0.32);  /* the link underline */
  --device-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
  --device-shadow-sm: 0 12px 28px rgba(0, 0, 0, 0.45);
  --gutter:        var(--s4);                 /* mirrors .wrap's side padding */

  /* --- Type stacks (DesignTokens.Type.Family) --- */
  --font-display: 'DM Serif Display', 'Hoefler Text', Georgia, serif;
  --font-sans:    'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono:    'IBM Plex Mono', ui-monospace, Menlo, Consolas, monospace;

  /* --- Spacing (CASpacing) --- */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s6: 24px; --s8: 32px; --s12: 48px; --s16: 64px; --s24: 96px;

  /* --- Radius (CARadius) --- */
  --r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-xl: 20px; --r-pill: 999px;

  /* --- Motion --- */
  --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur: 200ms;
}

/* ---------- Light mode · DORMANT, one attribute away ----------
   GTM decision #19 ships the marketing site dark-only, and every page,
   the landing page included, follows it: a light landing page beside dark
   legal pages reads wrong. The real-screens pass built a light landing page
   on 2026-10-31 and the same day it was switched off on the coordinator's
   call. This block is kept, finished and contrast-gated, so that turning it
   back on is ONE attribute: add data-scheme="auto" to index.html's <html>,
   and set its theme-color and color-scheme metas to the light-and-dark
   pairs route/index.html uses. No page carries the attribute today, so
   nothing below applies anywhere, and privacy, terms, support and the 404
   render byte-for-byte as they did on main either way.

   Values are the light pair documented at the foot of this file (from
   DesignTokens.Color.Light plus the CD-locked brand-ink split), with the two
   fixes the route page's WCAG walk found, which is why they differ from that
   comment:
     · --fg-3 is 0.60, not the documented 0.55. 0.55 measures 4.13:1 on
       bg-page and fails AA; 0.60 measures 4.85:1. Same departure as route.css.
     · the focus ring is fg-1, not mustard. Mustard on cream is 1.67:1, which
       is not a focus indicator; fg-1 is the page's own ink.
   Mustard stays the FILL in both modes (sun, stripe band, step numbers);
   tappable and accent TEXT takes the deep canyon brand ink. Every pair is in
   verify/sweep.py, both modes. The app frames themselves stay dark: the app
   is dark-locked, so a cream page holds dark screens, which is the truth. */
@media (prefers-color-scheme: light) {
  :root[data-scheme="auto"] {
    color-scheme: light;
    --bg-page:       #FAF3E2;
    --bg-card:       #FFFCF3;
    --bg-elevated:   #F7EFD9;
    --fg-1:          #06101F;
    --fg-2:          rgba(6, 16, 31, 0.72);
    --fg-3:          rgba(6, 16, 31, 0.60);
    --fg-4:          rgba(6, 16, 31, 0.30);
    --border-soft:   rgba(6, 16, 31, 0.08);
    --border-medium: rgba(6, 16, 31, 0.14);
    --border-strong: rgba(6, 16, 31, 0.28);
    --oxblood:       #6B2231;
    --brand-ink:     #C2410C;
    --focus-ring:    #06101F;
    --link-rule:     rgba(194, 65, 12, 0.32);
    --device-shadow: 0 24px 60px rgba(6, 16, 31, 0.22);
    --device-shadow-sm: 0 12px 28px rgba(6, 16, 31, 0.16);
  }
}

/* ---------- Reset ---------- */

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

html {
  background: var(--bg-page);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-width: 280px;
  background: var(--bg-page);
  color: var(--fg-1);
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 16px;
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

a {
  color: var(--brand-ink);
  text-decoration: none;
  border-bottom: 1px solid var(--link-rule);
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
a:hover, a:focus-visible { color: var(--fg-1); border-bottom-color: var(--fg-1); }

:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }

hr {
  border: 0;
  border-top: 1px solid var(--border-soft);
  margin: var(--s12) 0;
}

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

.wrap {
  width: 100%;
  max-width: 1020px;
  margin: 0 auto;
  padding: 0 var(--s4);
}

.wrap-narrow { max-width: 680px; }

.section { padding: var(--s12) 0; }
.section-tight { padding: var(--s8) 0; }

/* ---------- Type roles ---------- */

.eyebrow {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-3);
  margin: 0 0 var(--s4);
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -0.005em;
  color: var(--fg-1);
  margin: 0;
}

/* Second-clause accent is COLOR, never italic (type lock, 2026-06-27). */
.accent { color: var(--brand-ink); font-style: normal; }

.lede {
  font-size: 17px;
  line-height: 1.55;
  color: var(--fg-2);
  max-width: 34em;
  margin: 0;
}

.data {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--fg-3);
}

.muted { color: var(--fg-3); }

/* ---------- Header ---------- */

.site-header {
  padding: var(--s6) 0;
  border-bottom: 1px solid var(--border-soft);
  background: var(--bg-page);
}
.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
}
.brand-lockup {
  display: flex;
  align-items: center;
  gap: var(--s3);
  border-bottom: none;
  color: var(--fg-1);
}
.brand-lockup:hover, .brand-lockup:focus-visible { color: var(--fg-1); border-bottom: none; }
.brand-lockup img { width: 36px; height: 36px; border-radius: var(--r-md); }
.brand-lockup .brand-name {
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 1;
  letter-spacing: -0.005em;
}
.header-nav {
  display: flex;
  align-items: center;
  gap: var(--s4);
  font-size: 13px;
  font-weight: 600;
}
.header-nav a {
  color: var(--fg-2);
  border-bottom-color: transparent;
}
.header-nav a:hover, .header-nav a:focus-visible { color: var(--brand-ink); border-bottom-color: var(--brand-ink); }

/* ---------- Stripe band ---------- */

.stripe-band { display: flex; height: 5px; width: 100%; }
.stripe-band span { flex: 1; }
.stripe-band span:nth-child(1) { background: var(--pale-sun); }
.stripe-band span:nth-child(2) { background: var(--mustard); }
.stripe-band span:nth-child(3) { background: var(--orange); }
.stripe-band span:nth-child(4) { background: var(--canyon); }

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

.hero {
  position: relative;
  overflow: hidden;
  padding: var(--s16) 0 var(--s12);
  background: var(--bg-page);
}
.hero .wrap { position: relative; z-index: 1; }

.hero h1 {
  font-size: clamp(38px, 11vw, 68px);
  line-height: 1.02;
  margin: 0 0 var(--s6);
  /* Holds the headline to roughly two lines at every width and keeps it out of
     the sun motif's corner. Do not widen this without re-checking the overlap
     at 1200px and above. */
  max-width: 11em;
}
.hero .lede { font-size: 18px; max-width: 36em; }

/* The real app beside the ruled line. One column below 900px (copy, then the
   phone, centred); two from 900px, the same breakpoint the sun guard below
   turns on, because the two are one composition. */
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s12);
}
.device-hero {
  justify-self: center;
  width: min(250px, 72%);
}

/* Geometry matches the app's Welcome hero, not a web invention:
   ios/Modules/FeatureOnboarding/Sources/FeatureOnboarding/Frames/WelcomeFrame.swift
     :146  stripedSunDiameter   = 248
     :150  stripedSunBleedFraction = 0.36   -> 248 * 0.36 = 89.28 -> -89px
     :571  .offset(x: bleed, y: -bleed)     -> SYMMETRIC on both axes
     :573  settles at full opacity
   which implements Brand Assets/Trailhead Design System/handoff/tokens.json
   motif.striped_sun ("top-right corner, ~64% visible, offset = -36% of
   diameter"). Full opacity is the hero norm; the dimmed placements in the app
   (DebriefView 0.5, BriefRouteHero 0.14) are small watermarks behind content,
   which is a different job. The old 200px / -80 / -64 / 0.8 was none of these.

   NOTE the size is NOT scaled up at wide widths any more. It is one constant,
   because the motif is a brand constant. */
.hero-sun {
  position: absolute;
  top: -89px;
  right: -89px;
  width: 248px;
  height: 248px;
  border-radius: 50%;
  overflow: hidden;
  opacity: 1;
  pointer-events: none;
  z-index: 0;
}
.hero-sun span { display: block; width: 100%; height: 25%; }
.hero-sun span:nth-child(1) { background: var(--pale-sun); }
.hero-sun span:nth-child(2) { background: var(--mustard); }
.hero-sun span:nth-child(3) { background: var(--orange); }
.hero-sun span:nth-child(4) { background: var(--canyon); }

.hero-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s3);
  margin-top: var(--s8);
}

/* App Store availability, as PLAIN TEXT. Replaced by Apple's official black
   "Download on the App Store" asset once the listing exists to link to.
   This deliberately has no border, no background, and no glyph: the previous
   treatment drew a badge lookalike around U+F8FF (the Apple logo), which is an
   Apple-only private-use codepoint that renders as tofu off Apple platforms and
   which Apple's identity guidelines do not allow as text or in a homemade
   badge. Do not give this rule a box. */
.store-note {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.01em;
  color: var(--fg-1);
  margin: 0;
}

.coverage-line {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--fg-2);
  margin: 0;
}

/* ---------- Cards ---------- */

.card {
  background: var(--bg-card);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-xl);
  padding: var(--s6);
  transition: border-color var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
/* Hover only, and quiet: the cards hold no links, so they take no focus and
   must not look like buttons. A border step and a 2px lift, no colour. */
.card:hover { border-color: var(--border-strong); transform: translateY(-2px); }

/* ---------- What it solves: four problem cards ----------
   Each card reads top to bottom as the problem (the heading), then a row of
   a small phone beside what Wildheart does and why you will love it.
   THE HEADING LEADS, on a phone above all: the founder's 2026-10-31 read was
   that the title lines were getting missed on mobile. So the heading spans
   the card's full width ABOVE the phone row (never beside or below the
   frame), in the display face at 26 to 30px on phones (7.2vw, clamped),
   which is the biggest type in the card and reads at arm's length, with the
   feature label small and mono above it. Measured at 360, 390 and 430: the
   heading sits above the frame and never runs past three lines. The
   phone sits BESIDE the text even on a 360px screen, because a stacked
   phone is what made the old tour long: here a card costs roughly the
   phone's height, not the phone plus the copy. One column below 900px, two
   from 900px. The frames are the same files the strip uses, so they are
   already cached by the time a visitor scrolls here. */
.problems { padding: var(--s12) 0; }
.problem-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s4);
  margin: 0;
  padding: 0;
  list-style: none;
}
.problem { padding: var(--s4); }
.problem-label {
  margin: 0 0 var(--s3);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--fg-3);
}
.problem-label .n { color: var(--brand-ink); }
.problem h3 {
  font-size: clamp(26px, 7.2vw, 30px);
  line-height: 1.12;
  letter-spacing: -0.01em;
  margin: 0 0 var(--s4);
  /* Even lines, so no heading strands one word on a line of its own (card
     01 left "have." alone at 1440). Browsers without it wrap as before. */
  text-wrap: balance;
}
.problem-body {
  display: flex;
  align-items: flex-start;
  gap: var(--s4);
}
.problem-body .device {
  --frame-pad: 5px;
  --frame-r: 22px;
  flex: 0 0 auto;
  width: clamp(112px, 36%, 172px);
  box-shadow: var(--device-shadow-sm);
}
.problem-text { flex: 1 1 0; min-width: 0; }
.problem-text p {
  margin: 0 0 var(--s3);
  font-size: 15px;
  line-height: 1.55;
  color: var(--fg-2);
}
/* Why you will love it: the page's own ink, a step heavier, under a rule,
   so the benefit reads as the card's last word without a label. */
.problem-text .love {
  margin: 0;
  padding-top: var(--s3);
  border-top: 1px solid var(--border-soft);
  font-weight: 600;
  color: var(--fg-1);
}

/* ---------- Device frames (CSS only; no phone image) ----------
   The frame is drawn, not photographed: padding, a black bezel, the radius,
   and the Dynamic Island. Simulator SCREENSHOTS never include the island,
   so without it the CSS phone reads as a screenshot with a border; the two
   screen RECORDINGS do include it, so the drawn island has to sit exactly on
   the recorded one or a sliver of the real pill shows around it. Geometry is
   measured off the clips (a black-run scan of a 620px-wide frame): 192px
   wide, rows 22 to 77, centred, which is 31% of the screen width, 3.55% of
   it down from the top, aspect 192/56. Both percentages are taken against
   the SCREEN (the padding box minus the frame padding on each side), not the
   frame, so the island lands on the same pixels at every phone size.
   Frame size is two custom properties, so the small frames in the strip and
   in the paired sections scale the bezel down with the phone. */
.device {
  --frame-pad: 8px;
  --frame-r: 34px;
  position: relative;
  display: block;
  margin: 0;
  padding: var(--frame-pad);
  background: #000;
  border: 1px solid var(--border-medium);
  border-radius: var(--frame-r);
  box-shadow: var(--device-shadow);
}
.device::before {
  content: "";
  position: absolute;
  top: var(--frame-pad);
  left: 50%;
  width: calc((100% - 2 * var(--frame-pad)) * 0.31);
  aspect-ratio: 192 / 56;
  margin-top: calc((100% - 2 * var(--frame-pad)) * 0.0355);
  transform: translateX(-50%);
  background: #000;
  border-radius: var(--r-pill);
  z-index: 1;
  pointer-events: none;
}
.device img,
.device video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 620 / 1347;
  object-fit: cover;
  border-radius: calc(var(--frame-r) - var(--frame-pad));
  background: #000;
}
.device video:focus-visible { outline-offset: 2px; }

/* ---------- The strip: seven frames, swipeable ----------
   No JavaScript needed: a flex row with mandatory x scroll-snap. It bleeds
   to the viewport edges and pads itself back to the content column, so the
   first frame lines up with the text above and the rest run off the edge,
   which is the cue that there is more. The padding is computed from 100% of
   the row, never from vw, so a classic scrollbar can never push the page
   sideways. Vertical padding leaves room for the hover lift and the focus
   ring, which an overflow row would otherwise clip. */
.screens { padding: var(--s12) 0 var(--s8); }
.screens-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s2) var(--s6);
  margin-bottom: var(--s3);
}
.screens-head .eyebrow { margin: 0; }

.strip {
  --strip-pad: max(var(--gutter), calc((100% - 1020px) / 2 + var(--gutter)));
  position: relative;
  display: flex;
  gap: var(--s4);
  margin: 0;
  padding: var(--s4) var(--strip-pad) var(--s6);
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--strip-pad);
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
/* About 1.6 frames per phone screen, so the next one is always visibly cut
   by the edge: the cue that the row scrolls, which a phone does not show as
   a scrollbar. At 390px a flat 44vw fitted exactly two and hid the cue. */
.strip-item {
  flex: 0 0 auto;
  width: clamp(168px, calc((100vw - 48px) / 1.6), 216px);
  scroll-snap-align: start;
}
.strip-link {
  display: block;
  color: var(--fg-1);
  border-bottom: none;
  border-radius: var(--r-lg);
}
.strip-link:hover, .strip-link:focus-visible { color: var(--fg-1); border-bottom: none; }
.strip-phone {
  --frame-pad: 5px;
  --frame-r: 26px;
  box-shadow: var(--device-shadow-sm);
  transition: transform var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.zoom-btn:hover .strip-phone,
.zoom-btn:focus-visible .strip-phone {
  transform: translateY(-4px);
  border-color: var(--brand-ink);
}
.strip-label {
  display: block;
  margin-top: var(--s3);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--fg-3);
}
.strip-caption {
  display: block;
  margin-top: var(--s1);
  font-size: 14px;
  line-height: 1.45;
  color: var(--fg-2);
  transition: color var(--dur) var(--ease-out);
}
.strip-link:hover .strip-caption,
.strip-link:focus-visible .strip-caption { color: var(--fg-1); }

/* Arrows and dots: shipped with the `hidden` attribute and revealed by the
   inline script, so no visitor ever sees controls that cannot work. The
   attribute rule is restated because display:flex below would beat the
   user-agent [hidden] rule on specificity. */
.strip-controls {
  display: flex;
  align-items: center;
  gap: var(--s3);
}
.strip-controls[hidden] { display: none; }
.strip-dots { display: flex; align-items: center; gap: var(--s2); margin-right: auto; }
.strip-dots span {
  width: 6px;
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--fg-4);
  transition: width var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.strip-dots span.is-active { width: 18px; background: var(--brand-ink); }
.strip-btn {
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  font: inherit;
  color: var(--fg-1);
  background: var(--bg-card);
  border: 1px solid var(--border-medium);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: border-color var(--dur) var(--ease-out);
}
.strip-btn:hover { border-color: var(--brand-ink); }
.strip-btn:disabled { opacity: 0.4; cursor: default; border-color: var(--border-soft); }

/* ---------- Founder note ---------- */

.founder {
  background: var(--bg-card);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-xl);
  padding: var(--s8) var(--s6);
}
.founder h2 { font-size: clamp(24px, 6vw, 32px); line-height: 1.12; margin: 0 0 var(--s4); }
.founder p {
  font-size: 16px;
  line-height: 1.65;
  color: var(--fg-2);
  max-width: 34em;
  margin: 0 0 var(--s4);
}
.founder p:last-child { margin-bottom: 0; }
/* The note's lines are short on purpose, so they sit closer together than
   paragraphs of prose would. The two links share a line and wrap cleanly:
   no separator glyph, which a wrap would strand at the end of a line. */
.founder p:not(.eyebrow):not(.founder-links) { margin-bottom: var(--s2); }
.founder .founder-links {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s2) var(--s6);
  margin-top: var(--s4);
}

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

.site-footer {
  border-top: 1px solid var(--border-soft);
  background: var(--bg-page);
  padding: var(--s12) 0 var(--s8);
  margin-top: var(--s12);
}
.footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3) var(--s6);
  font-size: 14px;
  font-weight: 600;
  margin: 0 0 var(--s6);
}
.footer-nav a { color: var(--fg-2); border-bottom-color: var(--border-medium); }
.footer-nav a:hover, .footer-nav a:focus-visible { color: var(--brand-ink); border-bottom-color: var(--brand-ink); }
.footer-legal {
  font-size: 13px;
  line-height: 1.7;
  color: var(--fg-3);
  margin: 0 0 var(--s3);
  max-width: 44em;
}
.footer-legal:last-child { margin-bottom: 0; }

/* ---------- Legal + support pages ---------- */

/* .legal shares an element with .wrap, so it must never use the padding
   shorthand: that would reset .wrap's horizontal gutter to zero and paste the
   text against the viewport edge. Set the vertical sides only. */
.legal { padding-top: var(--s12); padding-bottom: var(--s8); }
.legal h1 {
  font-size: clamp(32px, 9vw, 46px);
  line-height: 1.05;
  margin: 0 0 var(--s3);
}
.legal .updated {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--fg-3);
  margin: 0 0 var(--s8);
}
.legal h2 {
  font-size: 26px;
  line-height: 1.15;
  margin: var(--s12) 0 var(--s3);
  padding-top: var(--s4);
  border-top: 1px solid var(--border-soft);
}
.legal h3 {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 16px;
  letter-spacing: 0.01em;
  line-height: 1.4;
  margin: var(--s6) 0 var(--s2);
  color: var(--fg-1);
}
.legal p, .legal li {
  font-size: 15px;
  line-height: 1.7;
  color: var(--fg-2);
}
.legal p { margin: 0 0 var(--s4); }
.legal ul, .legal ol { margin: 0 0 var(--s4); padding-left: var(--s6); }
.legal li { margin-bottom: var(--s2); }
.legal strong { color: var(--fg-1); font-weight: 700; }

.callout {
  background: var(--bg-elevated);
  border: 1px solid var(--border-medium);
  border-left: 3px solid var(--brand);
  border-radius: var(--r-md);
  padding: var(--s4) var(--s6);
  margin: 0 0 var(--s6);
}
.callout p:last-child { margin-bottom: 0; }

.steps {
  counter-reset: step;
  list-style: none;
  padding-left: 0;
}
.steps li {
  position: relative;
  padding-left: 30px;
  margin-bottom: var(--s3);
}
.steps li::before {
  counter-increment: step;
  content: counter(step);
  position: absolute;
  left: 0;
  top: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--brand);
  color: var(--on-brand);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 11px;
  line-height: 20px;
  text-align: center;
}

.back-link {
  display: inline-block;
  margin-top: var(--s12);
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--brand-ink);
}

/* ---------- Responsive (mobile-first: widths ADD, never subtract) ---------- */

/* NARROW-WIDTH SUN GUARD · load-bearing, MEASURED, do not delete or narrow.
   At the canonical -89px bleed the sun's box runs from -89px to +159px inside
   .hero, and the h1's first line sits between +56px and +73px (a selection rect
   uses the font's full content area, which DM Serif Display overshoots at
   line-height 1.02, so the rect starts ABOVE the padding edge). Vertically they
   always overlap, so clearance has to come from one axis or the other:

     · At 900px and up it comes from the X axis. The h1 caps at 11em / 68px, so
       its longest first line is fixed at 659px of text; the sun's left edge is
       viewport-159px. 900 is the first round breakpoint where 159 + 659 + the
       24px gutter clears, and the binding case is hero-alt-2's "Nothing on this
       map is" (683px right edge vs a 741px sun edge = 58px clear). It is NOT
       index.html: measuring only the recommended variant would have passed this
       at 768 and shipped a collision on whichever line Kyle actually picks.
     · Below 900px the headline is simply too wide to clear on X at any bleed,
       so the sun goes up instead: -216px puts its bottom edge 32px into .hero,
       which is 24px above the highest first-line rect at 375px.

   Verified with Range.getClientRects() on every h1 line of all THREE hero
   variants against .hero-sun's box at 320 / 375 / 414 / 600 / 700 / 768 / 900 /
   1200 / 1440, both with the webfont loaded and with fonts.googleapis.com
   blocked so the fallback serif's taller metrics are covered too. Zero
   intersections. If you change .hero's padding, the sun diameter or bleed, the
   h1 font ramp, or the 11em cap, re-measure before shipping. */
@media (max-width: 899.98px) {
  .hero-sun { top: -216px; }
}

@media (min-width: 600px) {
  :root { --gutter: var(--s6); }
  .wrap { padding: 0 var(--s6); }
  .hero { padding: var(--s24) 0 var(--s16); }
  .section { padding: var(--s16) 0; }
  .screens { padding: var(--s16) 0 var(--s12); }
  .problems { padding: var(--s16) 0; }
  .problem { padding: var(--s6); }
  .founder { padding: var(--s12) var(--s8); }
  .hero-actions { flex-direction: row; align-items: center; gap: var(--s6); }
}

/* HERO PHONE vs THE SUN · measured, like the guard above. At 900px and up the
   sun's box reaches 159px into .hero (248 - 89) and the phone column sits
   under its left half at the narrow end of this range, so the phone starts
   88px below the copy's top: 96px of padding + 88 = 184px, 25px clear of the
   sun at every width. Above about 1300px the sun has moved right of the
   column and the offset is just the stagger. Re-measure if the padding, the
   sun, or this offset changes. */
@media (min-width: 900px) {
  /* Kyle 2026-11-02: "images are too small even on desktop to make out what's happening". On a
     wide screen the strip shows frames at 300px (about 3.2 in the 1020px column, so the scroll cue
     stays) and the card phones grow to 210px. Phones keep their measured sizes. */
  .strip-item { width: 300px; }
  .problem-body .device { width: clamp(112px, 40%, 210px); }
  .hero-grid {
    grid-template-columns: minmax(0, 1fr) 280px;
    align-items: start;
  }
  /* Centred on the row, which includes the phone's offset, so the copy sits
     a little above the phone's own middle: the optical centre, not the
     geometric one. Moving the copy DOWN only adds clearance from the sun. */
  .hero-copy { align-self: center; }
  .device-hero {
    justify-self: end;
    width: 280px;
    margin-top: calc(var(--s16) + var(--s6));
  }
  .problem-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s6); }
  .problem h3 { font-size: 26px; }
}

/* In-page jumps (a strip frame to its problem card) land with a little air
   above the card, and glide only for visitors who have not asked for
   reduced motion. The landing page only. */
html.home { scroll-padding-top: var(--s6); }
@media (prefers-reduced-motion: no-preference) {
  html.home { scroll-behavior: smooth; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ==========================================================================
   Light pair · the documented source (GTM decision #19: dark-only v1).
   From DesignTokens.Color.Light plus the CD-locked brand-ink split.
   WIRED on two pages only: /route/ via css/route.css, and the landing page
   via the data-scheme="auto" block near the top of this file. Both carry the
   two fixes the WCAG walk found (fg-3 at 0.60, a fg-1 focus ring), which is
   why their values differ from this list. Mustard ink on cream is 1.67:1.

     --bg-page: #FAF3E2 · --bg-card: #FFFCF3 · --bg-elevated: #F7EFD9
     --fg-1: #06101F · --fg-2: rgba(6,16,31,0.72) · --fg-3: rgba(6,16,31,0.55)
     --border-soft: rgba(6,16,31,0.08) · --border-medium: rgba(6,16,31,0.14)
     --brand (FILL, unchanged): #F2B441 · brand INK (tappable text): #C2410C
     --oxblood (light): #6B2231
   ========================================================================== */

/* ---------- The zoom layer: any phone, large ----------
   Kyle 2026-11-02: "if you click them maybe we let the user zoom?". The strip phones are
   buttons (the label and caption below them stay the link to the card); every clip or frame
   phone carries a small Enlarge control. The dialog is native, so focus, Esc and the backdrop
   come for free; the enlarged phone is the same .device frame, sized to the viewport. */
.zoom-btn {
  all: unset;
  display: block;
  cursor: zoom-in;
  border-radius: var(--r-lg);
}
.zoom-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 4px; }
.zoom-btn-overlay {
  position: absolute;
  right: 10px;
  bottom: 10px;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  padding: 0;
  color: var(--fg-1);
  background: rgba(6, 16, 31, 0.74);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  cursor: zoom-in;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.zoom-btn-overlay:hover, .zoom-btn-overlay:focus-visible { border-color: var(--brand-ink); color: var(--brand-ink); }
.device-hero .zoom-btn-overlay { right: 14px; bottom: 14px; }
.strip-phone .zoom-btn-overlay { display: none; }
html.zoom-open { overflow: hidden; }
dialog.zoom {
  width: 100vw;
  height: 100dvh;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: var(--s6) var(--s4);
  border: 0;
  background: transparent;
  color: var(--fg-1);
}
dialog.zoom::backdrop { background: rgba(6, 16, 31, 0.9); }
dialog.zoom[open] {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  justify-items: center;
  align-items: center;   /* never stretch: the frame's height is its screen's */
  align-content: center;
  gap: var(--s4);
}
.zoom-device {
  --frame-pad: 8px;
  --frame-r: 34px;
  width: min(calc((100dvh - 120px) * 620 / 1347), calc(100vw - 32px));
  max-width: 560px;
}
.zoom-caption {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--fg-2);
  text-align: center;
}
.zoom-close {
  position: fixed;
  top: var(--s4);
  right: var(--s4);
  z-index: 2;
  padding: 10px 14px;
  font: 500 12px/1 var(--font-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fg-1);
  background: var(--bg-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  cursor: pointer;
}
.zoom-close:hover, .zoom-close:focus-visible { border-color: var(--brand-ink); color: var(--brand-ink); }
.zoom-close:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }


/* A clip the visitor paused: the frame dims and says so. Set by the script on the .device. */
.device video.clip { cursor: pointer; }
.device.is-paused video.clip { filter: brightness(0.5); }
.device.is-paused::after {
  content: "Paused";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  padding: 8px 12px;
  font: 500 11px/1 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-1);
  background: rgba(6, 16, 31, 0.78);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  pointer-events: none;
}
