/* ==========================================================================
   Knowledgebooks.ai — Base Styles
   Tokens live in tokens.css — reference them via var(--token-name), never
   hardcode a color, size, or spacing value here. See docs/DESIGN_SYSTEM.md
   for the full spec this file should implement.
   ========================================================================== */

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  background: var(--cream);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--size-subhead-body);
  line-height: 1.55;
}

h1, h2, h3, .chorus-line, .close-italic, .signature {
  font-family: var(--font-serif);
}

/* Orphan control — headings balance across lines, body avoids single-word
   last lines. Progressive enhancement; ignored where unsupported. */
h1, h2, h3, h4,
.chorus-line, .close-italic, .indict-line, .score-name, .fork-exit {
  text-wrap: balance;
}
p, .kb-doc__facts, .receipt-line {
  text-wrap: pretty;
}

.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* The hidden attribute has to beat any author display rule. Three elements set
   their own display (.caret, .keep-caret, .kept-check), which silently
   defeated the UA's [hidden] rule. Two consequences, both pre-existing:
   the consent card rendered its check and a blinking caret before you ever
   pressed Keep it, so the receipt arrived before the consent; and under
   reduced motion, where main.js does set the hero caret's hidden, it stayed
   on screen as a stuck caret (DESIGN_SYSTEM invariant 2). On the motion path
   the hero caret is meant to keep blinking, and still does. */
[hidden] { display: none !important; }

/* ---------- Focus & pressed states (craft floor) ----------
   One visible focus ring for the whole site: gold, offset, drawn only for
   keyboard users (:focus-visible), so it never fights a mouse click. Gold
   reads on cream and on the forest band, so one rule covers both fields.
   Every interactive element also has a pressed state — nothing is inert. */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  border-radius: 2px;
}
.btn-primary:active,
.keep-yes:active,
.keep-no:active,
.receipt-chip:active,
.portrait-fact-edit:active { transform: translateY(1px); }
.btn-primary { transition: background-color 150ms var(--ease), transform 100ms var(--ease); }
.btn-primary:hover { background: #234A2D; }
.receipt-chip { transition: background-color 150ms var(--ease), transform 100ms var(--ease); }
.receipt-chip:hover { background: rgba(26, 56, 33, 0.13); }
.keep-no { transition: color 150ms var(--ease); }
.keep-no:hover { color: var(--ink); }
.nav-blog-link:hover, .link-dotted:hover { color: var(--forest); }
footer a:hover { color: var(--ink); }

/* ---------- Nav ---------- */
#site-nav {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--cream);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-hairline) var(--ease);
}
#site-nav.scrolled {
  border-bottom-color: var(--line);
}
.nav-inner {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--space-2) var(--content-gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.nav-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: var(--ink);
}
.nav-orb {
  width: 16px;
  height: 16px;
  background: url('../assets/orb.svg') center / contain no-repeat;
  animation: orb-breathe 7s ease-in-out infinite;
}
@keyframes orb-breathe {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.06); }
}
.nav-wordmark {
  font-family: var(--font-sans);
  font-size: var(--size-mono-meta);
  font-weight: 700;
  letter-spacing: 0.13em;
}
.nav-actions { display: flex; align-items: center; gap: var(--space-2); }
.nav-blog-link { color: var(--muted); text-decoration: underline dotted; font-size: var(--size-ui); }

/* ---------- Buttons ---------- */
.btn-primary {
  display: inline-block;
  background: var(--forest);
  color: var(--cream);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--size-ui);
  padding: 12px 24px;
  border-radius: var(--radius-pill);
  text-decoration: none;
  white-space: nowrap; /* never let the CTA label wrap, esp. in the nav */
}
.link-dotted {
  color: var(--ink);
  text-decoration: underline dotted;
  margin-left: var(--space-2);
}
.cta-qualifier {
  color: var(--muted);
  font-size: var(--size-mono-meta);
  margin-top: var(--space-1);
}
/* Pre-launch/launch toggle — see docs/CTA_LAUNCH_STATE.md. main.js adds
   .is-live to <body> on the launch flip; hide the qualifier and swap the
   label at that point. */
body.is-live .cta-qualifier { display: none; }
/* Beta invitation — quiet small-text beneath the September qualifier. The DM
   is the only path (no waitlist/email capture, deliberately). Hidden on launch. */
.beta-invite {
  font-family: var(--font-sans);
  font-size: var(--size-caption);
  color: var(--muted);
  margin-top: var(--space-1);
  line-height: 1.5;
}
.beta-invite a { color: var(--forest); text-decoration: underline dotted; text-underline-offset: 3px; }
body.is-live .beta-invite { display: none; }

/* ---------- Section rhythm ---------- */
section {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--section-rhythm) var(--content-gutter);
}

/* 01 Hero */
.hero-grid {
  display: grid;
  grid-template-columns: 1.02fr 0.98fr;
  gap: 64px;
  min-height: 84vh;
  align-items: center;
}
#hero h1 {
  font-size: var(--size-hero);
  font-weight: 500;
  line-height: 1.07;
  letter-spacing: -0.015em;
}
.subhead {
  font-size: var(--size-subhead-body);
  color: var(--muted);
  max-width: 44ch;
  margin-top: 26px;
}
.hero-actions { margin-top: 38px; display: flex; align-items: center; gap: 22px; }
.hero-actions .link-dotted { margin-left: 0; }
.hero-page-composition {
  position: relative;
  height: 560px;
}

/* ==========================================================================
   THE KNOWLEDGEBOOK DOCUMENT — .kb-doc
   The canonical component, promoted from the travel card (Jul 30). Every
   Knowledgebook visual on the page is an instance: the hero page, the page
   with your name on it, the consent result, the travel page, the anti-card.
   Anatomy: paper-white surface, faint ruled lines, serif title, mono meta,
   short gold rule under the title, serif facts at one size per card.

   An instance sets only --kb-* values plus its own width/position. It never
   restates the anatomy; that lives here, once.
     --kb-pad       card padding
     --kb-rule      ruled-line step, --kb-rule-end the 1px line's far edge
     --kb-rule-top  where the grid starts, so lines fall between entries
     --kb-title     serif title size
     --kb-fact      serif fact size, --kb-lead its line box

   Two expression rules, both budgeted:
   · TILT (.kb-doc--tilt) means a document in motion or on display. Travel
     only. Every in-place card renders flat.
   · STAMPS (.kb-doc__stamp) are the status language: one per card, mono,
     short, and only where it states a real status. Three on the page —
     travel (TRAVELS), consent (KEPT), anti-card (NOT YOURS TO READ).
   ========================================================================== */
.kb-doc {
  position: relative;
  background-color: var(--card-white);
  border: 1px solid var(--ink-border);
  border-radius: var(--radius-paper);
  padding: var(--kb-pad, 26px 30px 30px);
  /* Four-stop form with a literal end stop, so every instance's computed
     background-image is what it was before the promotion. */
  background-image: repeating-linear-gradient(
    transparent,
    transparent var(--kb-rule, 33px),
    var(--ruled-line) var(--kb-rule, 33px),
    var(--ruled-line) var(--kb-rule-end, 34px));
  background-position: 0 var(--kb-rule-top, 0px);
}
.kb-doc__head {
  display: flex;
  align-items: var(--kb-head-align, baseline);
  justify-content: space-between;
  gap: var(--kb-head-gap, 16px);
  flex-wrap: var(--kb-head-wrap, nowrap);
}
.kb-doc__title {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--kb-title, var(--size-page-title-sm));
  letter-spacing: -0.01em;
  color: var(--ink);
}
.kb-doc__meta { font-size: var(--size-mono-meta); color: var(--muted); white-space: nowrap; }
.kb-doc__rule {
  display: block;
  width: var(--kb-rule-w, 40px);
  height: var(--kb-rule-h, 2px);
  background: var(--gold);
  border-radius: 2px;
  margin-top: var(--kb-rule-gap, 12px);
}
.kb-doc__facts {
  font-family: var(--font-serif);
  font-size: var(--kb-fact, var(--size-page-line));
  line-height: var(--kb-lead, 34px);
  color: var(--ink);
  margin-top: var(--kb-facts-top, 12px);
}
.kb-doc__note { margin-top: var(--kb-note-top, 20px); font-size: var(--size-caption-sm); color: var(--muted); }
.kb-doc__stamp {
  position: absolute;
  right: 18px;
  bottom: 16px;
  font-size: var(--size-mono-meta);
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--gold);
  border: 1.5px solid var(--gold);
  border-radius: 3px;
  padding: 4px 9px;
  transform: rotate(-8deg);
  opacity: 0.85;
}
/* A document in motion or on display. Travel only — the tilt budget. */
.kb-doc--tilt { transform: rotate(-2deg); box-shadow: 0 10px 30px rgba(7, 25, 9, 0.10); }
/* Hero only: the page is taped in. An ornament on the canonical surface, not
   part of the anatomy. Remove this class and the tape child to conform fully. */
.kb-doc--taped { padding: 0; overflow: hidden; }

/* The anti-card — the same anatomy, gone cold. A Knowledgebook that isn't
   yours: paper without warmth, no ruled lines to read by, redactions where
   the facts should be, and the stamp in cold ink instead of gold. The
   geometry is deliberately untouched, including the stamp's angle: the
   diptych only works if the two cards are the same object, differently held. */
.kb-doc--cold {
  background-color: var(--cold-paper);
  border-color: var(--cold-bar);
  background-image: none;
}
.kb-doc--cold .kb-doc__title { color: var(--cold-ink); }
.kb-doc--cold .kb-doc__meta { color: var(--cold-ink); }
.kb-doc--cold .kb-doc__rule { background: var(--cold-bar); }
.kb-doc--cold .kb-doc__stamp { color: var(--cold-ink); border-color: var(--cold-ink); opacity: 1; }
.kb-redactions { display: flex; flex-direction: column; gap: var(--kb-redact-gap, 18px); }
.kb-redaction { display: block; height: var(--kb-redact-h, 15px); border-radius: 2px; background: var(--cold-bar); }
.kb-redaction:nth-child(1) { width: 86%; }
.kb-redaction:nth-child(2) { width: 71%; }
.kb-redaction:nth-child(3) { width: 92%; }
.kb-redaction:nth-child(4) { width: 58%; }

/* ==========================================================================
   THE ASSISTANT PANEL — .kb-panel
   The other half of the page-wide grammar the travel section established:
   Knowledgebooks are warm paper, the assistant is a muted device panel. Used
   by the travel trace card and the receipts answer card. Never a document.
   ========================================================================== */
.kb-panel {
  background: var(--forest-tint);
  border: 1px solid var(--ink-border);
  border-radius: 14px;
  padding: var(--kb-panel-pad, 18px 18px 16px);
}
.kb-panel__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.kb-panel__title { display: flex; align-items: center; gap: 8px; font-family: var(--font-serif); font-size: var(--size-ui); color: var(--ink); }
.kb-panel__chip {
  font-size: var(--size-mono-meta-sm);
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--muted);
  border: 1px solid var(--ink-border);
  border-radius: 5px;
  padding: 1px 6px;
  margin-left: 4px;
}
/* The one caret on the site — blinks, and vanishes under reduced motion. */
.caret {
  display: inline-block;
  width: 2px;
  height: 1em;
  background: var(--forest);
  margin-left: 2px;
  transform: translateY(3px);
  animation: caret-blink 1.1s steps(1) infinite;
}
@keyframes caret-blink { 0%, 45% { opacity: 1; } 55%, 100% { opacity: 0; } }

/* ---------- Hero composition ---------- */
.hero-chat-cluster {
  position: absolute;
  top: 0;
  left: -8px;
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: flex-start;
}
.hero-bubble {
  background: var(--forest-tint);
  border-radius: 16px 16px 16px 4px;
  padding: 12px 17px;
  font-size: var(--size-ui);
  color: var(--ink);
  opacity: 0.55;
  max-width: 230px;
  transition: opacity var(--dur-dissolve) var(--ease), filter var(--dur-dissolve) var(--ease);
}
/* Clearing: the message goes to nothing and leaves no hole. */
.hero-bubble.is-cleared { opacity: 0; filter: blur(2px); }

/* Keeping: the line that lifts out of the message and settles on the page.
   JS positions it over the bubble, then transitions it to the landing row. */
.hero-lift {
  position: absolute;
  z-index: 4;
  font-family: var(--font-serif);
  font-size: var(--size-page-line);
  line-height: 1.2;
  color: var(--ink);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translate(0, 0);
  transition: transform var(--dur-lift) var(--ease-lift),
              opacity 320ms var(--ease);
}
.hero-lift.is-lifting { opacity: 0.9; }
.hero-lift.is-landed { opacity: 0; }
.hero-timer-card {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--card-white);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 8px 12px;
  box-shadow: 0 1px 2px rgba(7, 25, 9, 0.05);
}
.hero-timer-label { font-size: var(--size-caption-sm); color: var(--muted); white-space: nowrap; }
/* "· you set the timer." — the control claim, riding beside both countdowns. */
.timer-note { font-size: var(--size-mono-meta); color: var(--muted); white-space: nowrap; }
.hero-timer-pill {
  font-size: var(--size-caption-sm);
  font-weight: 600;
  color: var(--amber-ink);
  background: var(--amber-tint);
  border-radius: 20px;
  padding: 2px 9px;
}
.hero-tabs {
  position: absolute;
  top: 44px;
  right: 12px;
  z-index: 1;
  display: flex;
  gap: 10px;
}
.hero-tab {
  background: var(--tape);
  border: 1px solid var(--line);
  border-bottom: none;
  border-radius: 10px 10px 0 0;
  padding: 9px 18px 26px;
  font-size: var(--size-caption-sm);
  font-weight: 600;
  color: rgba(26, 56, 33, 0.75);
  white-space: nowrap;
}
/* The hero page — a .kb-doc instance, flat and unstamped. The ruled grid
   starts 18px down so it clears the tape and lands where the body's own
   gradient used to: first line 33px below the tape, as before. */
.hero-page {
  --kb-rule-top: 18px;
  --kb-facts-top: 24px;
  --kb-title: var(--size-page-title);
  --kb-head-align: flex-start;
  position: absolute;
  top: 88px;
  left: 24px;
  right: 0;
  z-index: 2;
  box-shadow: var(--shadow-hero-card);
}
/* Tape strip + perforation — the hero page's signature only. */
.paper-tape { height: 18px; background: var(--tape); position: relative; }
.paper-perf {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 5px;
  height: 4px;
  background-image: radial-gradient(circle, rgba(112, 90, 90, 0.3) 1.4px, transparent 1.9px);
  background-size: 16px 4px;
}
/* The tape sits above the padded body; the card paints the ruled grid.
   44/56/48 is what this card actually rendered before the promotion: the old
   generic .paper-body rule sat later in the sheet and overrode the 30/38/34
   written here, so those were dead declarations. Keeping the rendered values
   so the hero composition doesn't shift. The ruled step does change, 42px to
   the canonical 33px, which is what .hero-page-body asked for in the first
   place and now matches the 34px fact leading instead of drifting across it. */
.hero-page-body { padding: 44px 56px 48px; }
/* Reserved height for the fourth fact typewriting in — the signature moment
   must not reflow the card as it lands. */
.hero-page-facts { min-height: 136px; }
.hero-typed-row { display: flex; align-items: center; }
.hero-typed-row .caret { height: 21px; transform: none; }

/* 02 Problem — two-column: the trap in words, beside the four tellings */
#problem {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 64px;
  align-items: center;
  padding-top: var(--space-7);
  padding-bottom: var(--space-7);
}
#problem h2 {
  font-size: var(--size-section);
  max-width: 22ch;
}
#problem p {
  font-size: var(--size-subhead-body);
  line-height: 1.7;
  max-width: 56ch;
  margin-top: var(--space-3);
}
/* The four tellings — same fact, retyped March→June, decaying as they age.
   Default opacities below are the settled decay (also the reduced-motion and
   no-JS state); main.js hides them and re-reveals on scroll when motion is ok. */
.problem-tellings {
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-width: 400px;
  width: 100%;
  justify-self: end;
}
.telling {
  transform-origin: left center;
  transition: opacity var(--dur-stamp) var(--ease-snap), transform var(--dur-stamp) var(--ease-snap);
}
.telling:nth-child(1) { opacity: 0.18; }
.telling:nth-child(2) { opacity: 0.30; }
.telling:nth-child(3) { opacity: 0.50; }
.telling:nth-child(4) { opacity: 0.82; }
.telling-month { font-size: var(--size-mono-meta); color: var(--muted); margin-bottom: 6px; }
.telling-bubble {
  background: var(--forest-tint);
  border-radius: 16px 16px 4px 16px;
  padding: 13px 17px;
  font-size: var(--size-ui);
  line-height: 1.5;
  color: var(--ink);
}
@media (max-width: 900px) {
  #problem { grid-template-columns: 1fr; gap: 48px; }
  .problem-tellings { justify-self: start; max-width: 340px; }
}

/* Shared eyebrow — mono, muted, tracked label above a section. */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--size-mono-meta);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-3);
}
.eyebrow--center { text-align: center; }

/* 03 Indictment — the four failures, named. Typography only; staggered
   reveal on scroll (main.js). Defaults are the visible reduced-motion state. */
/* Two columns: the four failures in words, beside the anti-card that shows
   them. The diptych partner to the page card in beat 05 — same component,
   inverted, which is the whole argument in one object. */
#indictment {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 64px;
  align-items: center;
  padding-top: var(--space-6);
  padding-bottom: var(--space-6);
}
.indict-visual { justify-self: end; width: 100%; max-width: 420px; }
.anti-doc { --kb-pad: 26px 30px 78px; }
.indict-lines {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: 20ch;
}
.indict-line {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--size-indict);
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--ink);
  transition: opacity 450ms var(--ease), transform 450ms var(--ease);
}

/* 03b Symmetry — the warm mirror of the indictment. Same stacked
   list; warmed with a gold marker on each property (the positives) vs. the
   cold, unmarked negatives above. Staggered reveal via main.js. */
#symmetry { padding-top: var(--space-6); padding-bottom: var(--space-6); }
.symmetry-h2 {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--size-submoment);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.symmetry-intro {
  font-family: var(--font-serif);
  font-size: var(--size-subhead-body);
  line-height: 1.7;
  color: var(--ink);
  max-width: 60ch;
  margin-top: var(--space-2);
}
/* The scorecard. Each row grades a property against the demo above it: the
   mark fills gold as the row enters view (main.js staggers it). The fourth
   row stays a dashed outline — travel is late 2026, so it is not checked. */
.score-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-4);
  max-width: 52ch;
}
.score-item {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}
.score-mark {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-sans);
  font-size: var(--mid-sans-2);
  font-weight: 700;
  /* unchecked resting state — the check scales in when .is-scored lands */
  color: transparent;
  background: transparent;
  border: 1.5px solid var(--ink-border);
  transform: scale(0.9);
  transition: color 240ms var(--ease), background-color 240ms var(--ease),
              border-color 240ms var(--ease), transform 240ms var(--ease);
}
.score-mark.is-scored {
  color: var(--amber-ink);
  background: var(--amber-tint);
  border-color: transparent;
  transform: scale(1);
}
/* Pending: never fills. Dashed ring says "not yet" without a claim. */
.score-mark--pending,
.score-mark--pending.is-scored {
  color: transparent;
  background: transparent;
  border-style: dashed;
  border-color: var(--gold);
}
.score-body { display: flex; flex-direction: column; gap: 2px; }
.score-name {
  font-family: var(--font-serif);
  font-weight: 500;
  font-size: var(--mid-serif-4);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.score-anchor {
  font-size: var(--size-mono-meta);
  color: var(--muted);
  text-decoration: underline dotted;
  text-underline-offset: 3px;
  width: fit-content;
}
.score-anchor::after { content: " ↑"; }
.score-item--pending .score-anchor::after { content: " ↓"; }
.score-anchor:hover { color: var(--forest); }
.symmetry-close {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: var(--size-submoment);
  color: var(--ink);
  margin-top: var(--space-4);
  max-width: 40ch;
}
/* Screen-reader-only text (the pending row's "not yet shipped"). */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* 03 Deletion */
.timer-card {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  background: var(--card-white);
  border-radius: var(--radius-hero-card);
  padding: var(--space-2);
  box-shadow: var(--shadow-sm);
  margin-top: var(--space-2);
}
.timer-clock { font-size: var(--size-clock); }
/* "Auto-deletes in" is a single unit at every width. Left to wrap, it breaks
   at its own hyphen into "Auto- / deletes / in", which reads as a defect. */
.timer-label { white-space: nowrap; }
.mechanism-line {
  color: var(--muted);
  font-size: var(--size-caption);
  margin-top: var(--space-1);
  /* The caption belongs to the pill above it, not to the body below. Without
     this it sat flush on the next paragraph and the hierarchy collapsed. */
  margin-bottom: var(--space-3);
}
.close-line {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: var(--size-submoment);
  margin-top: var(--space-3);
}

/* ---------- Middle act (04–06): paper-as-surface, zero shadows ---------- */
/* Middle-act headline: exactly one serif size (46px), centered where used. */
.mid-h2 {
  font-size: var(--mid-serif-1);
  font-weight: 500;
  line-height: 1.18;
  letter-spacing: -0.01em;
}
#keep-moment > .mid-h2 { text-align: center; }

/* 04 keep moment — centered single column, 560px stack */
.keep-stack {
  max-width: 560px;
  margin: 56px auto 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.keep-user-bubble {
  align-self: flex-end;
  background: var(--forest-tint);
  border-radius: 16px 16px 4px 16px;
  padding: 14px 19px;
  font-size: var(--mid-sans-1);
  color: var(--ink);
  max-width: 420px;
  opacity: 0.8;
}
.keep-card {
  background: var(--forest-tint);
  border: 1px solid var(--ink-border);
  border-radius: 16px;
  padding: 22px 24px;
  transition: opacity var(--dur-dissolve) var(--ease), filter var(--dur-dissolve) var(--ease);
}
.keep-card.dissolving { opacity: 0; filter: blur(2px); }
.keep-prompt { font-family: var(--font-serif); font-size: var(--mid-serif-5); line-height: 1.45; }
.keep-buttons { display: flex; align-items: center; gap: 10px; margin-top: 16px; }
.keep-yes { border-radius: var(--radius-chip); padding: 12px 22px; border: none; cursor: pointer; }
.keep-no {
  border: none;
  background: transparent;
  color: var(--muted);
  font-family: var(--font-sans);
  font-size: var(--mid-sans-1);
  font-weight: 600;
  padding: 8px 10px;
  cursor: pointer;
}
.keep-skipped-caption { text-align: center; font-size: var(--mid-sans-2); color: var(--muted); }

/* The consent result — a mini .kb-doc, narrower than the page card so it
   reads as a single kept plan rather than the whole book. Deep bottom padding
   gives the stamp its room, the way travel's does. */
.keep-page {
  --kb-pad: 48px 44px 76px;
  --kb-rule: 42px;
  --kb-rule-end: 43px;
  --kb-title: var(--mid-serif-2);
  --kb-fact: var(--mid-serif-4);
  --kb-lead: 42px;
  --kb-facts-top: 30px;
  max-width: 620px;
  margin: 72px auto 0;
}
/* Reserved height for the kept line typewriting in. */
.keep-page-facts { min-height: 126px; }
.keep-typed-row { display: flex; align-items: center; }
.keep-caret { height: 25px; transform: none; }
.kept-check {
  display: inline-flex;
  margin-left: 10px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--amber-tint);
  color: var(--amber-ink);
  font-family: var(--font-sans);
  font-size: var(--mid-sans-2);
  font-weight: 700;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}

/* (04b note-transform removed in the July 17 advocate restructure — it argued
   to the consumer register and moved to asksafely.ai.) */

/* 05 The page — intro on cream, then a full-bleed ruled portrait band */
#the-page { max-width: none; padding: 0; }
.page-intro {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--section-rhythm) var(--content-gutter) 72px;
}
.page-intro p {
  font-family: var(--font-serif);
  font-size: var(--size-subhead-body);
  line-height: 1.7;
  max-width: 62ch;
  margin-top: 28px;
}
/* The closing beat under the portrait band. No bottom padding — the next
   section's rhythm supplies the gap, so the Phase 4 measurements hold. */
.page-close {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--space-4) var(--content-gutter) 0;
}
.page-close .close-line { max-width: 46ch; }
/* The page with your name on it — the largest .kb-doc instance. Was a
   full-bleed ruled band; now a contained canonical card, so it reads as the
   same object as the hero page, the consent result, and the anti-card it is
   the diptych partner to. Flat, and unstamped: the stamp budget is three and
   travel/consent/anti-card hold them. */
.portrait-wrap {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 var(--content-gutter);
}
.portrait-doc {
  --kb-pad: 64px 56px 56px;
  --kb-rule: 52px;
  --kb-rule-end: 53px;
  --kb-title: var(--mid-serif-2);
  --kb-rule-w: 48px;
  --kb-rule-h: 3px;
  --kb-rule-gap: 14px;
  --kb-head-align: flex-start;
  --kb-head-wrap: wrap;
  --kb-head-gap: 24px;
}
.portrait-facts { margin-top: 56px; max-width: 960px; }
.portrait-fact {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  font-family: var(--font-serif);
  font-size: var(--mid-serif-3);
  line-height: 52px;
  color: var(--ink);
  transition: opacity 450ms var(--ease), transform 450ms var(--ease);
}
/* Each fact lands, then its provenance tag arrives a beat later (main.js).
   Defaults are the visible end-state for reduced motion / no-JS. */
.portrait-fact .prov { font-size: var(--size-mono-meta); color: var(--muted); white-space: nowrap; transition: opacity 450ms var(--ease); }
/* Editable fact (designed-behavior demo, keep-moment style) — the last fact
   is tappable; a brief edit state, then it updates with a quiet confirmation. */
.portrait-fact-edit {
  font-family: var(--font-serif);
  font-size: var(--mid-serif-3);
  line-height: 52px;
  color: var(--ink);
  background: none;
  border: none;
  padding: 0 2px;
  margin: 0;
  text-align: left;
  cursor: pointer;
  border-bottom: 1px dotted rgba(7, 25, 9, 0.35);
  transition: background 200ms var(--ease);
}
.portrait-fact-edit:hover { color: var(--forest); }
.portrait-fact-edit.editing { background: var(--amber-tint); border-radius: 4px; border-bottom-color: transparent; }
.edit-fact-tail { display: inline-flex; align-items: baseline; gap: 10px; white-space: nowrap; }
.edit-fact-badge {
  font-family: var(--font-mono);
  font-size: var(--size-mono-meta);
  color: var(--amber-ink);
  background: var(--amber-tint);
  border-radius: 6px;
  padding: 2px 7px;
}
.portrait-demo-note { font-family: var(--font-sans); font-size: var(--size-caption); color: var(--muted); margin-top: var(--space-3); }

/* 08 Saturday demo — asymmetric 340px/1fr split */
.demo-subline {
  text-align: center;
  font-size: var(--mid-sans-1);
  color: var(--muted);
  margin-top: 12px;
}
/* The second caption follows the framing line, so it sits closer. */
.demo-subline--tight { margin-top: 4px; }
.demo-split {
  display: grid;
  grid-template-columns: 340px 1fr;
  gap: 72px;
  align-items: start;
  margin-top: 52px;
}
/* The 14px rhythm lives on the bubbles rather than as a rail `gap`, so a
   cleared bubble can collapse its own spacing along with its height. A flex
   gap belongs to the container and can't be transitioned away per item. */
.demo-rail { display: flex; flex-direction: column; align-items: flex-start; }
.demo-bubble {
  background: var(--forest-tint);
  border-radius: 16px 16px 16px 4px;
  padding: 13px 18px;
  font-size: var(--mid-sans-1);
  color: var(--ink);
  max-width: 320px;
  margin-bottom: 14px;
}
.demo-annotation { font-size: var(--mid-mono); color: var(--muted); }
.demo-rail-foot {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-top: 4px;
  flex-wrap: wrap;
}
/* Quiet replay affordance, mono label style. Sits beside the caption so the
   control is where the thing it replays just happened. */
.demo-replay {
  font-family: var(--font-mono);
  font-size: var(--mid-mono);
  color: var(--muted);
  background: none;
  border: 0;
  /* Padding buys a finger-sized hit area on an 11px label; the negative
     margin gives the space back so the label still sits on the caption's
     baseline. The underline follows the text, not the padding box. */
  padding: 9px 6px;
  margin: -9px -6px;
  cursor: pointer;
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}
.demo-replay:hover { color: var(--forest); }
.demo-replay[disabled] { opacity: 0.35; cursor: default; text-decoration: none; }

/* ---- The receipts sequence (beat 08) -------------------------------------
   Rebuilt Jul 30. The old version dissolved both scaffolds at once, 900ms
   after the section hit 45% visible, and drew the marks at 1700ms: the whole
   story was over in under two seconds, before a scrolling reader arrived. So
   they met one bubble, a caption about bubbles they never saw, and the two
   cleared bubbles' reserved space reading as a void.

   Now: staggered entry, a two-second hold, then the scaffolds clear one after
   the other (1100ms each) while the caption fades in, then the answer and its
   receipt marks arrive. ~6.8s total. See initSaturdayDemo() for the schedule.

   .demo-seq is added by main.js ONLY when motion is enabled, so the base
   styles above stay the no-JS and reduced-motion end state: everything
   visible. Every state here is opacity/filter, never display or height, so
   all three bubbles hold their space and nothing reflows as they clear. */
.demo-seq .demo-annotation,
.demo-seq .demo-answer-body,
.demo-seq .demo-receipts,
.demo-seq .demo-replay { opacity: 0; }
/* One transition declaration for the bubbles, with the duration read from a
   custom property, so a state class only has to change the value: entry is
   quick, clearing is slow, and neither has to restate the whole shorthand. */
.demo-seq .demo-bubble {
  opacity: 0;
  --bubble-dur: var(--dur-bubble-in);
  transition: opacity var(--bubble-dur) var(--ease),
              filter var(--bubble-dur) var(--ease),
              max-height var(--dur-collapse) var(--ease),
              padding-top var(--dur-collapse) var(--ease),
              padding-bottom var(--dur-collapse) var(--ease),
              margin-bottom var(--dur-collapse) var(--ease);
}
/* The space closes only after both cleared bubbles are fully invisible: held
   open through the clearing so nothing jumps mid-story, then released so the
   end state isn't the question stranded under 150px of nothing. main.js sets
   the starting max-height inline, since a transition can't start from none. */
.demo-seq .demo-bubble.is-collapsed {
  max-height: 0;
  /* The padding has to go too. With border-box, max-height: 0 still can't take
     an element below its own padding, which left each cleared bubble 26px tall
     and only half the space recovered. min-height: 0 for the same reason on a
     flex item, whose automatic minimum size would otherwise hold it open. */
  padding-top: 0;
  padding-bottom: 0;
  min-height: 0;
  margin-bottom: 0;
  overflow: hidden;
}
/* The replay control appears once the sequence has finished, which is the
   first moment it does anything. Added once and kept: on later replays it
   stays put and just dims while disabled, rather than vanishing under the
   finger that pressed it. */
.demo-seq .demo-replay.is-in { opacity: 1; transition: opacity var(--dur-answer-in) var(--ease); }
.demo-seq .demo-bubble.is-in { opacity: 1; }
.demo-seq .demo-annotation.is-in { opacity: 1; transition: opacity var(--dur-caption-in) var(--ease); }
.demo-seq .demo-answer-body.is-in,
.demo-seq .demo-receipts.is-in { opacity: 1; transition: opacity var(--dur-answer-in) var(--ease); }
/* After .is-in so it wins on equal specificity. main.js also drops .is-in. */
.demo-seq .demo-bubble.is-cleared {
  --bubble-dur: var(--dur-bubble-out);
  opacity: 0;
  filter: blur(2px);
}
.demo-answer { --kb-panel-pad: 44px 52px 40px; }
.demo-answer .kb-panel__head { margin-bottom: 20px; }
.demo-orb { width: 8px; height: 8px; border-radius: 50%; background: var(--lavender); }
.demo-answer-body { font-family: var(--font-serif); font-size: var(--mid-serif-4); line-height: 1.65; --kb-mark: 100%; }
/* Recall marks: lavender underline = Knowledgebook fact; ink = from chat.
   The underline is a background gradient whose width (--kb-mark) draws
   left-to-right once the scaffolding bubbles clear. Default 100% is the
   reduced-motion / no-JS state; main.js starts it at 0% when motion is ok. */
.recall {
  padding: 0 2px 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: var(--kb-mark, 100%) 1.5px;
  transition: background-size 300ms var(--ease);
}
.recall:nth-of-type(2) { transition-delay: 150ms; }
.recall-kb { background-image: linear-gradient(var(--lavender), var(--lavender)); }
/* Traced: the fact a just-opened receipt sourced. Its mark thickens and the
   lavender lifts to full strength — the answer pointing back at its source. */
.recall.is-traced {
  background-size: var(--kb-mark, 100%) 3px;
  background-color: rgba(199, 173, 212, 0.18);
  transition: background-size 260ms var(--ease-snap), background-color 260ms var(--ease);
}
.recall-chat { background-image: linear-gradient(rgba(7, 25, 9, 0.24), rgba(7, 25, 9, 0.24)); }
.demo-receipts { display: flex; gap: 10px; margin-top: 24px; flex-wrap: wrap; }
.receipt-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-sans);
  font-size: var(--mid-sans-2);
  font-weight: 600;
  color: var(--forest);
  /* Card-white on the muted panel, like the travel panel's chips. The old
     forest-tint would have gone invisible once the answer became a panel. */
  background: var(--card-white);
  border: none;
  border-radius: var(--radius-chip);
  padding: 7px 13px;
  cursor: pointer;
  white-space: nowrap;
}
/* Receipts open INSTANTLY — no transition, by design (DESIGN_SYSTEM.md).
   The inset is paper quoted inside the device: explicit card-white so it
   keeps reading as the source page now that the answer around it is a panel.
   That is the grammar doing the work — tapping a receipt hands you paper. */
.receipt-inset {
  margin-top: 14px;
  background-color: var(--card-white);
  background-image: repeating-linear-gradient(transparent 0 38px, var(--ruled-line) 38px 39px);
  border: 1px solid var(--ink-border);
  border-radius: var(--radius-paper);
  padding: 18px 22px;
}
.receipt-line { font-family: var(--font-serif); font-size: var(--mid-serif-5); line-height: 38px; color: var(--ink); }
.receipt-prov { font-size: var(--mid-mono); color: var(--amber-ink); margin-top: 8px; }
@media (max-width: 900px) {
  .demo-split { grid-template-columns: 1fr; gap: 40px; }
}

/* The chorus breath */
.chorus-breath {
  padding-top: var(--chorus-breath);
  padding-bottom: var(--chorus-breath);
  text-align: center;
}
.chorus-line {
  font-style: italic;
  font-size: var(--size-submoment);
}
/* The chorus enacts itself: "The chat clears." fades to 45% on scroll;
   "The Knowledgebook doesn't." holds. */
#chorus-fade { transition: opacity 900ms var(--ease) 300ms; }
#chorus-fade.faded { opacity: 0.45; }

/* ---------- 09 Travel — on cream (the dark act is dissolved; #fork is the
   only forest band). Copy and trace card recolored for the cream field. ---------- */
#travel {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 72px;
  align-items: center;
  padding-top: var(--space-6);
  padding-bottom: var(--space-6);
}
#travel h2 { color: var(--ink); font-size: var(--size-section); font-weight: 500; line-height: 1.18; letter-spacing: -0.01em; }
.travel-copy p {
  font-family: var(--font-serif);
  font-size: var(--size-body-sm);
  line-height: 1.7;
  color: var(--ink);
  max-width: 52ch;
  margin-top: var(--space-3);
}
/* Legible gold pill on the forest field. */
.tag-amber {
  display: inline-block;
  color: var(--gold);
  background: rgba(209, 160, 55, 0.14);
  border: 1px solid rgba(209, 160, 55, 0.35);
  font-size: var(--size-mono-meta);
  font-weight: 600;
  padding: 3px 11px;
  border-radius: 20px;
  margin-top: 26px;
}
/* Right column: the traveling page, with its trace record beneath. */
.travel-visual { justify-self: end; width: 100%; max-width: 440px; }
/* The traveling page — paper leaving home, tilted and passport-stamped. THE
   REFERENCE INSTANCE: the component was extracted from this card, so these
   are the component's defaults and this rule only names what's specific —
   the deep bottom padding that gives the stamp room under the last line. */
.traveling-page {
  --kb-pad: 26px 30px 78px;
  --kb-rule-top: 74px;
}
/* Trace card — the travel record beneath the page. The first .kb-panel: the
   assistant as a muted device, distinct from the warm paper above it. */
.trace-card {
  margin: 26px 0 0 18px;
  max-width: 400px;
}
.trace-meter { text-align: right; line-height: 1; }
.trace-meter-num { display: block; font-size: var(--mid-serif-3); font-weight: 500; color: var(--ink); }
.trace-meter-label { font-family: var(--font-sans); font-size: var(--size-mono-meta-sm); font-weight: 600; letter-spacing: 0.13em; color: var(--muted); }
.trace-divider { height: 1px; background: rgba(7, 25, 9, 0.12); margin: 14px 0; }
.trace-groups { display: flex; flex-direction: column; gap: 11px; }
.trace-group { display: flex; flex-direction: column; gap: 6px; }
.trace-group-label { font-family: var(--font-sans); font-size: var(--size-mono-meta-sm); font-weight: 700; letter-spacing: 0.13em; color: var(--muted); }
.trace-chip { align-self: flex-start; font-family: var(--font-sans); font-size: var(--mid-sans-2); color: var(--ink); background: var(--card-white); border: 1px solid var(--line); border-radius: 10px; padding: 7px 11px; }
.trace-foot { font-family: var(--font-sans); font-size: var(--size-caption-sm); color: var(--muted); margin-top: 14px; }
@media (max-width: 900px) {
  #travel { grid-template-columns: 1fr; gap: 56px; }
  .travel-visual { justify-self: start; }
  #indictment { grid-template-columns: 1fr; gap: 44px; }
  .indict-visual { justify-self: start; max-width: 100%; }
}

/* ---------- 04 The fork — the only forest band, full-height structural
   center. Centered typographic peak; gold exit line opens the architecture. */
#fork {
  max-width: none;
  background: var(--forest);
  color: var(--cream-on-forest);
  min-height: 100vh;
  display: flex;
  align-items: center;
  text-align: center;
  padding: var(--space-6) var(--content-gutter);
}
.fork-inner { max-width: 880px; margin: 0 auto; }
#fork h2 {
  font-weight: 500;
  font-size: var(--size-fork);
  line-height: 1.18;
  letter-spacing: -0.015em;
  color: var(--cream);
  margin: 0;
}
#fork em { font-style: italic; display: inline-block; margin-top: 26px; }
#fork p {
  font-size: var(--size-body-lg);
  line-height: 1.8;
  color: var(--cream-on-forest);
  max-width: 58ch;
  margin: 48px auto 0;
}
.fork-exit {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: var(--size-submoment);
  color: var(--gold) !important;
  max-width: none !important;
  margin-top: 40px !important;
}

/* ---------- 11 Founder note — on cream, near the footer. Centered
   signature block, name set right. */
#founder-note {
  padding-top: var(--space-4);
  padding-bottom: var(--space-6);
  text-align: center;
}
.founder-inner { max-width: 640px; margin: 0 auto; }
.founder-rule { display: inline-block; width: 64px; height: 1px; background: rgba(7, 25, 9, 0.18); }
.founder-body {
  font-family: var(--font-serif);
  font-size: var(--size-subhead-body);
  line-height: 1.8;
  color: var(--ink);
  margin-top: 44px;
  text-align: left;
}
/* Founder signature lockup — small author photo + name/title, centered as a
   unit. A byline plate, not a portrait: 52px, hairline ring, no shadow. */
.founder-sig {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-top: var(--space-3);
  text-align: left;
}
.founder-avatar {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--ink-border);
  flex: 0 0 auto;
}
.founder-sig-name {
  display: flex;
  flex-direction: column;
  font-family: var(--font-sans);
  font-size: var(--size-caption);
  color: var(--ink);
  line-height: 1.35;
}
.founder-sig-title { font-size: var(--size-caption-sm); color: var(--muted); }
.founder-sig-link { color: inherit; text-decoration: none; }
.founder-sig-link:hover { text-decoration: underline dotted; text-underline-offset: 3px; }

/* 10 Why now — conviction. Reading block on cream. */
#why-now { padding-top: var(--space-6); padding-bottom: var(--space-6); }
.why-now-body {
  font-family: var(--font-serif);
  font-size: var(--mid-serif-4);
  line-height: 1.7;
  color: var(--ink);
  max-width: 60ch;
}

/* 12 Honest-scope strip — three mono lines on a paper card. */
#scope-strip { padding-top: var(--space-4); padding-bottom: var(--space-4); }
.scope-card {
  max-width: 760px;
  margin: 0 auto;
  background: var(--card-white);
  border: 1px solid var(--ink-border);
  border-radius: var(--radius-paper);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.scope-line {
  font-family: var(--font-mono);
  font-size: var(--size-caption);
  line-height: 1.6;
  color: var(--muted);
}
.scope-label { color: var(--ink); font-weight: 600; }

/* 13 Honest close */
#honest-close { text-align: center; }
.close-italic {
  font-style: italic;
  font-size: var(--size-page-title-sm);
}
.honesty-line {
  color: var(--muted);
  font-size: var(--size-caption);
  margin-top: var(--space-1);
}
#honest-close h2 { font-size: var(--size-section); margin-top: var(--space-3); }
.incentive-line {
  color: var(--muted);
  font-size: var(--size-caption);
  margin-top: var(--space-1);
}
#close-cta { margin-top: var(--space-3); }

/* Blog cards — Wave 6 paper-as-surface. NO tape/perforation here. */
.blog-card-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.blog-card-grid--two { grid-template-columns: 1fr 1fr; }
@media (max-width: 900px) {
  .blog-card-grid, .blog-card-grid--two { grid-template-columns: 1fr; }
}
/* Featured manifesto card — full-width, promoted above the two-up grid. */
.blog-featured {
  display: block;
  background: var(--card-white);
  border: 1px solid var(--ink-border);
  border-radius: var(--radius-paper);
  padding: var(--space-4);
  margin-top: var(--space-3);
  text-decoration: none;
  color: var(--ink);
  transition: border-color 150ms var(--ease);
}
.blog-featured:hover { border-color: var(--ink); }
.blog-featured .eyebrow { margin-bottom: var(--space-1); }
.blog-featured h4 { font-family: var(--font-serif); font-weight: 500; font-size: var(--size-page-title); letter-spacing: -0.01em; margin-top: 6px; }
.blog-featured:hover h4 { text-decoration: underline dotted var(--muted); text-underline-offset: 4px; }
.blog-featured p { color: var(--muted); font-size: var(--size-ui); line-height: 1.55; margin-top: var(--space-1); max-width: 60ch; }
.blog-featured-read { display: inline-block; color: var(--forest); font-size: var(--size-mono-meta); margin-top: var(--space-2); }
.blog-card {
  display: block;
  background: var(--card-white);
  border: 1px solid var(--ink-border);
  border-radius: var(--radius-paper);
  padding: var(--space-3);
  text-decoration: none;
  color: var(--ink);
  transition: border-color 150ms var(--ease);
}
.blog-card:hover { border-color: var(--ink); }
.blog-card:hover h4 { text-decoration: underline dotted var(--muted); text-underline-offset: 4px; }
.blog-byline { color: var(--muted); }
.blog-card h4 { font-size: var(--size-page-title-sm); margin-top: var(--space-1); }
.blog-card p { color: var(--muted); font-size: var(--size-caption); margin-top: 6px; }

footer {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--space-3) var(--content-gutter) var(--space-5);
  color: var(--muted);
  font-size: var(--size-caption);
}
footer a { color: var(--muted); }

/* ---------- Responsive: hero collapses below 1280px ---------- */
@media (max-width: 1280px) {
  .hero-grid {
    /* minmax(0,1fr), not 1fr: once the composition is in normal flow on
       phones, a plain 1fr track grows to its widest child's min-content and
       drags the H1 past the viewport. minmax(0,…) caps the track. */
    grid-template-columns: minmax(0, 1fr);
    min-height: auto;
  }
  .hero-copy { max-width: 640px; min-width: 0; }
}
@media (max-width: 640px) {
  #hero h1 { font-size: var(--size-hero-phone); }
  #problem h2, #travel h2, #honest-close h2, .mid-h2 {
    font-size: var(--size-section-phone);
  }
  #fork h2 { font-size: var(--size-fork-phone); }
  .indict-line { font-size: var(--size-indict-phone); }
  .why-now-body { font-size: var(--size-subhead-body); }

  /* Nav: 48px gutters are too wide on phones — they crushed the CTA and
     collided the wordmark with the actions. Tighten gutters, gaps, and the
     CTA so brand + Blog + CTA sit comfortably on one row. */
  .nav-inner, .read-nav-inner { padding-left: var(--space-2); padding-right: var(--space-2); }
  .nav-brand { gap: 8px; }
  .nav-wordmark { letter-spacing: 0.08em; }
  .nav-actions { gap: var(--space-2); }
  #nav-cta { padding: 10px 16px; font-size: var(--size-caption); }

  /* Space audit (Jul 30): phones were inheriting the full desktop rhythm —
     150px of padding top AND bottom meant ~300px of dead air between every
     section (36% of an 844px screen), and 48px gutters ate a quarter of the
     width. The gaps were framing nothing. Retune the tokens for small screens
     and every gap on the page follows, since all spacing derives from them. */
  :root {
    --section-rhythm: 88px;
    --content-gutter: 24px;
    --chorus-breath: 104px;
  }
  /* The full-bleed portrait band and the page intro carry their own values. */
  /* The page card keeps as much measure as a contained card can give: a
     tighter outer gutter and tighter card padding, so converting the old
     full-bleed band into a card costs the facts as little width as possible
     (they must still hold at two lines — see the fact sizing below). */
  .portrait-wrap { padding: 0 var(--space-2); }
  .page-intro { padding-bottom: var(--space-4); }
  #fork { min-height: 0; padding-top: var(--space-6); padding-bottom: var(--space-6); }
  /* These four opt out of --section-rhythm with their own space tokens, so they
     need retuning too — otherwise they stay at desktop scale on a phone. */
  #problem, #indictment, #symmetry, #why-now, #travel {
    padding-top: var(--space-5);
    padding-bottom: var(--space-5);
  }

  /* Hero composition — the desktop layering (timer card + sibling tabs
     floating over the page) collides on phones. Linearize it: stack the
     chat bubble + timer, drop the decorative tabs, and let the page flow
     beneath instead of being absolutely positioned over everything. */
  .hero-page-composition { height: auto; margin-top: var(--space-3); }
  .hero-chat-cluster { position: static; }
  .hero-timer-card { flex-wrap: wrap; }
  .hero-tabs { display: none; }
  .hero-page { position: static; left: auto; right: auto; margin-top: var(--space-2); }
  /* Tighter card padding on phones; let the fact-count wrap under the title
     instead of clipping off the right edge. */
  .hero-page-body { padding: 24px 24px 28px; }
  .hero-page-head { flex-wrap: wrap; }

  /* ---- Phone steps for the display serif ----------------------------------
     The 30px submoment size had no phone step, so these two closing lines
     inherited it and ran to five and three lines at 390px. The chorus keeps
     30px on purpose: it's the refrain, and it already sits on two clean
     lines, so stepping it down would cost presence and buy nothing. */
  .close-line, .symmetry-close { font-size: var(--size-submoment-phone); }

  /* The four failures. 20ch is a desktop measure; on a phone it capped the
     column at 260px, left 82px of the content width unused, and wrapped
     three of the four onto two lines, which breaks the list's rhythm. Full
     width plus the phone step above puts each failure back on one line. */
  .indict-lines { max-width: none; }

  /* ---- About-you card: a page of entries, not a stack of headlines -------
     26px on 52px leading is the desktop measure. At 390px the first fact ran
     to four lines and the card passed 670px tall. Step the serif down, tighten
     the leading, and stack each fact's provenance directly under it: the
     two-column row parked "from chat · May 2" beside line one of a four-line
     fact, detached from the fact it belonged to. Desktop keeps the row.
     The ruled band is drawn on a 53px period to match the desktop 52px
     leading, one fact per rule. That period belongs to the same desktop
     layout, so the phone rhythm gets its own: 30px rules on 30px leading,
     each entry filling consecutive ruled rows with one blank rule between
     entries. Left at 53px the rules cut through the smaller text, and that
     grid is also the group spacing: nothing extra inside an entry, a whole
     empty rule between them. */
  .portrait-doc {
    --kb-pad: var(--space-4) 20px var(--space-4);
    --kb-rule: 29px;
    --kb-rule-end: 30px;
  }
  .portrait-facts { display: flex; flex-direction: column; gap: 30px; }
  .portrait-fact {
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    font-size: var(--size-portrait-fact-phone);
    line-height: 30px; /* a length, so .prov inherits the row too */
  }
  .portrait-fact-edit {
    font-size: var(--size-portrait-fact-phone);
    line-height: 30px;
  }

  /* ---- Both countdowns ---------------------------------------------------
     The deletion label was inheriting the section's 19px body size inside a
     nowrap flex row, so it hyphen-broke to "Auto- / deletes / in" next to
     28px digits. Label at label size, digits on a clamp, and the row free to
     wrap so "· you set the timer." can drop to its own line whole. */
  .timer-card { flex-wrap: wrap; }
  .timer-label { font-size: var(--size-caption-sm); }
  .timer-clock { font-size: var(--size-clock-phone); }

  /* ---- Travel card head -------------------------------------------------
     space-between plus a nowrap fact-count squeezed the title to 108px and
     broke "About you" into "About / you". */
  .traveling-page-head { flex-wrap: wrap; }
  .traveling-page-title { white-space: nowrap; }
}

/* ---------- Reduced motion: everything static, everything legible ---------- */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
  /* main.js must also render all typewriter lines whole and the note at
     full opacity under this condition — see DESIGN_SYSTEM.md. */

  /* Beat 08 static end state: the question that stands, the caption, and the
     answer with its receipts. The two scaffold bubbles leave the flow rather
     than sitting at opacity 0, because with no sequence to explain it their
     reserved space would read as the same void the rebuild removed. */
  #saturday-demo .demo-scaffold { display: none; }
}
