/* assets/styles.css
 * ========================================================================
 * Phase 1 Foundation — scaffold styles.
 * FOUND-02: @font-face for Cormorant Garamond; FOUND-03: Georgia fallback
 * with capsize-measured overrides; FOUND-04: @property for the three
 * animated colour tokens; FOUND-12: palette-aware focus ring + skip link.
 *
 * Phase 3 extends this file with @keyframes palette-journey + wordmark
 * tinting + color-scheme: only light at :root (IDENT-07). Phase 4 adds
 * prose typography (line-length, leading, body rhythm).
 *
 * Formatter note: `npm run format` currently runs Prettier with default
 * options (Plan 06 will ship `.prettierrc` with `singleQuote: true`).
 * Running `prettier --write` on this file BEFORE Plan 06 converts single
 * quotes to double quotes and lowercases hex literals — leaving the CSS
 * functionally identical but breaking Plan 05's literal-string acceptance
 * greps. Leave the file unformatted until Plan 06's `.prettierrc` lands.
 * ========================================================================
 */

/* -- 1. Font declarations (FOUND-02 + FOUND-03) ------------------------ */

@font-face {
  font-family: 'Cormorant Garamond';
  src: url('/assets/fonts/cormorant-garamond-latin-wght-normal.woff2') format('woff2-variations');
  font-weight: 300 700;      /* variable wght axis per fontsource */
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC,
                 U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IDENT-01: italic axis WOFF2 for the bespoke wordmark. Same family
 * name as the normal-axis face above; the browser picks this face
 * when font-style: italic resolves on a descendant. Copied from
 * node_modules/@fontsource-variable/cormorant-garamond/files/ so the
 * file is served from /assets/fonts/ (FOUND-02 self-hosted invariant). */
@font-face {
  font-family: 'Cormorant Garamond';
  src: url('/assets/fonts/cormorant-garamond-latin-wght-italic.woff2') format('woff2-variations');
  font-weight: 300 700;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC,
                 U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ======================================================================
 * FOUND-03: Georgia fallback metrics derived by @capsizecss/core 4.1.3.
 * Measured: 2026-04-24 via `node tools/measure-fallback.mjs`
 * Source:   assets/fonts/cormorant-garamond-latin-wght-normal.woff2
 * Regenerate after `npm update` of @fontsource-variable/cormorant-garamond.
 * ====================================================================== */
@font-face {
  font-family: 'Cormorant Garamond Fallback';
  src: local('Georgia');
  ascent-override: 105.0816%;
  descent-override: 32.639%;
  line-gap-override: 0%;
  size-adjust: 87.9317%;
}

/* -- 2. @property registrations for animated tokens (FOUND-04) --------- */
/* Per ARCHITECTURE.md Anti-Pattern 3: without @property, custom-property
 * animation is discrete (jump-cut). Register syntax: '<color>' to enable
 * smooth interpolation in Phase 3's palette journey. Only the three
 * explicitly-animated tokens are registered (Claude's Discretion in
 * CONTEXT.md).
 */

@property --ink {
  syntax: '<color>';
  inherits: true;
  initial-value: #0F2B2B;       /* Deep Forest — D-04 brand anchor */
}
@property --canvas-bg {
  syntax: '<color>';
  inherits: true;
  initial-value: #F5F0E8;       /* Cream */
}
@property --accent {
  syntax: '<color>';
  inherits: true;
  initial-value: #C9A84C;       /* Warm Gold */
}

/* -- 3. Brand palette (non-animated custom properties) ----------------- */
/* Full palette. Individual colours referenced directly when a token is
 * NOT part of the palette journey. Phase 3 may animate more of these via
 * additional @property declarations if the journey expands.
 */

:root {
  --deep-forest: #0F2B2B;
  --teal:        #1A4A4A;
  --sage:        #486F60;
  --soft-sage:   #A8C5B8;
  --warm-gold:   #C9A84C;
  --cream:       #F5F0E8;
  --off-white:   #FAFAF7;
  --light-teal:  #E8F0EE;

  /* IDENT-07: prevent OS dark-mode reinterpretation of the palette
   * journey. Complements <meta name="color-scheme" content="light">
   * in index.html (line 7); IDENT-07 wording asks for the CSS form
   * specifically — this declaration is the more authoritative second
   * hand-off and is what the requirement specifies as the contract. */
  color-scheme: only light;
}

/* -- 4. Reset + base --------------------------------------------------- */

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

html, body {
  margin: 0;
  padding: 0;
  background: var(--canvas-bg);
  color: var(--ink);
}

body {
  /* Capsize-derived stack — fallback metrics tuned to Cormorant. */
  font-family: 'Cormorant Garamond', 'Cormorant Garamond Fallback', Georgia, serif;
  font-size: 1.25rem;          /* 20px — Cormorant reads small at 1.125rem; bumped for readability */
  line-height: 1.65;           /* within the 1.6–1.7 range from A11Y-04 */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6 {
  font-family: 'Cormorant Garamond', 'Cormorant Garamond Fallback', Georgia, serif;
  font-weight: 500;
  margin: 0;
}

/* -- 5. Skip link (FOUND-12) ------------------------------------------- */
/* Visually hidden until :focus-visible. On reveal: palette-aware — Cream
 * background, Deep Forest text, Warm Gold focus ring. Matches the first
 * palette stage so it never clashes with the dawn/forest band.
 */

.skip-link {
  position: absolute;
  top: 0;
  left: 0;
  padding: 0.5rem 1rem;
  background: var(--cream);
  color: var(--deep-forest);
  font-weight: 600;
  text-decoration: underline;
  clip-path: inset(50%);
  overflow: hidden;
  z-index: 1000;
}
.skip-link:focus-visible,
.skip-link:focus {
  clip-path: none;
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* -- 6. Focus ring (FOUND-12) ------------------------------------------ */
/* Palette-aware via var(--accent); re-tints automatically as the palette
 * journey advances in Phase 3. Fallback :focus for UAs without
 * :focus-visible (unlikely in 2026 but harmless).
 */

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
:focus:not(:focus-visible) {
  outline: none;
}

/* -- 7. Flow-field canvas wrapper (D-09) ------------------------------- */
/* Full-viewport fixed; renders ABOVE section backgrounds so the lines are
 * visible against §11's opaque placeholder palette bands (and, in Phase 3,
 * the live palette journey). pointer-events: none keeps clicks/scroll
 * untouched. Phase 2 attaches the rAF render loop to <canvas id="flow-field">
 * via assets/flow-field.js. <noscript> (in index.html) sets display:none
 * for no-JS visitors.
 */

.flow-field-wrap {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
}
#flow-field {
  display: block;
  width: 100%;
  height: 100%;
}

/* -- 7a. Film-grain overlay (post-Phase-5 polish) -------------------- */
/* Fixed-position SVG noise tile, repeating, sat on top of everything at
 * very low opacity. Adds a subtle paper/film texture without competing
 * with type or canvas — invisible at first glance but tactile on close
 * inspection. pointer-events: none so it never blocks interaction.
 * Skip on reduced-motion not because grain itself moves (it doesn't)
 * but because some visitors find any extra visual texture distracting;
 * the @media query honours that preference. */
.grain-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  background-image: url('/assets/grain.svg');
  background-repeat: repeat;
  opacity: 0.04;
  mix-blend-mode: multiply;
}
@media (prefers-reduced-motion: reduce) {
  .grain-overlay { display: none; }
  /* RHY-06: hide scroll-progress line under reduced motion — the line's
   * purpose is animation; pinning at full fill would be decorative without
   * function. See §16 for the line's full rule set. */
  .scroll-progress { display: none; }
}
html[data-motion="reduced"] .grain-overlay { display: none; }
html[data-motion="reduced"] .scroll-progress { display: none; }

/* -- 8. Layout ---------------------------------------------------------- */

header,
footer {
  padding: 1.5rem 2rem;
}

main {
  display: flex;
  flex-direction: column;
}

/* D-08: each section ships with min-height so the scroll driver (Plan 03)
 * has real surface to write --scroll against. Phase 4 replaces the <h2>
 * placeholders with manifesto prose and may adjust padding; the min-height
 * guarantees scroll progress is meaningful even with empty bands. */
main > section {
  min-height: 80vh;
  padding: 4rem 2rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

main > section h2 {
  font-size: clamp(1.75rem, 4vw, 3rem);
}

/* -- 9. Utilities ------------------------------------------------------- */

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

.footer-placeholder {
  /* Phase 4 replaces this with the signed footer + privacy link.
   * (Sibling header-mark placeholder selector retired in Phase 3 IDENT-01.) */
  font-style: italic;
  color: var(--ink);
  margin: 0;
}

.footer-line {
  /* Phase 4 CONT-08 — signed footer line in cream band. Renders below
   * the Section 8 prose with the privacy link + year. The middle-dot
   * separator (U+00B7) provides typographic breathing room without an
   * em-dash's narrative weight. */
  font-style: italic;
  color: var(--ink);
  margin: 0;
  padding: 1.5rem 0;
  text-align: center;
}
.footer-line a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 0.04em;
}
.footer-line a:hover {
  text-decoration-thickness: 0.08em;
}

/* -- 9a. Wordmark (IDENT-01..03) --------------------------------------- */
/* Inline SVG in <header>. currentColor + var(--ink) cascade re-tints the
 * mark automatically as Phase 3's palette journey advances (IDENT-02 —
 * no per-phase asset duplication). Hover/focus scales the dot only —
 * the punctum responds; the arcs stay still (D-01d, D-02b). The parent
 * <a> carries the accessible name (aria-label="Synar.ch — home"); the
 * <svg> is aria-hidden="true" + focusable="false" so screen readers
 * announce the link, not the glyph (IDENT-03). */
/* IDENT-01 (post-Phase-5 second redesign): wordmark stripped to its
 * essence — italic "synar" and "ch" set tight against the gold punctum
 * dot, with a helix-trace exhalation extending rightward into the
 * field. No bracket arcs, no eye, no bloom — the dot IS the wordmark's
 * focal gesture. Text colour follows --ink (palette journey); the dot
 * stays gold as the etymological full-stop and the field's ink-source.
 *
 * First-load: text fades in (0.2-1.0s), dot drops into the punctum
 * from just above (0.5-1.5s), dot settles into a perpetual gentle
 * pulse (1.5s+), helix-trace draws rightward and dissipates (1.8-4.8s).
 *
 * Layout: position fixed top-left, large signature scale, persistent
 * during scroll. Pinned palette tokens via body.privacy-page rule mean
 * the wordmark stays Deep Forest on both privacy/404 pages even though
 * the index page's palette journey would otherwise re-tint it. */
.wordmark {
  position: fixed;
  top: 1.5rem;
  left: 1.75rem;
  z-index: 100;                /* above canvas (z=1), below grain (z=9999) */
  display: inline-block;
  line-height: 0;
  color: var(--ink);
  text-decoration: none;
}
.wordmark-svg {
  width: clamp(7rem, 12vw, 11rem);
  height: auto;
  display: block;
}

/* Wordmark text fades in alongside the dot's drop. */
@keyframes wordmark-text-appear {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.wordmark-text {
  animation: wordmark-text-appear 800ms ease-out 200ms backwards;
}

/* The dot is the wordmark's only kinetic element. It starts ~14 units
 * above the punctum, fades in, and falls into place with a slight
 * cubic-bezier overshoot (the bezier's y2=1.2 dips below the destination
 * before settling — a small bounce that reads as "punctuation landing").
 * Pulse keyframes preserve translate(0, 0) so the steady-state stays
 * pinned at the SVG's authored cx/cy (199, 44). */
@keyframes wordmark-dot-fall {
  0%   { transform: translate(0, -14px); opacity: 0; }
  30%  { opacity: 1; }
  100% { transform: translate(0, 0); opacity: 1; }
}
@keyframes wordmark-dot-pulse {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50%      { transform: translate(0, 0) scale(1.5); }
}
.wordmark-dot {
  transform-box: fill-box;
  transform-origin: center;
  filter: drop-shadow(0 0 4px rgba(201, 168, 76, 0.5));
  animation:
    wordmark-dot-fall 1s cubic-bezier(0.5, 0, 0.5, 1.2) 500ms backwards,
    wordmark-dot-pulse 3.2s ease-in-out 1500ms infinite;
}
.wordmark:hover .wordmark-dot,
.wordmark:focus-visible .wordmark-dot {
  filter: drop-shadow(0 0 10px rgba(201, 168, 76, 0.8));
}
/* :focus-visible outline is inherited from §6 (FOUND-12 palette-aware
 * focus ring); no override needed here. */

/* -- 9b. Helix-trace exhalation (Stage-2 fabric weave) ---------------- */
/* After the wordmark intro completes, a fine sinusoidal stroke emerges
 * from the dot's punctum and extends rightward into the page, drawn
 * over ~3s, then fades. Stroke = currentColor so the trace participates
 * in the palette journey, matching the medium of the flow field
 * underneath (the field is drawn with --ink). The dot stays gold; the
 * trace it exhales is INK — the wordmark feeds the field.
 *
 * Two shape variants — toggleable via the ?-panel
 * (data-wordmark-trace="calm|expressive|none"). Default "calm".
 *
 * pathLength=100 is set on the <path> so stroke-dasharray operates in
 * normalised units regardless of actual path length, keeping the
 * keyframe values portable between the two variants.
 *
 * The path exits the SVG viewBox (x>320); .wordmark-svg overflow is
 * lifted to visible so the stroke can extend slightly into the page
 * region beyond the wordmark's box. The fixed-position .wordmark
 * container has no overflow constraint, so the spill renders as
 * intended. Reduced-motion zeroes both keyframes (§10), so the trace
 * never appears for those visitors. */
.wordmark-svg { overflow: visible; }

.wordmark-trace--calm,
.wordmark-trace--expressive {
  display: none;
  fill: none;
  stroke-width: 1;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  opacity: 0;
}

@keyframes wordmark-trace-emerge {
  0%   { stroke-dashoffset: 100; opacity: 0; }
  15%  { opacity: 0.55; }
  60%  { stroke-dashoffset: 0;   opacity: 0.55; }
  100% { stroke-dashoffset: 0;   opacity: 0; }
}
@keyframes wordmark-trace-replay {
  0%   { stroke-dashoffset: 100; opacity: 0; }
  20%  { opacity: 0.55; }
  70%  { stroke-dashoffset: 0;   opacity: 0.55; }
  100% { stroke-dashoffset: 0;   opacity: 0; }
}

html[data-wordmark-trace="calm"] .wordmark-trace--calm,
html[data-wordmark-trace="expressive"] .wordmark-trace--expressive {
  display: inline;
  animation: wordmark-trace-emerge 3s cubic-bezier(0.4, 0, 0.4, 1) 2700ms forwards;
}

html[data-wordmark-trace="calm"] .wordmark:hover .wordmark-trace--calm,
html[data-wordmark-trace="calm"] .wordmark:focus-visible .wordmark-trace--calm,
html[data-wordmark-trace="expressive"] .wordmark:hover .wordmark-trace--expressive,
html[data-wordmark-trace="expressive"] .wordmark:focus-visible .wordmark-trace--expressive {
  animation: wordmark-trace-replay 2s cubic-bezier(0.4, 0, 0.4, 1);
}

/* -- 10. Reduced motion (forward-compatible) --------------------------- */
/* FOUND-06 writes <html data-motion="reduced"> before first paint.
 * Phase 1 has no animated styles; this block is a placeholder to make
 * the contract explicit for Phase 2 and Phase 3 (which add keyframes). */
html[data-motion="reduced"] * {
  animation-duration: 0s !important;
  animation-delay: 0s !important;
  transition-duration: 0s !important;
}

/* -- 10a. Page-load stagger (post-Phase-5 polish) -------------------- */
/* Orchestrated reveal: header arrives first, then the 8 sections with
 * cumulative 80ms delays, then the footer. Sections below the fold also
 * fade-up — feels like the manifesto is being authored as the visitor
 * descends. animation-fill-mode: backwards holds opacity:0 during the
 * delay so the section doesn't flash visible before its turn. Reduced-
 * motion users get instant render via §10's animation-* zeroing. */
@keyframes fade-up {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
header,
main > section,
footer {
  animation: fade-up 700ms cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
}
header                            { animation-delay: 0ms; }
main > section:nth-of-type(1)     { animation-delay: 120ms; }
main > section:nth-of-type(2)     { animation-delay: 200ms; }
main > section:nth-of-type(3)     { animation-delay: 280ms; }
main > section:nth-of-type(4)     { animation-delay: 360ms; }
main > section:nth-of-type(5)     { animation-delay: 440ms; }
main > section:nth-of-type(6)     { animation-delay: 520ms; }
main > section:nth-of-type(7)     { animation-delay: 600ms; }
main > section:nth-of-type(8)     { animation-delay: 680ms; }
footer                            { animation-delay: 760ms; }

/* -- 11. Palette journey (IDENT-05) ----------------------------------- */
/* Replaces Phase 2's TEMPORARY §11 (band-tracker.js + per-band ink
 * retargeting selectors) — that scaffolding existed only to keep canvas lines
 * visible against opaque section backgrounds during the gate review.
 * This block delivers the same auto-tinting via @keyframes
 * palette-journey driven by animation-timeline: scroll(root block) on
 * Chrome/Edge/Safari 18+ (compositor thread), with an IntersectionObserver
 * + discrete-state CSS fallback for Firefox. assets/band-tracker.js +
 * assets/app.js's import line are deleted in the SAME commit as this
 * change (D-07c — eviction-and-replacement atomicity is load-bearing;
 * a non-atomic commit window leaves dark sections with invisible
 * canvas lines).
 *
 * --ink, --canvas-bg, --accent are @property-registered as <color> in
 * §2 above. Without that registration this block jump-cuts (Pitfall 6).
 *
 * Phase 4 TODO: in the gold band, accent stays warm-gold per D-03e
 * ("accent follows ink's contrast partner") — but bg is also warm-gold,
 * so any decorative accent (signup-button strokes, hyperlinks) renders
 * gold-on-gold and is invisible. When CONT/SIGNUP introduces those
 * elements in Sections 6/7, override the relevant component's accent
 * to var(--deep-forest) inside @media or via a section-scoped rule.
 */

/* (a) Per-section background fills — provide the visible per-band colour
 *     behind the (transparent over compositor-driven) canvas. Identical
 *     to old §11's `main > section[data-palette='X'] { background: ... }`
 *     rules (kept intact during the eviction).
 *
 *     Per-section text-colour overrides on dark bands are RETAINED.
 *     Plan revision (auto-fix Rule 1, 2026-04-27): the plan author's
 *     claim that "body text inherits color: var(--ink) which is now
 *     animated by the journey itself" is structurally wrong — `--ink`
 *     is a single root token whose value at any moment matches the
 *     CURRENTLY-VISIBLE band. Sections off-screen still inherit that
 *     same root `--ink`, so e.g. the Position (forest) heading with
 *     no scoped color renders #0F2B2B-on-#0F2B2B (1:1) when the user
 *     is scrolled to the dawn band. pa11y-ci catches this as a
 *     color-contrast failure on first paint. The literal cream text
 *     on dark bands is the correct invariant — independent of scroll
 *     position — and does not conflict with the animated --ink (which
 *     drives the canvas line, NOT body text). */
main > section[data-palette='dawn']    { background: #F5F0E8; }
main > section[data-palette='forest']  { background: #0F2B2B; color: #F5F0E8; }
main > section[data-palette='teal']    { background: #1A4A4A; color: #F5F0E8; }
main > section[data-palette='sage']    { background: #486F60; color: #F5F0E8; }
main > section[data-palette='gold']    { background: #C9A84C; }
main > section[data-palette='cream']   { background: #F5F0E8; }

/* (b) Modern path: CSS scroll-driven animation. Compositor thread; no
 *     JS in the hot loop. animation-timeline: scroll(root block) maps
 *     0% → top of <html>'s scroll position, 100% → bottom. Keyframes
 *     placed at section centres (each section is 1/8 of total scroll;
 *     centres at 6.25%, 18.75%, ..., 93.75%). Adjacent identical
 *     keyframes (43.75% ↔ 56.25% sage; 68.75% ↔ 81.25% gold) express
 *     the per-band SUSTAIN — the colour holds while the visitor reads
 *     across two sections (Pitfall 5 — modern browsers handle this
 *     correctly as "no animation between these stops"). */
@supports (animation-timeline: scroll()) {
  :root {
    animation: palette-journey linear;
    animation-duration: 1ms;             /* REQUIRED — scroll-driven animations need a non-zero duration to progress; the timeline drives, the duration is a placeholder (RESEARCH.md Pattern 2) */
    animation-timeline: scroll(root block);
  }
  @keyframes palette-journey {
    0%     { --ink: #0F2B2B; --canvas-bg: #F5F0E8; --accent: #C9A84C; }  /* dawn (boundary) */
    6.25%  { --ink: #0F2B2B; --canvas-bg: #F5F0E8; --accent: #C9A84C; }  /* dawn — section 1 centre */
    18.75% { --ink: #F5F0E8; --canvas-bg: #0F2B2B; --accent: #A8C5B8; }  /* forest — section 2 centre */
    31.25% { --ink: #F5F0E8; --canvas-bg: #1A4A4A; --accent: #A8C5B8; }  /* teal — section 3 centre */
    43.75% { --ink: #F5F0E8; --canvas-bg: #486F60; --accent: #A8C5B8; }  /* sage — section 4 centre */
    56.25% { --ink: #F5F0E8; --canvas-bg: #486F60; --accent: #A8C5B8; }  /* sage SUSTAIN — section 5 centre */
    68.75% { --ink: #0F2B2B; --canvas-bg: #C9A84C; --accent: #C9A84C; }  /* gold — section 6 centre */
    81.25% { --ink: #0F2B2B; --canvas-bg: #C9A84C; --accent: #C9A84C; }  /* gold SUSTAIN — section 7 centre */
    93.75% { --ink: #0F2B2B; --canvas-bg: #F5F0E8; --accent: #C9A84C; }  /* cream — section 8 centre */
    100%   { --ink: #0F2B2B; --canvas-bg: #F5F0E8; --accent: #C9A84C; }  /* cream (boundary) */
  }
}

/* (c) Fallback path: discrete data-palette-active states with 500ms
 *     transition. assets/palette-fallback.js writes the active section's
 *     data-palette to <html> via IntersectionObserver
 *     (rootMargin: '-49% 0px -49% 0px' creates a viewport-midline strip
 *     so exactly one section is intersecting at any moment). The
 *     transition smooths each discrete jump; the @property registrations
 *     in §2 enable colour interpolation during the transition window. */
@supports not (animation-timeline: scroll()) {
  :root {
    transition: --ink 500ms ease, --canvas-bg 500ms ease, --accent 500ms ease;
  }
  :root[data-palette-active='dawn']   { --ink: #0F2B2B; --canvas-bg: #F5F0E8; --accent: #C9A84C; }
  :root[data-palette-active='forest'] { --ink: #F5F0E8; --canvas-bg: #0F2B2B; --accent: #A8C5B8; }
  :root[data-palette-active='teal']   { --ink: #F5F0E8; --canvas-bg: #1A4A4A; --accent: #A8C5B8; }
  :root[data-palette-active='sage']   { --ink: #F5F0E8; --canvas-bg: #486F60; --accent: #A8C5B8; }
  :root[data-palette-active='gold']   { --ink: #0F2B2B; --canvas-bg: #C9A84C; --accent: #C9A84C; }
  :root[data-palette-active='cream']  { --ink: #0F2B2B; --canvas-bg: #F5F0E8; --accent: #C9A84C; }
}

/* -- 12. Prose typography (CONT-01..02 / D-CSS) ----------------------- */
/* Each section's body prose constrains to 65ch measure, centred, with
 * paragraph spacing that gives the literary register room to breathe.
 * Phase 1 already locked the global serif stack (Cormorant Garamond +
 * Georgia fallback, font-size 1.125rem, line-height 1.65 — see §4 above).
 * This block is the LAYOUT contract for the prose; the FONT contract is
 * unchanged.
 *
 * The 65ch measure derives from the A11Y-04 acceptance window (60–75ch).
 * 65ch is the centre of that range and matches the eye's optimal saccade
 * length for serif body text at 1.125rem with 1.65 leading.
 *
 * `margin-inline: auto` centres the column inside the section's
 * `padding: 4rem 2rem` (Phase 1 §8). `margin-block: 1em` gives roughly
 * one body-line gap between paragraphs — visually a "verse break", not a
 * dense block. Combined with the section's `min-height: 80vh` + flex
 * `justify-content: center`, the prose floats vertically in the middle
 * of each band. */

main > section > p {
  max-width: 65ch;
  margin-inline: auto;
  margin-block: 1em;
}

/* First paragraph of each section: no top margin (the section's flex
 * centring already handles vertical positioning; an extra 1em on top
 * would make the prose feel low-anchored within the band). */
main > section > p:first-of-type {
  margin-top: 0;
}

/* Last paragraph of each section: no bottom margin (mirror; prevents
 * an extra gap before the next band's background-colour edge). */
main > section > p:last-of-type {
  margin-bottom: 0;
}

/* Lede — section-opening lines set in italic display scale, used as
 * visual emphasis on declarative thesis sentences and section lead-ins.
 * Reads as a pull-quote-y opener without being a hidden h2. Stays
 * within the 65ch prose column so line wraps follow the same rhythm
 * as the rest of the band. */
main > section > p.lede {
  font-size: clamp(1.45rem, 2.8vw, 1.95rem);
  font-style: italic;
  line-height: 1.35;
  font-weight: 500;
}

/* In-prose anchors on the manifesto. Inherit the prose colour (which
 * follows --ink through the palette journey) so the user-agent default
 * blue — which fails contrast on most bands — never appears. Underline
 * treatment mirrors the .privacy-prose / 404-page anchor styling so the
 * visual register is consistent across pages. */
main > section > p > a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 0.04em;
}
main > section > p > a:hover {
  text-decoration-thickness: 0.08em;
}

/* Lexicon hover-reveal — the dictionary-entry line in the footer is
 * always visible; the longer Greek-roots paragraph below it stays at
 * opacity 0 until the visitor hovers the entry, then fades in gently.
 * A small footer reward for the curious reader.
 *
 * Accessibility: touch devices (no hover capability) and reduced-motion
 * visitors see the etymology unconditionally — both groups skip the
 * reveal so the content is never gated behind a gesture they can't
 * perform. cursor: help signals "more info on hover" via the standard
 * question-mark cursor. The etymology paragraph is in the DOM at all
 * times, so screen readers announce it linearly regardless of CSS. */
.lexicon { cursor: help; }
.lexicon + p {
  opacity: 0;
  transition: opacity 0.7s ease;
}
.lexicon:hover + p,
.lexicon:focus-within + p {
  opacity: 1;
}
@media (hover: none) {
  .lexicon + p { opacity: 1; }
}
html[data-motion="reduced"] .lexicon + p { opacity: 1; }

/* -- 13. Privacy page (SIGNUP-09) ------------------------------------- */
/* Scoped via body.privacy-page so this block only applies to
 * /privacy.html, leaving index.html's manifesto layout unchanged.
 *
 * The privacy page's single <section> is short reading material; the
 * 80vh min-height from §8 would push the <footer> off-screen on tall
 * viewports. Override to auto. The 65ch prose constraint from §12 still
 * applies (prose readability is a global contract).
 *
 * Subsection headings (h2) are VISIBLE (unlike index.html's
 * visually-hidden h2 landmarks) — these are real content headings.
 * Hierarchy: h1 (page title), h2 (each subsection), no h3 needed. */

/* Pin the palette tokens on privacy + 404 pages so they do NOT participate
 * in the manifesto palette journey. The :root @keyframes animates --ink /
 * --canvas-bg / --accent based on scroll position; on a single-section
 * page like /privacy or /404 that animation traverses ink+bg combinations
 * authored for the 8-band manifesto, hitting low-contrast intermediates
 * partway through scroll (text reads as invisible). The body-scope
 * declarations below override the inherited animated values on these
 * pages — descendants inherit the pinned values, and the canvas (which
 * reads --ink off documentElement) is irrelevant here because privacy.html
 * and 404.html do not include the flow-field canvas. */
body.privacy-page,
body.error-page {
  --ink: #0F2B2B;
  --canvas-bg: #F5F0E8;
  --accent: #C9A84C;
  background: var(--canvas-bg);
  color: var(--ink);
}

body.privacy-page main > section {
  min-height: auto;
  padding: 4rem 2rem 6rem;     /* extra bottom padding so footer breathes */
}

body.privacy-page .privacy-prose {
  /* Constrain the entire prose section to a 65ch column.
   * Headings inherit the constraint via .privacy-prose containment. */
  max-width: 65ch;
  margin-inline: auto;
}

body.privacy-page .privacy-prose > * {
  max-width: 100%;             /* override any per-element 65ch from prose typography */
}

body.privacy-page .privacy-prose h1 {
  font-size: clamp(1.75rem, 4vw, 2.5rem);
  margin-block: 0 1.5em;
}

body.privacy-page .privacy-prose h2 {
  font-size: clamp(1.25rem, 2.5vw, 1.75rem);
  margin-block: 2em 0.5em;
}

body.privacy-page .privacy-prose ul {
  margin-block: 1em;
  padding-inline-start: 1.5em;
}
body.privacy-page .privacy-prose li {
  margin-block: 0.5em;
}

body.privacy-page .privacy-prose a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 0.04em;
}

body.privacy-page .privacy-prose .privacy-lead {
  font-style: italic;
  font-size: 1.05em;
}

body.privacy-page .privacy-prose .privacy-meta {
  margin-top: 3em;
  padding-top: 1.5em;
  border-top: 1px solid currentColor;
  font-size: 0.9em;
  opacity: 0.85;
}

/* -- 13a. 404 page (CONT-09) ----------------------------------------- */
/* Scoped via body.error-page so this block only applies to /404.html.
 * Auto-fix Rule 2 (correctness): in-prose anchors on the 404 page would
 * otherwise inherit the browser default link colour (blue) and render
 * 1.6:1 on the deep-forest band — pa11y-ci flags this as a colour-
 * contrast failure. Forcing `color: inherit` re-tints the link to the
 * forest band's `color: #F5F0E8` (cream), giving 12.6:1 contrast. The
 * underline + offset mirrors the .privacy-prose anchor treatment so
 * the visual register is consistent across non-manifesto pages. */

body.error-page main > section > p > a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 0.04em;
}
body.error-page main > section > p > a:hover {
  text-decoration-thickness: 0.08em;
}

/* -- 14. Drop cap (D-CSS / Pitfall 6) --------------------------------- */
/* The Opening section's first paragraph gets a 3.5em italic dropped
 * initial letter — a tactile invitation to read. Scoped to dawn band
 * only; subsequent sections do not need this device.
 *
 * Numbering note: the plan specified §13 but §13 was already taken by
 * the Privacy page block (Plan 04-04, lines 425-511). Rule 1 deviation:
 * shifted to §14 to preserve existing numbering integrity.
 *
 * Pitfall 6 mitigation: the drop cap colour is HARD-PINNED to
 * deep-forest #0F2B2B (matches the dawn band's --ink value). If the
 * drop cap inherited `color: currentColor` (and therefore the animated
 * --ink), the dropped letter would visibly recolour during the dawn
 * → forest palette transition while it was still in view. The dawn
 * band's section min-height of 80vh (Phase 1 §8) means the drop cap
 * has scrolled out of view BEFORE the palette transition begins,
 * making the pin a defence-in-depth — but it costs nothing and
 * eliminates an entire class of visual regression.
 *
 * font-style: italic on ::first-letter resolves correctly across
 * Chrome 138+, Firefox 142+, Safari 18+ (MDN browser-compat 2026-04).
 * The italic axis WOFF2 loaded in Phase 3
 * (assets/fonts/cormorant-garamond-latin-wght-italic.woff2) is the
 * resolved face.
 *
 * Selector uses `>` (direct child) so it scopes to the dawn section's
 * own first paragraph, NOT to any nested element's first paragraph.
 * `p:first-of-type` ensures only the FIRST paragraph in the section
 * gets the drop cap — Section 1 has 3 paragraphs after 04-01. */
section[data-palette='dawn'] > p:first-of-type::first-letter {
  font-size: 3.5em;
  float: left;
  line-height: 0.85;
  padding: 0.05em 0.1em 0 0;
  font-weight: 500;
  font-style: italic;
  color: #0F2B2B;
}

/* -- 15. Signup form (SIGNUP-01..03 / D-CSS) ------------------------- */
/* Form lives in Section 7 (gold band). Styled to feel like the
 * manifesto continues — serif typeface (inherits Cormorant Garamond
 * via body cascade), generous padding, no rounded "modern" feel.
 * Underline-only focus state — palette-aware via --accent EXCEPT in
 * the gold band where --accent is also gold (invisible against the
 * gold background); we override to deep-forest in section[data-palette='gold'].
 *
 * Honeypot hide pattern (research correction #5 / Pitfall 3):
 * position:absolute + left:-9999px instead of display:none.
 * Modern bots filter display:none fields; left:-9999px is invisible
 * to humans + screen readers but bots can't easily distinguish from
 * a real input. data-1p-ignore + data-lpignore=true (in HTML)
 * suppress 1Password / LastPass autofill respectively. */

.signup {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 65ch;             /* match prose measure (§12) */
  margin-inline: auto;
  margin-top: 2rem;
}

.signup__email {
  font-family: inherit;
  font-size: 1.0625rem;
  line-height: 1.5;
  padding: 0.75rem 0;
  border: 0;
  border-bottom: 1px solid currentColor;
  background: transparent;
  color: currentColor;
  outline: none;
  width: 100%;
}
.signup__email::placeholder {
  color: currentColor;
  opacity: 0.5;
}
.signup__email:focus,
.signup__email:focus-visible {
  /* Underline-only focus state — thicker bottom border. The palette-
   * aware default is --accent; in the gold band we override below
   * because --accent == --canvas-bg (invisible). */
  border-bottom-width: 2px;
  outline: none;
}

/* Gold-band override: --accent is gold-on-gold; use ink (deep-forest)
 * for the focus underline so it's visible. Phase 3 §11 left a TODO
 * comment for this; this rule honours it. */
section[data-palette='gold'] .signup__email:focus,
section[data-palette='gold'] .signup__email:focus-visible {
  border-bottom-color: var(--ink);
}

.signup__honeypot {
  /* Pitfall 3 — left:-9999px instead of display:none. */
  position: absolute;
  left: -9999px;
  height: 0;
  width: 0;
  overflow: hidden;
}

.signup__button {
  font-family: inherit;
  font-size: 1.0625rem;
  font-style: italic;
  padding: 0.75rem 1.5rem;
  border: 1px solid currentColor;
  background: transparent;
  color: currentColor;
  cursor: pointer;
  align-self: flex-start;
  transition: background 200ms ease, color 200ms ease;
}
.signup__button:hover,
.signup__button:focus-visible {
  background: var(--ink);
  color: var(--canvas-bg);
}
/* Gold-band: --ink is deep-forest, --canvas-bg is gold; the swap
 * above produces forest button on hover — readable against the
 * gold band background. No override needed; the palette tokens
 * resolve correctly here. */

.signup__consent {
  font-size: 0.9375rem;
  line-height: 1.55;
  max-width: none;             /* override §12's 65ch — the consent
                                  paragraph IS the form's full width */
  margin: 0.5rem 0 0;
  opacity: 0.85;
}
.signup__consent a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 0.04em;
}

.signup__status {
  /* Initially empty; signup.js writes confirmation prose into this
   * region. The aria-live="polite" attribute (in HTML) means screen
   * readers announce the change without interrupting current speech. */
  min-height: 1.5rem;
  font-style: italic;
  margin-top: 1rem;
}
.signup__status:empty {
  display: none;               /* avoid empty box reserving space when
                                  no message is rendered. min-height
                                  kicks in only when populated. */
}

/* -- 16. Reading Rhythm (Phase 7, RHY-01..06) ------------------------- */
/* Prose measure, anchor scroll-margin, scroll-progress line, palette-flip
 * breathing, reduced-motion handling. Reuses var(--scroll) already
 * written to <html> by assets/scroll-driver.js (Phase 2/3) — no new JS.
 *
 * Numbering note: the plan named this block §13, but §13 was already
 * taken by the Privacy page block (Plan 04-04), §14 by the Drop cap
 * (Plan 04-05), §15 by the Signup form (Plan 04-07). Rule 3 deviation:
 * shifted to §16 to preserve existing numbering integrity. The "Reading
 * Rhythm" identity and RHY-01..06 traceability are unchanged. See
 * .planning/phases/07-reading-rhythm/07-CONTEXT.md for the locked
 * decisions.
 *
 * Acceptance contract (RHY-01..06):
 *   RHY-01  ≈62-char prose measure via max-width: 36rem on body prose.
 *   RHY-02  scroll-margin-block-start: 5rem on every <section> + [id]
 *           anchor target, so anchor jumps land below the fixed wordmark.
 *   RHY-03  1px fixed left-edge progress line driven by var(--scroll).
 *   RHY-04  ≥96px breathing room (padding-block-start: 6rem) on sections
 *           that follow a palette flip; class is applied in index.html.
 *   RHY-05  Mobile rhythm verified at 375 / 768 / 1280 — no extra rules
 *           needed; the 36rem cap auto-constrains below 576px.
 *   RHY-06  Progress line hidden under prefers-reduced-motion (rule
 *           lives in §7a's existing reduced-motion block above). */

/* RHY-01: prose measure — 36rem ≈ 62 chars in italic Cormorant at the
 * default 16px root. Applied per body-prose container so the canvas,
 * wordmark, signup form, and footer line are unaffected. The §12 rule
 * `main > section > p { max-width: 65ch }` already ships a similar
 * constraint for paragraphs; this block tightens it to a calibrated
 * rem-based measure and extends the cap to lists, blockquotes, and
 * pull-quotes too. The 36rem value wins over 65ch because rem is
 * font-size-locked (predictable across viewports) whereas ch shifts
 * with the rendered font's `0` glyph width. */
main > section > p,
main > section > ul,
main > section > ol,
main > section > blockquote,
main > section > .pull-quote {
  max-width: 36rem;
}

/* RHY-02: anchor scroll-margin — 5rem clears the fixed wordmark
 * (top: 1.5rem + ~3.5rem mark height) on anchor jumps so the
 * destination prose lands cleanly below the mark, not under it. */
section,
[id] {
  scroll-margin-block-start: 5rem;
}

/* RHY-04: palette-flip breathing — 96px (6rem) padding-block-start on
 * sections that follow a palette flip. Class is applied in index.html
 * on the four relevant <section>s; targeting via class keeps the rule
 * legible and rename-safe versus :nth-of-type. The §8 rule
 * `main > section { padding: 4rem 2rem }` already provides 4rem of
 * vertical padding; this rule ADDS the extra 2rem on the top edge so
 * the flip-after section reads with ~6rem total above its first line. */
.palette-flip-after {
  padding-block-start: 6rem;
}

/* RHY-03: left-edge scroll-progress line — 1px-wide fixed line, tinted
 * by var(--accent) (which animates across the palette journey via
 * §11's @keyframes), scaled vertically by var(--scroll) (which the rAF
 * scroll listener in assets/scroll-driver.js writes on <html> every
 * frame). transform-origin: top means the line grows downward from 0
 * to full viewport height as the visitor descends. z-index sits below
 * the wordmark (z=100) and grain (z=9999) but above the canvas (z=1).
 * pointer-events: none so the line never blocks clicks. The element is
 * authored as a real <div aria-hidden="true"> in index.html — it has
 * no semantic role and screen readers must skip it. */
.scroll-progress {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--accent);
  transform: scaleY(var(--scroll, 0));
  transform-origin: top;
  z-index: 50;
  pointer-events: none;
}

/* -- 17. v1.1 Inline SVGs + Trust UI (SVG-01..05, TRUST-01..04) -------- */
/* Hand-authored line-art SVGs (Phase 8) sized + placed via CSS; trust-UI
 * form restructure for the Signup section.
 *
 * Register (08-CONTEXT.md, binding for every authored path):
 *   stroke="currentColor" + fill="none" + stroke-width="1.5"
 *   stroke-linecap="round" + stroke-linejoin="round"
 *   2-5% jitter on Bezier control-point handles (paths read as authored,
 *   not vector-tool output). All paths inherit currentColor — the palette
 *   journey re-tints them automatically as the band advances.
 *
 * Roster:
 *   SVG-01  Opening hero (two abstract figures, ~10° lean, 40% gap)
 *   SVG-02  Position diagram (Coach + Coachee + AI nodes, evidentiary)
 *   SVG-03  Practice timeline (4 icons: Schedule / Record / Transcribe /
 *           Reflect)
 *   SVG-04  Signup lock-with-open-shackle (decorative, beside reassurance)
 *   SVG-05  reserved (no asset shipped this plan; placeholder for v1.1.x
 *           future inline iconography under the same hand-register)
 *   TRUST-01..04  visible <label>Email</label>, reassurance line beneath
 *           submit, lock icon, Resend backend untouched. */

/* SVG-01 — Opening hero — REMOVED 2026-04-29.
 * The two-figure abstract line-art read as random disconnected strokes at
 * rendered scale (the AI-vector-tool register trap PITFALLS.md flagged).
 * Drop-cap C carries the Opening section as it did in v1.0; the manifesto
 * register prefers no image to an unsuccessful one. SVG-01 is deferred —
 * see PROJECT.md for the post-v1.1 status. */

/* SVG-02 — Position diagram. Block-centred beside the pull-quote on wide
 * viewports (the section is single-column flex; the figure stacks below
 * the lede on every viewport — the brief "to the right on desktop" was
 * deferred in favour of the simpler stack since the section is already a
 * vertical column and a side-by-side layout would require breaking the
 * §8 flex-column contract). The italic Cormorant labels inside the SVG
 * use the same family stack as body prose; fill: currentColor honours
 * the palette journey. */
.position-figure {
  margin: 1.5rem auto;
  max-width: 28rem;
  text-align: center;
}
.position-svg {
  width: 100%;
  height: auto;
  display: block;
  margin: 0 auto;
}
.position-label {
  font-family: 'Cormorant Garamond', 'Cormorant Garamond Fallback', Georgia, serif;
  font-style: italic;
  font-size: 22px;
  fill: currentColor;
}

/* SVG-03 — Practice timeline. Four cards in a horizontal row on desktop,
 * vertical stack on mobile. The cards themselves are visual scaffolding
 * for the lifecycle; the prose paragraphs that follow elaborate the
 * geometry. */
.practice-timeline {
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-start;
  justify-content: center;
  gap: 0.5rem;
  margin: 2rem auto;
  max-width: 100%;
}
.timeline-card {
  flex: 0 1 auto;
  text-align: center;
  min-width: 0;
}
.timeline-icon {
  width: 3.25rem;
  height: 3.25rem;
  display: block;
  margin: 0 auto 0.6rem;
}
.timeline-step {
  font-style: italic;
  font-size: 1.125rem;
  margin: 0;
  /* REWRITE-01: was opacity 0.9 — cream-on-sage caps at ~4.98:1, so any
   * muting drops below WCAG AA (axe color-contrast). Full strength. */
  opacity: 1;
}
.timeline-arrow {
  width: 2.5rem;
  height: 1.25rem;
  flex: 0 0 auto;
  align-self: flex-start;
  margin-top: 1.05rem;
  opacity: 0.7;
}
@media (max-width: 48rem) {
  .practice-timeline {
    gap: 0.25rem;
  }
  .timeline-card {
    flex: 1 1 0;
  }
  .timeline-icon {
    width: 2.25rem;
    height: 2.25rem;
  }
  .timeline-step {
    font-size: 0.875rem;
  }
  .timeline-arrow {
    width: 1.25rem;
    margin-top: 0.6rem;
  }
}

/* SVG-04 — Signup label. Visible label sits above the input in italic
 * Cormorant. The §15 Phase 4 form rules continue to govern the input +
 * button + consent + status. */
.signup__label {
  display: block;
  font-style: italic;
  font-size: 1rem;
  margin-bottom: 0.25rem;
  opacity: 0.9;
}

/* -- 18. Section figures — hand-drawn line-art SVGs (PIC-01..04) -------- */
/* Replaces the original v1.1 photographs. The fal.ai photographs read
 * as too-rendered against the manifesto's typographic register; the
 * hand-drawn SVG versions inherit currentColor and palette journey
 * tinting like every other inline SVG on the page. Two figures:
 *   .section-figure--cabinet  Diagnosis section, filing cabinet vertical
 *   .section-figure--chairs   Invitation section, two chairs horizontal
 */
.section-figure {
  margin: 2rem auto;
}
.section-figure svg {
  width: 100%;
  height: auto;
  display: block;
}
.section-figure--chairs {
  max-width: 32rem;
}

/* -- 19. Simulations (SIM-01..08) ------------------------------------- */
/* Sim 1 "Where the data lives" (Principle section) and Sim 2 "Coach
 * view vs coachee view" (Practice section). Both share the .sim base
 * with a modifier class (.sim--data-locality, .sim--coach-coachee).
 *
 * Plan 10-01 lands the Sim 1 selectors and the shared base. Plan
 * 10-03 appends the Sim 2 selectors below. Plans 10-02/10-04 add no
 * CSS — the JS modules write attributes the CSS already responds to.
 *
 * Palette tokens: --ink, --canvas-bg, --accent are inherited from the
 * sage band of the palette journey. No new colour tokens.
 */
.sim {
  margin: 2rem auto;
  max-width: min(100%, 42rem);
}
/* Sim caption / empty-state lede typography. The existing
 * `main > section > p.lede` rule (line ~614) uses a direct-child
 * selector and does NOT match `.lede` elements that sit inside a
 * <figure> (Sim 1 caption) or a nested <section> (Sim 2 empty
 * state). This rule mirrors the existing typography contract so the
 * lede register is preserved across both sims. */
.sim .lede {
  font-size: clamp(1.45rem, 2.8vw, 1.95rem);
  font-style: italic;
  line-height: 1.35;
  font-weight: 500;
}
.sim__toggle {
  border: none;
  padding: 0;
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0;
}
.sim__toggle label {
  padding-inline: 1rem;
  padding-block: 0.625rem;
  min-block-size: 2.75rem;
  cursor: pointer;
  font-style: italic;
  font-size: 1.125rem;
  color: inherit;
  display: inline-flex;
  align-items: center;
}
.sim__toggle input:checked + label {
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 0.08em;
  text-underline-offset: 0.25em;
}
.sim__toggle-divider {
  opacity: 0.3;
  align-self: center;
}
.sim__caption {
  margin-block: 2rem 1rem;
}
.sim__panes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
}
.sim__pane {
  padding: 1rem;
}
/* Sim 1 only — stack the two panes in the same grid cell so the
 * figure height is max(platform, pod) at all times. Toggling [data-view]
 * swaps `visibility: hidden` between them; the hidden pane still
 * reserves layout space, so the figure does not jump vertically and
 * the content below stays in place. The horizontal extent is also
 * stable because the visible pane occupies a single full-width column. */
.sim--data-locality .sim__panes {
  grid-template-columns: 1fr;
  gap: 0;
}
.sim--data-locality .sim__pane--platform,
.sim--data-locality .sim__pane--pod {
  grid-column: 1;
  grid-row: 1;
}
@media (max-width: 48rem) {
  .sim--coach-coachee .sim__panes {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
}
.sim__pane-heading {
  font-style: italic;
  font-size: 1.25rem;
  margin: 0 0 1rem;
}
.sim__platform-list,
.sim__pod-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.sim__platform-list li,
.sim__pod-list li {
  font-style: italic;
  font-size: 1.125rem;
  line-height: 1.5;
  margin-block-end: 0.75rem;
}
.sim__pod-scope {
  font-style: italic;
  font-size: 0.9375rem;
  line-height: 1.5;
  margin: 1rem 0 0;
  /* REWRITE-01: was 0.75 — fails AA on the sage band once the Pod view is
   * toggled visible (cream-on-sage ceiling ~4.98:1). */
  opacity: 1;
}
/* Sim 1 inline icons — same line-art register as the Practice timeline.
 * Sized in em so they track the italic-Cormorant text size. */
.sim__icon {
  width: 1.25em;
  height: 1.25em;
  vertical-align: -0.22em;
  margin-inline-end: 0.4em;
  flex-shrink: 0;
}
/* Subtle hover animations. The §10 prefers-reduced-motion global rule
 * sets animation-duration to 0s !important so reduced-motion users see
 * static icons regardless of the keyframe declarations below. */
@keyframes sim-icon-record-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.08); }
}
@keyframes sim-icon-transcribe-shiver {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-1.5px); }
}
@keyframes sim-icon-reflect-breath {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.08); }
}
@keyframes sim-icon-schedule-flip {
  0%   { transform: skewX(0deg); }
  35%  { transform: skewX(-6deg); }
  70%  { transform: skewX(2deg); }
  100% { transform: skewX(0deg); }
}
@keyframes sim-icon-schedule-mark {
  0%   { transform: scale(0.4); opacity: 0; }
  60%  { transform: scale(1.3); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
/* Practice timeline — slow cycle keyframes. Same active gestures as the
 * hover keyframes above, but with an 8-second total period (~1s active,
 * ~7s rest) and 2s offsets per step so the four icons fire in sequence
 * Schedule -> Record -> Transcribe -> Reflect, looping forever. The
 * §10 prefers-reduced-motion global rule zeroes animation-duration
 * site-wide so reduced-motion users see static icons. */
@keyframes timeline-schedule-flip-cycle {
  0%, 9%, 100% { transform: skewX(0deg); }
  3%          { transform: skewX(-6deg); }
  6%          { transform: skewX(2deg); }
}
@keyframes timeline-schedule-mark-cycle {
  0%, 100% { transform: scale(1); }
  6%       { transform: scale(1.4); }
  12%      { transform: scale(1); }
}
@keyframes timeline-record-cycle {
  0%, 100% { transform: scale(1); }
  4%       { transform: scale(1.08); }
  8%       { transform: scale(1); }
  12%      { transform: scale(1.08); }
  16%      { transform: scale(1); }
}
@keyframes timeline-transcribe-cycle {
  0%, 100% { transform: translateY(0); }
  6%       { transform: translateY(-1.5px); }
  12%      { transform: translateY(0); }
}
@keyframes timeline-reflect-cycle {
  0%, 100% { transform: scale(1); }
  6%       { transform: scale(1.08); }
  12%      { transform: scale(1); }
}
/* Schedule — page flips, then date dot marks (one-shot per hover) */
.sim__platform-list li:hover .sim__icon--schedule .sim__icon-body,
.timeline-card:hover .sim__icon--schedule .sim__icon-body {
  animation: sim-icon-schedule-flip 600ms ease-in-out;
  transform-origin: 0% 50%;
  transform-box: fill-box;
}
.sim__platform-list li:hover .sim__icon--schedule .sim__icon-dot,
.timeline-card:hover .sim__icon--schedule .sim__icon-dot {
  animation: sim-icon-schedule-mark 700ms ease-out 200ms backwards;
  transform-origin: 50% 50%;
  transform-box: fill-box;
}
/* Record — capsule pulses, suggesting active sound (continuous on hover) */
.sim__pod-list li:hover .sim__icon--record .sim__icon-capsule,
.timeline-card:hover .sim__icon--record .sim__icon-capsule {
  animation: sim-icon-record-pulse 1.4s ease-in-out infinite;
  transform-origin: 50% 50%;
  transform-box: fill-box;
}
/* Transcribe — five bars shiver with staggered phase (continuous on hover) */
.sim__pod-list li:hover .sim__icon--transcribe .sim__icon-bar,
.timeline-card:hover .sim__icon--transcribe .sim__icon-bar {
  animation: sim-icon-transcribe-shiver 0.7s ease-in-out infinite;
}
.sim__pod-list li:hover .sim__icon--transcribe .sim__icon-bar--2,
.timeline-card:hover .sim__icon--transcribe .sim__icon-bar--2 { animation-delay: 80ms; }
.sim__pod-list li:hover .sim__icon--transcribe .sim__icon-bar--3,
.timeline-card:hover .sim__icon--transcribe .sim__icon-bar--3 { animation-delay: 160ms; }
.sim__pod-list li:hover .sim__icon--transcribe .sim__icon-bar--4,
.timeline-card:hover .sim__icon--transcribe .sim__icon-bar--4 { animation-delay: 240ms; }
.sim__pod-list li:hover .sim__icon--transcribe .sim__icon-bar--5,
.timeline-card:hover .sim__icon--transcribe .sim__icon-bar--5 { animation-delay: 320ms; }
/* Reflect — both arcs breathe outward (continuous on hover) */
.sim__pod-list li:hover .sim__icon--reflect path,
.timeline-card:hover .sim__icon--reflect path {
  animation: sim-icon-reflect-breath 1.8s ease-in-out infinite;
  transform-origin: 50% 50%;
  transform-box: fill-box;
}
/* Practice timeline — default (no-hover) cycle. Each icon fires its
 * gesture once per 8s loop, with 2s offsets so the four chain in order. */
.practice-timeline .sim__icon--schedule .sim__icon-body {
  animation: timeline-schedule-flip-cycle 8s ease-in-out infinite;
  transform-origin: 0% 50%;
  transform-box: fill-box;
}
.practice-timeline .sim__icon--schedule .sim__icon-dot {
  animation: timeline-schedule-mark-cycle 8s ease-in-out infinite;
  transform-origin: 50% 50%;
  transform-box: fill-box;
}
.practice-timeline .sim__icon--record .sim__icon-capsule {
  animation: timeline-record-cycle 8s ease-in-out 2s infinite;
  transform-origin: 50% 50%;
  transform-box: fill-box;
}
.practice-timeline .sim__icon--transcribe .sim__icon-bar {
  animation: timeline-transcribe-cycle 8s ease-in-out 4s infinite;
}
.practice-timeline .sim__icon--transcribe .sim__icon-bar--2 { animation-delay: calc(4s + 80ms); }
.practice-timeline .sim__icon--transcribe .sim__icon-bar--3 { animation-delay: calc(4s + 160ms); }
.practice-timeline .sim__icon--transcribe .sim__icon-bar--4 { animation-delay: calc(4s + 240ms); }
.practice-timeline .sim__icon--transcribe .sim__icon-bar--5 { animation-delay: calc(4s + 320ms); }
.practice-timeline .sim__icon--reflect path {
  animation: timeline-reflect-cycle 8s ease-in-out 6s infinite;
  transform-origin: 50% 50%;
  transform-box: fill-box;
}
.sim__live {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
/* [data-view] toggle on the figure root drives pane visibility. Both
 * panes are in the DOM at all times so the no-JS path renders the
 * default platform-view automatically. */
.sim--data-locality[data-view="platform"] .sim__pane--pod { visibility: hidden; }
.sim--data-locality[data-view="pod"] .sim__pane--platform { visibility: hidden; }
/* Sim 2 — coach / coachee split file-tree. */
.sim-tree {
  list-style: none;
  padding-inline-start: 0;
  margin: 0;
}
.sim-tree ul {
  list-style: none;
  padding-inline-start: 1rem;
  margin: 0;
  border-inline-start: 1px solid color-mix(in oklch, var(--ink) 40%, transparent);
}
.sim-tree__node {
  padding-block: 0.25rem;
  padding-inline-start: 1.25rem;
  font-style: italic;
  font-size: 1.125rem;
  line-height: 1.5;
  position: relative;
}
.sim-tree__label {
  display: inline-block;
}
/* Hairline marker — drawn as a 1px pseudo-element rather than a Unicode
 * character. Folder marker is a short right-leaning arc; leaf marker
 * is a short horizontal stroke. Both use currentColor at 60% opacity
 * so they read as part of the line-art register. */
.sim-tree__node--folder::before {
  content: '';
  position: absolute;
  inset-inline-start: 0.4rem;
  inset-block-start: 0.85rem;
  inline-size: 0.5rem;
  block-size: 0;
  border-block-start: 1px solid color-mix(in oklch, var(--ink) 60%, transparent);
  transform: rotate(-15deg);
  transform-origin: 0 50%;
}
.sim-tree__node--leaf::before {
  content: '';
  position: absolute;
  inset-inline-start: 0.4rem;
  inset-block-start: 0.95rem;
  inline-size: 0.5rem;
  block-size: 0;
  border-block-start: 1px solid color-mix(in oklch, var(--ink) 60%, transparent);
}
.sim-tree__access-line {
  font-style: italic;
  font-size: 0.9375rem;
  line-height: 1.5;
  margin: 0.15rem 0 0;
  /* REWRITE-01: was 0.75 — fails AA on the sage band (cream-on-sage
   * ceiling ~4.98:1). "Private. Not shared." now reads at full strength. */
  opacity: 1;
}
.sim__actions {
  display: flex;
  justify-content: center;
  gap: 1rem;
  margin-block-start: 1.5rem;
}
.sim__action {
  background: transparent;
  border: none;
  padding: 0.5rem 0;
  color: inherit;
  cursor: pointer;
  font-family: inherit;
  font-style: italic;
  font-size: 1.125rem;
  text-decoration: underline;
  text-decoration-thickness: 0.04em;
  text-underline-offset: 0.15em;
}
.sim__action:hover {
  text-decoration-thickness: 0.08em;
}
.sim__active-dot {
  display: inline-block;
  inline-size: 0.4rem;
  block-size: 0.4rem;
  border-radius: 50%;
  background: var(--accent);
  margin-inline-start: 0.5rem;
  vertical-align: middle;
}
/* Mobile reorder per UI-SPEC: at <=768px, coachee pane comes first
 * (the architectural claim is "the coachee&rsquo;s data is unchanged"), coach
 * pane second, buttons last. The desktop grid puts coach first /
 * coachee second by source order; we use grid `order` to swap on
 * mobile without touching the DOM. */
.sim--coach-coachee .sim__pane--coach { order: 1; }
.sim--coach-coachee .sim__pane--coachee { order: 2; }
@media (max-width: 48rem) {
  .sim--coach-coachee .sim__pane--coachee { order: 1; }
  .sim--coach-coachee .sim__pane--coach { order: 2; }
}
/* noscript layout — when JS is disabled, the empty [data-sim2-coach-pane]
 * shell still renders (just empty). Hide it under noscript by collapsing
 * its width; the <noscript> coach pane is the visible substitute. */
.sim--coach-coachee [data-sim2-coach-pane]:empty {
  display: none;
}

/* -- 20. Foreground veil ("!" toggle) --------------------------------- */
/* assets/veil.js sets html[data-veil] on "!" keydown. Hide foreground
 * UI (header, footer, scroll-progress, skip-link) and the contents of
 * each manifesto section, leaving the section bands themselves intact
 * so the §11 palette journey still paints across the viewport. */
html[data-veil] header,
html[data-veil] footer,
html[data-veil] .scroll-progress,
html[data-veil] .skip-link {
  display: none;
}
html[data-veil] main > section > * {
  display: none;
}

/* -- 21. Chair wobble (post-Phase-8 polish) ---------------------------- */
/* When the cursor enters a chair group, the chair rocks gently backward
 * as if someone has just sat down — pivots on the back-feet floor
 * contact, overshoots forward, settles. Four chairs across two sections
 * each get their own rhythm + amplitude so no two wobbles look the
 * same. The whole block lives inside @media (prefers-reduced-motion:
 * no-preference) so visitors who request reduced motion get static
 * chairs (no hover effect at all). */
@media (prefers-reduced-motion: no-preference) {
  .chairs-svg .chair {
    transform-box: fill-box;
    pointer-events: bounding-box;
  }
  /* Pivot on the back-feet midpoint at the floor line. Left chair's
   * back-feet sit at ≈x=115 in the 88..188 bbox (27% across); the
   * floor line is at y=245 in the 88..250 bbox (97% down). Right
   * chair mirrors at 73%, same y. */
  .chairs-svg .chair--left {
    transform-origin: 27% 97%;
  }
  .chairs-svg .chair--right {
    transform-origin: 73% 97%;
  }
  /* Section 1 (dawn) — first pair, gentler rhythm. */
  [data-palette="dawn"] .chairs-svg .chair--left:hover {
    animation: chair-wobble-1 1.6s cubic-bezier(0.34, 1.5, 0.64, 1);
  }
  [data-palette="dawn"] .chairs-svg .chair--right:hover {
    animation: chair-wobble-2 1.3s cubic-bezier(0.28, 1.55, 0.5, 1);
  }
  /* Section 6 (gold) — second pair, deliberately different so the
   * empty-chairs invitation feels lived-in rather than mechanical. */
  [data-palette="gold"] .chairs-svg .chair--left:hover {
    animation: chair-wobble-3 1.5s cubic-bezier(0.4, 1.4, 0.6, 1);
  }
  [data-palette="gold"] .chairs-svg .chair--right:hover {
    animation: chair-wobble-4 1.75s cubic-bezier(0.3, 1.6, 0.55, 1);
  }
  /* Each keyframe set: tilt back, overshoot forward (small bounce),
   * tiny secondary rock, settle. Sign of rotation differs per side
   * (negative = top tilts left = back for left-chairs; positive =
   * top tilts right = back for right-chairs). */
  @keyframes chair-wobble-1 {
    0%   { transform: rotate(0); }
    22%  { transform: rotate(-2.0deg); }
    55%  { transform: rotate(0.7deg); }
    80%  { transform: rotate(-0.3deg); }
    100% { transform: rotate(0); }
  }
  @keyframes chair-wobble-2 {
    0%   { transform: rotate(0); }
    18%  { transform: rotate(2.4deg); }
    50%  { transform: rotate(-0.9deg); }
    78%  { transform: rotate(0.4deg); }
    100% { transform: rotate(0); }
  }
  @keyframes chair-wobble-3 {
    0%   { transform: rotate(0); }
    25%  { transform: rotate(-1.7deg); }
    60%  { transform: rotate(0.8deg); }
    85%  { transform: rotate(-0.25deg); }
    100% { transform: rotate(0); }
  }
  @keyframes chair-wobble-4 {
    0%   { transform: rotate(0); }
    24%  { transform: rotate(2.8deg); }
    55%  { transform: rotate(-1.0deg); }
    82%  { transform: rotate(0.45deg); }
    100% { transform: rotate(0); }
  }
}

/* -- 22. Homepage rewrite — hero, ephemeral input, triplet (REWRITE-01) -- */
/* Cinematic rebuild of the index page: one idea per band. Re-uses every
 * existing palette/typography/motion token; adds no new colour. All
 * styling is here (CSP style-src 'self' forbids inline styles); the two
 * new JS modules (ephemeral.js, reveal.js) only set classes + CSSOM
 * custom properties, which the strict CSP permits (same path scroll-driver
 * already uses). Sharp corners throughout — the manifesto register rejects
 * the rounded "modern" feel (see §15). */

/* -- 22a. Hero (dawn band) -------------------------------------------- */
.hero {
  text-align: center;
  gap: 0;
}
/* Kill the §14 dawn drop-cap on the hero — its first child is the eyebrow
 * label, not body prose, and a 3.5em initial would wreck it. The selector
 * must out-specify §14's `section[data-palette='dawn'] > p:first-of-type`
 * (0,2,3), so it carries the attribute + class too (0,3,3). */
section[data-palette='dawn'].hero > p:first-of-type::first-letter {
  font-size: inherit;
  float: none;
  padding: 0;
  font-weight: inherit;
  font-style: inherit;
  line-height: inherit;
  color: inherit;
}
.hero__eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-size: clamp(0.78rem, 1.4vw, 0.95rem);
  font-style: normal;
  opacity: 0.8;
  margin: 0 auto 1.5rem;
}
.hero__title {
  font-size: clamp(2.5rem, 6.8vw, 5.25rem);
  line-height: 1.04;
  font-weight: 500;
  letter-spacing: -0.012em;
  max-width: 20ch;
  margin: 0 auto;
  text-wrap: balance;
}
/* The headline's full-stop IS the brand punctum — gold, gently pulsing,
 * the same gesture as the wordmark dot. */
.hero__dot {
  display: inline-block;
  width: 0.32em;
  height: 0.32em;
  vertical-align: -0.02em;
  margin-inline-start: 0.03em;
  overflow: visible;
}
.hero__dot circle {
  transform-box: fill-box;
  transform-origin: center;
  filter: drop-shadow(0 0 5px rgba(201, 168, 76, 0.5));
  animation: hero-dot-pulse 3.2s ease-in-out 1400ms infinite;
}
@keyframes hero-dot-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.3); }
}
.hero__sub {
  font-size: clamp(1.1rem, 2.1vw, 1.4rem);
  line-height: 1.5;
  max-width: 40rem;
  margin: 1.75rem auto 0;
  opacity: 0.9;
}
.hero__actions {
  margin: 2.25rem auto 0;
  max-width: none;
}
/* Primary CTA — filled with --ink, sharp corners, inverts on hover.
 * Overrides the §12 in-prose anchor underline (higher specificity). */
.cta {
  display: inline-block;
  font-style: italic;
  font-size: 1.15rem;
  padding: 0.7rem 1.7rem;
  /* FIXED colours, not the animated --ink/--canvas-bg palette tokens: the
   * hero is always the cream dawn band, and those two tokens CROSS OVER as
   * the scroll journey moves toward the forest band — mid-transition the
   * button's background == its text colour and the label vanishes. Deep
   * forest on cream is always correct here and never inverts. */
  border: 1px solid var(--deep-forest);
  background: var(--deep-forest);
  color: var(--cream);
  text-decoration: none;
  transition: background 220ms ease, color 220ms ease;
}
.cta:hover,
.cta:focus-visible {
  background: transparent;
  color: var(--deep-forest);
}
/* Breathing scroll cue — a soft chevron that nods downward. */
.scroll-cue {
  display: block;
  width: 1.85rem;
  margin: 3rem auto 0;
  color: inherit;
  opacity: 0.55;
  animation: scroll-cue-breathe 2.6s ease-in-out infinite;
}
.scroll-cue svg {
  width: 100%;
  height: auto;
  display: block;
}
@keyframes scroll-cue-breathe {
  0%, 100% { transform: translateY(0); opacity: 0.45; }
  50%      { transform: translateY(4px); opacity: 0.85; }
}

/* -- 22b. Ephemeral input (forest band) ------------------------------- */
/* "Write something private; release it; watch it dissolve into the field
 * and leave no trace." The proof is that nothing is ever transmitted —
 * assets/ephemeral.js animates words drifting up (echoing the wordmark's
 * helix-trace exhalation) and clears the value. Deliberately NOT a form. */
.ephemeral {
  position: relative;
  max-width: 36rem;
  margin: 2.5rem auto 0;
}
.ephemeral__label {
  display: block;
  font-style: italic;
  font-size: 1.05rem;
  opacity: 0.85;
  margin-bottom: 0.75rem;
}
.ephemeral__row {
  display: flex;
  gap: 1rem;
  align-items: baseline;
  border-bottom: 1px solid currentColor;
}
.ephemeral__field {
  flex: 1 1 auto;
  min-width: 0;
  font-family: inherit;
  font-style: italic;
  font-size: 1.2rem;
  line-height: 1.5;
  padding: 0.6rem 0;
  border: 0;
  background: transparent;
  color: currentColor;
  outline: none;
}
.ephemeral__field::placeholder {
  color: currentColor;
  opacity: 0.4;
}
.ephemeral__release {
  flex: 0 0 auto;
  font-family: inherit;
  font-style: italic;
  font-size: 1rem;
  padding: 0.3rem 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 0.04em;
}
.ephemeral__release:hover {
  text-decoration-thickness: 0.08em;
}
.ephemeral__caption {
  font-style: italic;
  font-size: 0.95rem;
  opacity: 0.7;
  margin: 0.85rem 0 0;
  min-height: 1.4em;
}
.ephemeral__live {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* Drift overlay — JS positions it over the input row and fills it with
 * per-word spans. Each word floats up and dissolves; --i, --dx, --rot are
 * set per word by ephemeral.js. */
.ephemeral__ghost {
  position: absolute;
  left: 0;
  right: 0;
  pointer-events: none;
  font-style: italic;
  font-size: 1.2rem;
  line-height: 1.5;
}
.ephemeral__word {
  display: inline-block;
  white-space: pre;
  will-change: transform, opacity;
  animation: ephemeral-drift 1.9s cubic-bezier(0.4, 0, 0.4, 1) both;
  animation-delay: calc(var(--i, 0) * 70ms);
}
@keyframes ephemeral-drift {
  0%   { opacity: 0.92; transform: translate(0, 0) rotate(0); filter: blur(0); }
  55%  { opacity: 0.5; }
  100% { opacity: 0; transform: translate(var(--dx, 6px), -2.4em) rotate(var(--rot, 2deg)); filter: blur(2.5px); }
}

/* -- 22c. Triplet reveal (gold band) ---------------------------------- */
/* "owns / sees / sees neither" — revealed line by line as the band enters
 * view (assets/reveal.js adds .is-revealed; html.js-reveal-armed gates the
 * hidden start-state so no-JS / reduced-motion visitors see all three at
 * once). */
.triplet {
  list-style: none;
  padding: 0;
  margin: 2.75rem auto 0;
  max-width: 36rem;
  text-align: center;
}
.triplet__line {
  font-style: italic;
  font-size: clamp(1.3rem, 3vw, 1.9rem);
  line-height: 1.3;
  margin: 0.6rem 0;
}
html.js-reveal-armed .triplet__line {
  opacity: 0;
  /* visibility:hidden (not just opacity:0) so axe/htmlcs SKIP the not-yet-
   * revealed lines for color-contrast — transparent text is otherwise
   * flagged. visibility flips instantly on reveal (kept out of the
   * transition list); opacity + transform carry the fade. */
  visibility: hidden;
  transform: translateY(12px);
  transition: opacity 0.85s ease, transform 0.85s ease;
  transition-delay: calc(var(--i, 0) * 240ms);
}
html.js-reveal-armed .triplet__line.is-revealed {
  opacity: 1;
  visibility: visible;
  transform: none;
}

/* -- 22d. Mobile rhythm ----------------------------------------------- */
@media (max-width: 36rem) {
  .hero__title { letter-spacing: -0.005em; }
  .ephemeral__row { gap: 0.5rem; }
  .ephemeral__release { font-size: 0.9375rem; }
}
