/* ══════════════════════════════════════════════════════════════════════════
   AISAAC — THE ONE STYLESHEET
   The Blueprint System (RULE 02): paper, ink, Geist. Single source of truth for the whole site.

   REBUILD SPEC §2.1. Rules that govern this file:
     · No other stylesheet ships, except the deliberately-foreign artifact
       islands (cinematic-plumbing.html inline, lab/explainer.html bundle)
       and ssri/style.css (separate research sub-brand, unlisted).
     · NO @media may appear in any HTML file. Three breakpoints, declared
       here, once: 1100 (container) · 900 (grids→2col, nav→hamburger)
       · 600 (grids→1col).
     · Page-scoped <style> is permitted ONLY for a page-unique LAYOUT
       (an embed's aspect ratio), max 20 lines, using var(--*) only.
       Never a color, never a font-family, never card chrome.
     · Fonts are self-hosted (assets/fonts/). Zero external requests.

   PATH LAW (confirmed 2026-08-07 against the live deploy):
     The site serves from https://sancovp.github.io/aisaac/ — a GitHub Pages
     PROJECT subpath, not a domain root. Root-absolute hrefs ("/style.css")
     resolve to sancovp.github.io/style.css and 404. Every in-page href and
     src is therefore RELATIVE ("style.css" from root, "../style.css" from
     one level down). Absolute URLs appear ONLY in <link rel=canonical> and
     the og:/twitter: meta, where the protocol requires them.

   BLOCK VOCABULARY (spec §1.1). Nothing outside this list may be styled:
     B0 head · B1 .nav · B2 .claim · B3 .artifact · B4 .receipts
     B5 .mechanism · B6 .ladder · B7 .honest · B8 .price-block
     B9 .final-cta · B10 .footer
   VISUAL-REBUILD ADDITIONS (2026-08-07, Fable pass — recorded, not snuck in):
     .artifact-code (build.html's quickstart pre — was an undefined class
     rendering in browser-default mono at the top of an empty frame) ·
     .price-block.wide (a .pricing-table crammed inside the 620px block got
     ~176px for its description column) · .panels.span-last (a 7-item panel
     grid orphaned its 7th) · .claim-duo + .portrait-frame (the isaac.html
     portrait slot, Isaac's order) · .tier-sheet + .tier .anchor (pricing.html
     reskinned onto the system; content frozen verbatim).
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Self-hosted fonts (replaces the fonts.googleapis.com @import) ───────── */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('assets/fonts/inter-latin-var.woff2') format('woff2');
  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+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url('assets/fonts/jetbrains-mono-latin-var.woff2') format('woff2');
  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+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Fraunces — THE DISPLAY VOICE (RULE 02 §2). SIL OFL 1.1; the licence ships
   beside it at assets/fonts/fraunces-OFL.txt. Subset to the same latin range
   as Inter/Mono; the SOFT and WONK axes are pinned flat (SOFT=0 sharp
   terminals, WONK=0 no wonky forms) so only wght + opsz remain variable.
   opsz is left variable ON PURPOSE: font-optical-sizing is `auto` by default,
   so a claim set at 5rem gets the high-contrast display cut and a pull-quote
   at 1rem gets the sturdy text cut, from one file. Used ONLY for h1/h2
   claims, .myth register lines and pull-quotes — never for chrome. */
@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('assets/fonts/fraunces-latin-var.woff2') format('woff2');
  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+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Geist + Geist Mono — THE BLUEPRINT VOICE (RULE 02). SIL OFL 1.1; the licence
   ships beside them at assets/fonts/geist-OFL.txt. Variable weight, one file each.
   Geist Mono carries every claim, number and button; Geist carries the prose. */
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('assets/fonts/geist-var.woff2') format('woff2');
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('assets/fonts/geist-mono-var.woff2') format('woff2');
}

/* ── Tokens ─────────────────────────────────────────────────────────────── */
/* THE BLUEPRINT PALETTE (RULE 02): white paper, black ink, two greys, and
   nothing else. The old token NAMES are kept because 80+ pages spend them —
   every accent name now resolves to ink, so no page can carry a colour. */
:root {
  --paper: #ffffff;
  --ink: #000000;
  --signal: #e10600;           /* BOOK A CALL, everywhere. Paper text on it = 4.97:1 */
  --signal-deep: #b30500;      /* the book buttons' hover */
  --leak-act: #b45309;         /* the leak tree's "so we" — amber, 5.0:1 on paper (yellow text is unreadable on white) */
  --leak-good: #15803d;        /* the leak tree's "so that you" — green, 5.0:1 on paper */
  /* SECTION TINTS (RULE 02 §1) — a section's hue says what it is about. bg = the section's ground,
     the bare name = its accent (top edge · label · card headings); every accent ≥5.4:1 on its ground */
  --tint-red-bg: #fdeeec;    --tint-red: #c20500;      /* the problem */
  --tint-gold-bg: #fff4e6;   --tint-gold: #a44a06;     /* the framework */
  --tint-violet-bg: #f3eeff; --tint-violet: #6a2fd6;   /* the theory */
  --tint-cyan-bg: #e9f7fd;   --tint-cyan: #075985;     /* what you build */
  --tint-green-bg: #eaf6ee;  --tint-green: #116b33;    /* growth, community */
  /* THE LOOP'S OWN PALETTE — used ONLY as the separators of the four levels, so the list and the
     film beside it share one colour code: gold = the CEO's orb, cyan / violet = the first two
     worker cards. Bars, never text. */
  --loop-gold: #ff8c1f;
  --loop-cyan: #1abfff;
  --loop-violet: #9e6bff;
  /* THE PARCELS that ride the blueprint columns — a little isometric package, yellow with an ink
     outline and a strip of tape. Drawn here, not the emoji, so it is the same on every device. */
  --pkg: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><g stroke='%23000' stroke-width='1' stroke-linejoin='round'><polygon points='10,2 18,6 10,10 2,6' fill='%23ffe26b'/><polygon points='2,6 10,10 10,18 2,14' fill='%23ffd43b'/><polygon points='18,6 10,10 10,18 18,14' fill='%23e6b400'/></g><polygon points='5.2,4.4 6.8,3.6 14.8,7.6 13.2,8.4' fill='%23fff6c9'/></svg>");
  --line: #e6e6e6;             /* the faint blueprint columns */
  --bp-columns:
    linear-gradient(var(--line), var(--line)) calc(50% - 550px) 0 / 1px 100% no-repeat,
    linear-gradient(var(--line), var(--line)) calc(50% - 183px) 0 / 1px 100% no-repeat,
    linear-gradient(var(--line), var(--line)) calc(50% + 183px) 0 / 1px 100% no-repeat,
    linear-gradient(var(--line), var(--line)) calc(50% + 550px) 0 / 1px 100% no-repeat;
  --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.16 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  --void: #ffffff;
  --glass-bg: rgba(0,0,0,0.02);
  --glass-border: rgba(0,0,0,var(--rule-a));
  --glass-shine: rgba(255,255,255,0);
  --blur: 0px;
  --mercury-blue: #000000;
  --mercury-violet: #3d3d3d;
  --mercury-chrome: #555555;
  --mercury-teal: #000000;
  --mercury-rose: #3d3d3d;
  --amber: #000000;            /* DEPRECATED — dead theme. Do not use on new pages. */
  --text-1: #000000;
  --text-2: #3d3d3d;
  --text-3: #6b6b6b;           /* 5.3:1 on paper — the lightest text allowed */
  --ok: #1f7a3a;
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 22px;
  --r-bp: 28px;                /* the blueprint card */
  --wrap: 1100px;              /* the one container width (breakpoint 1) */
  --body: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --mono: 'Geist Mono', 'SF Mono', ui-monospace, monospace;
  --display: 'Geist Mono', 'SF Mono', ui-monospace, monospace;

  /* ── THE DENSITY GRADIENT (RULE 02 §3) ────────────────────────────────
     Depth made visible. Every one of these is overridden by the
     body[data-depth] block below; the values HERE are depth 1 (the rung
     level), so a page that forgets its attribute renders exactly as the
     site did before this token layer existed. Nothing else in this file
     hard-codes a vertical rhythm, a heading size or a hairline alpha —
     if it does, the gradient has a hole in it. */
  --sec-y: 5rem;               /* section vertical rhythm */
  --claim-y: 5rem;             /* the claim band's own top pad */
  --h1-size: clamp(2.2rem, 5vw, 3.8rem);
  --h1-measure: 20ch;          /* the claim's line length */
  --h2-size: clamp(1.45rem, 3vw, 2.1rem);
  --lede-size: 0.98rem;
  --lh-lede: 1.85;             /* leading tightens as you descend */
  --lh-prose: 1.9;
  --rule-a: 0.14;              /* hairline alpha — rules get MORE present deeper */
  --micro: var(--body);        /* micro-labels flip to mono at depth 2+ */
  --r-card: var(--r-lg);       /* glass rounds off toward instrument-panel */
}

/* Depth 0 — THE DOOR. Spacious: huge type, few elements, one action.
   Depth 1 — the rungs and the map (the :root defaults above).
   Depth 2 — notes and posts: reading density, mono micro-labels.
   Depth 3 — inside/: data-table energy, brightest hairlines, tightest set. */
body[data-depth="0"] {
  --sec-y: 7rem; --claim-y: 7rem;
  --h1-size: clamp(2.8rem, 7.5vw, 5.4rem); --h1-measure: 16ch;
  --h2-size: clamp(1.7rem, 3.6vw, 2.6rem);
  --lede-size: 1.1rem; --lh-lede: 1.7; --lh-prose: 1.8;
  --rule-a: 0.12;
}
body[data-depth="2"] {
  --sec-y: 4rem; --claim-y: 4rem;
  --h1-size: clamp(1.9rem, 4vw, 2.9rem); --h1-measure: 24ch;
  --h2-size: clamp(1.3rem, 2.4vw, 1.75rem);
  --lede-size: 0.94rem; --lh-lede: 1.76; --lh-prose: 1.82;
  --rule-a: 0.18;
  --micro: var(--mono);
  --r-card: var(--r-md);
}
body[data-depth="3"] {
  --sec-y: 3.2rem; --claim-y: 3.4rem;
  --h1-size: clamp(1.7rem, 3.2vw, 2.4rem); --h1-measure: 26ch;
  --h2-size: clamp(1.2rem, 2vw, 1.5rem);
  --lede-size: 0.9rem; --lh-lede: 1.66; --lh-prose: 1.72;
  --rule-a: 0.22;
  --micro: var(--mono);
  --r-card: var(--r-sm);
}

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

html { scroll-behavior: smooth; scroll-padding-top: 5.5rem; }
/* ^ scroll-padding: the nav is sticky, so every in-page anchor (#price,
   #think, #exhibit …) used to land its target UNDER the bar. */

body {
  font-family: var(--body);
  font-weight: 400;
  background: var(--void);
  color: var(--text-1);
  min-height: 100vh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  animation: page-fade-in .8s ease-out;
}
@keyframes page-fade-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

/* THE BLUEPRINT COLUMNS (RULE 02) — four faint vertical rules down the whole
   page, at the container's edges and its thirds, like a drafting sheet. They
   sit behind everything, so a band with its own ground (grain, ink) covers
   them and the columns only show on plain paper. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: var(--bp-columns);
}

a { color: inherit; text-decoration: none; }
img, video, iframe { max-width: 100%; }

/* Keyboard focus must always be visible (WCAG 2.4.7) */
a:focus-visible, button:focus-visible {
  outline: 2px solid var(--mercury-blue);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--mercury-blue); color: var(--void);
  font-family: var(--mono); font-size: 0.75rem; padding: 0.7rem 1.2rem;
}
.skip-link:focus { left: 0; }

/* ══ B1 — NAV (the nav IS the ladder; identical bytes on every page) ═════ */
.nav, .header {          /* .header = legacy alias, retained until C re-renders blog/ */
  padding: 1.2rem 2rem;
  display: flex; justify-content: space-between; align-items: center; gap: 1.5rem;
  border-bottom: 1px solid var(--glass-border);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  position: sticky; top: 0; z-index: 100;
  background: rgba(255,255,255,0.92);
  flex-wrap: wrap;
}
.logo {
  font-family: var(--mono); font-weight: 700; font-size: 1.05rem; letter-spacing: -0.02em;
  flex-shrink: 0; color: var(--ink);
}
.nav-links {
  display: flex; gap: 1.6rem; align-items: center; flex-wrap: wrap;
  margin-left: auto;
}
.nav-links a { font-size: 0.8rem; color: var(--text-3); transition: color 0.2s; }
.nav-links a:hover, .nav-links a[aria-current="page"] { color: var(--text-1); }
.nav-cta {
  font-family: var(--mono); font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink); background: var(--paper); border: 1px solid var(--ink);
  padding: 0.5rem 1.2rem; border-radius: var(--r-sm); transition: background 0.15s, color 0.15s;
  display: inline-block;
}
.nav-cta:hover { background: var(--ink); color: var(--paper); }

/* THE GLASS TOP BAR (RULE 02) — the one glass surface and the one colour on
   the site. It is sticky, so it never leaves the screen, and it carries only
   the TWI wordmark and a red BOOK A CALL: the visitor can never not see the
   button. Paper at 55% under a blur, so whatever scrolls beneath frosts. */
.nav-glass {
  background: rgba(255,255,255,0.55);
  backdrop-filter: blur(16px) saturate(1.4); -webkit-backdrop-filter: blur(16px) saturate(1.4);
  border-bottom: 1px solid rgba(0,0,0,0.12);
  padding: 0.8rem 2rem; flex-wrap: nowrap;
}
.nav-glass .logo { font-size: 0.95rem; letter-spacing: -0.02em; min-width: 0; flex: 1 1 auto; }
/* ⛔ `.logo` is flex-shrink:0 site-wide; the long TWI name must be allowed to shrink and wrap,
   or on a phone it shoves the book button off the screen (measured: its right edge at 429px
   on a 390px screen). */
.nav-book {
  flex: none; cursor: pointer; white-space: nowrap;
  font-family: var(--mono); font-weight: 700; font-size: 0.82rem;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--paper); background: var(--signal); border: 1px solid var(--signal);
  border-radius: var(--r-md); padding: 0.72rem 1.25rem;
  transition: background 0.15s, border-color 0.15s;
}
.nav-book:hover { background: var(--signal-deep); border-color: var(--signal-deep); }
/* BOOK A CALL IS RED EVERYWHERE — every button that opens the call (data-buy="call"),
   on paper and on the ink band alike. The doubled attribute out-ranks `.bp-ink .cta-primary`. */
.cta-primary[data-buy="call"][data-buy] {
  background: var(--signal); border-color: var(--signal); color: var(--paper);
}
.cta-primary[data-buy="call"][data-buy]:hover { background: var(--signal-deep); border-color: var(--signal-deep); color: var(--paper); }
.nav-book:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
@media (max-width: 600px) {
  .nav-glass { padding: 0.65rem 1rem; gap: 0.9rem; }
  .nav-glass .logo { font-size: 0.74rem; line-height: 1.3; }
  .nav-book { font-size: 0.72rem; padding: 0.62rem 0.85rem; }
}

/* The hamburger is PROGRESSIVE ENHANCEMENT, in the safe direction:
   nav.js stamps data-nav="collapsed" on the header. With JS off, no
   attribute is ever set, the toggle stays hidden and the links stay
   visible (wrapping to a second row). The menu is never unreachable. */
.nav-toggle {
  display: none;
  font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-2);
  background: var(--glass-bg); border: 1px solid var(--glass-border);
  border-radius: var(--r-sm); padding: 0.5rem 0.9rem; cursor: pointer;
}
.nav-toggle:hover { color: var(--text-1); border-color: rgba(125,211,252,0.25); }

/* ══ Layout primitives ══════════════════════════════════════════════════ */
section { padding: var(--sec-y) 1.5rem; }
.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; }

/* Spacing utilities. These exist so that NO page ever needs a style=""
   attribute for a layout nudge — the block vocabulary stays closed. */
.flush-top { padding-top: 0; }
.mt-s { margin-top: 1.6rem; }
.mt-m { margin-top: 2.2rem; }
.mt-l { margin-top: 3.5rem; }
.measure { max-width: 68ch; }
.measure-none { max-width: none; }

/* ── The in-article figure. ───────────────────────────────────────────────
   54 posts carried this as three inline declarations on every image, which
   is 41 copies of a border colour the stylesheet already owns — and the
   reason the diagrams never picked up a token change. One class instead.
   height:auto keeps the intrinsic width/height attributes (which every img
   now carries, so the layout does not shift while the PNG loads) from
   squashing the render. */
.fig { margin: 2rem 0; }
.fig img, img.fig-img {
  display: block; width: 100%; height: auto;
  border-radius: var(--r-card); border: 1px solid var(--glass-border);
}
.fig figcaption, .fig-cap {
  font-family: var(--micro); font-size: 0.7rem; line-height: 1.7;
  color: var(--text-3); margin-top: 0.6rem;
}
.dark {
  background: var(--glass-bg);
  border-top: 1px solid var(--glass-border);
  border-bottom: 1px solid var(--glass-border);
}

/* ══ Typography ═════════════════════════════════════════════════════════ */
.eyebrow, .tag {         /* .tag = legacy alias */
  font-family: var(--mono); font-size: 0.78rem; font-weight: 500; letter-spacing: 0;
  color: var(--ink); margin-bottom: 1.5rem;
  display: flex; align-items: center; gap: 0;
}
/* the node and its connector: a square, a short line, then the pill */
.eyebrow::before, .tag::before {
  content: ''; display: block; width: 3.1rem; height: 11px; flex: none;
  background:
    linear-gradient(var(--ink), var(--ink)) 11px 50% / calc(100% - 11px) 1px no-repeat,
    linear-gradient(var(--paper), var(--paper)) 1px 1px / 9px 9px no-repeat,
    linear-gradient(var(--ink), var(--ink)) 0 0 / 11px 11px no-repeat;
}

/* THE BRAND TAG PILLS (vocabulary + assignments: tools/tags.py). A post declares
   which of the framework concepts it belongs to, right in the eyebrow row, and
   each pill links back to that concept's section on the corpus index. No new
   block vocabulary and no new colour: the eyebrow is ALREADY the mono label
   voice, so a pill is that voice plus a hairline. The hover border is the exact
   rgba `.rung:hover` already uses — the filter row and the pills are the same
   control, so they light up the same way. */
.eyebrow { flex-wrap: wrap; row-gap: 0.5rem; }
.eyebrow a {
  color: var(--text-2); text-decoration: none; line-height: 1;
  padding: 0.28rem 0.6rem; border: 1px solid var(--glass-border);
  border-radius: var(--r-sm); background: var(--glass-bg);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.eyebrow a:hover { color: var(--mercury-blue); border-color: rgba(125,211,252,0.25); }

/* The claims speak in the display serif; the chrome stays Inter. That split
   IS the de-generic move (RULE 02 §2) — the gradient fill and the breathe
   are Builder A's base, kept. Fraunces carries the gradient at 650: lighter
   and the stems thin out under a transparent fill. */
h1 {
  font-family: var(--display);
  font-weight: 700; font-size: var(--h1-size); line-height: 1.08;
  letter-spacing: -0.035em; margin-bottom: 1.5rem; max-width: var(--h1-measure);
  color: var(--ink);
}
/* FIX: an <h1><span> had no rule at all, so the second clause inherited the
   same transparent fill and read as one run-on line on five pages. */
h1 span, h1 .accent { color: var(--ink); }

h2 {
  font-family: var(--body);
  font-weight: 600; font-size: var(--h2-size); line-height: 1.15;
  letter-spacing: -0.025em; margin-bottom: 1.4rem; max-width: 30ch;
  color: var(--text-1);
}
/* h3/h4 are chrome, not claims — they stay in Inter. */
h3 { font-weight: 600; font-size: 1.05rem; margin-bottom: 0.6rem; color: var(--text-1); }
h4 { font-weight: 600; font-size: 0.92rem; margin-bottom: 0.4rem; color: var(--text-1); }

/* .myth — the myth-register line (RULE 02 §2, RULE 00 law 2). The ONE place
   outside a heading or a pull-quote that speaks in the display voice, and it
   belongs to inside/ and to Isaac's personal pages. Never at the door. */
.myth {
  font-family: var(--display); font-weight: 400;
  font-size: 1.12rem; line-height: 1.7; letter-spacing: -0.005em;
  color: var(--text-1); max-width: 54ch; margin-bottom: 1.6rem;
}

.lede, .sub {
  font-size: var(--lede-size); line-height: var(--lh-lede); color: var(--text-2);
  max-width: 56ch; margin-bottom: 2.2rem;
}
.lede strong, .sub strong { color: var(--text-1); font-weight: 500; }
.fineprint { font-family: var(--micro); font-size: 0.74rem; line-height: 1.7; color: var(--text-3); max-width: 60ch; }

/* ══ B2 — CLAIM (exactly one per page) ══════════════════════════════════ */
.claim { padding: var(--claim-y) 1.5rem calc(var(--claim-y) * 0.6); }
.claim > .wrap { display: block; }
.claim .lede { margin-bottom: 2.4rem; }

/* .claim-duo — a claim band with a PORTRAIT beside it (isaac.html, by
   Isaac's order: his page carries his photo). Copy left, portrait right;
   the portrait is a framed exhibit in the same glass language as
   .artifact-frame, 4:5 because a face is not a film. */
.claim-duo > .wrap {
  display: grid; grid-template-columns: 1.55fr 1fr;
  gap: 3rem; align-items: center;
}
.claim-duo .claim-copy, .claim-duo .portrait { min-width: 0; }
.portrait { margin: 0; justify-self: end; width: 100%; max-width: 340px; }
.portrait-frame {
  position: relative; aspect-ratio: 4 / 5; width: 100%;
  border: 1px solid var(--ink); border-radius: var(--r-lg);
  background: var(--paper);
  overflow: hidden;
}
.portrait-frame > img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.portrait figcaption {
  font-family: var(--micro); font-size: 0.7rem; line-height: 1.7;
  color: var(--text-3); margin-top: 0.6rem;
}

/* ══ CTAs ═══════════════════════════════════════════════════════════════ */
.cta-primary, .cta-big {   /* .cta-big = legacy alias */
  font-family: var(--mono); font-size: 0.95rem; font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--ink);
  padding: 0.95rem 1.6rem; border-radius: var(--r-md);
  display: inline-block; cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.cta-primary:hover, .cta-big:hover { background: var(--ink); color: var(--paper); }
.cta-secondary {
  font-size: 0.78rem; color: var(--text-3);
  border-bottom: 1px solid var(--text-3); padding-bottom: 2px; transition: color 0.2s;
}
.cta-secondary:hover { color: var(--mercury-blue); border-color: var(--mercury-blue); }

/* ══ B3 — ARTIFACT (the world slot) ═════════════════════════════════════ */
.artifact { margin: 0; }
.artifact-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
  border: 1px solid var(--ink);
  border-radius: var(--r-lg);
  background: var(--paper);
  overflow: hidden;
}
.artifact-frame > iframe,
.artifact-frame > video,
.artifact-frame > img { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; object-fit: cover; display: block; }
.artifact-frame > svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }

/* The quickstart code exhibit (build.html B3). The class was USED and defined
   nowhere: the <pre> sat unstyled — browser-default monospace, no padding —
   in the top-left corner of an empty 16:9 frame. A six-line install does not
   want 590px of glass around it, so a code-bearing frame drops the film
   aspect and hugs its readout. */
.artifact-frame:has(> pre.artifact-code) { aspect-ratio: auto; }
.artifact-frame > pre.artifact-code {
  margin: 0; padding: 2rem 2.4rem; overflow-x: auto;
}
.artifact-code code {
  font-family: var(--mono); font-size: clamp(0.72rem, 1.4vw, 0.92rem);
  line-height: 2; color: var(--text-1); white-space: pre;
}

/* Inline-SVG diagrams. All colour and type lives HERE so no HTML file ever
   declares a hex value or a font-family (law L4). Diagrams use classes only. */
.diagram .d-ring { fill: none; stroke: var(--glass-border); stroke-width: 1.5; stroke-dasharray: 5 7; }
.diagram .d-arrow { fill: var(--mercury-violet); opacity: 0.85; }
.diagram .d-node { fill: var(--void); stroke: var(--glass-border); stroke-width: 1; }
.diagram .d-hub { fill: rgba(125,211,252,0.06); stroke: rgba(125,211,252,0.35); stroke-width: 1.2; }
.diagram .d-t { fill: var(--text-1); font-family: var(--body); font-size: 13px; font-weight: 600; }
.diagram .d-sub { fill: var(--text-3); font-family: var(--body); font-size: 9.5px; }
.diagram .d-k { fill: var(--mercury-blue); font-family: var(--mono); font-size: 9px; letter-spacing: 0.14em; font-weight: 600; }
.diagram .d-hubt { fill: var(--mercury-teal); font-family: var(--body); font-size: 15px; font-weight: 700; }

/* The placeholder must read as DELIBERATE, not missing: a framed slot with
   a caption and a live destination — never a grey box. */
.artifact-placeholder {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.9rem; text-align: center; padding: 2rem;
  background:
    linear-gradient(135deg, rgba(125,211,252,0.05), rgba(167,139,250,0.04)),
    repeating-linear-gradient(135deg, transparent 0 14px, rgba(255,255,255,0.012) 14px 28px);
  transition: all 0.3s;
}
.artifact-placeholder:hover { background-color: rgba(125,211,252,0.03); }
.artifact-placeholder .ap-line {
  font-family: var(--mono); font-size: 0.82rem; letter-spacing: 0.02em;
  color: var(--mercury-blue); line-height: 1.7; max-width: 44ch;
}
.artifact-placeholder .ap-go {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text-2);
  border: 1px solid var(--glass-border); border-radius: 999px; padding: 0.45rem 1.1rem;
}
.artifact-prov {
  font-family: var(--micro);
  font-size: 0.76rem; line-height: 1.7; color: var(--text-3);
  margin-top: 0.9rem; max-width: 68ch;
}
.artifact-prov a { color: var(--mercury-blue); }
.artifact-prov a:hover { color: var(--text-1); }

/* ══ Glass cards ════════════════════════════════════════════════════════ */
.card, .tier, .panel, .link-card {
  background: var(--glass-bg);
  backdrop-filter: blur(var(--blur)); -webkit-backdrop-filter: blur(var(--blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--r-card);
  padding: 2rem;
  box-shadow: 0 0 0 1px rgba(125,211,252,0.03), 0 8px 32px rgba(0,0,0,0.3), inset 0 1px 0 var(--glass-shine);
  transition: all .4s cubic-bezier(.4,0,.2,1);
  position: relative; overflow: hidden;
}
.card::before, .tier::before, .link-card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(125,211,252,0.25) 20%, rgba(167,139,250,0.35) 50%, rgba(192,192,192,0.25) 80%, transparent);
  animation: shimmer 4s ease-in-out infinite alternate;
}
@keyframes shimmer { 0% { opacity: .4; transform: translateX(-10%); } 100% { opacity: 1; transform: translateX(10%); } }
.card:hover, .tier:hover, .link-card:hover {
  border-color: rgba(125,211,252,0.15);
  box-shadow: 0 0 0 1px rgba(125,211,252,0.08), 0 0 40px rgba(125,211,252,0.06), 0 12px 40px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.08);
  transform: translateY(-2px);
}
.card.highlight, .tier.highlight { border-color: rgba(125,211,252,0.2); background: rgba(125,211,252,0.04); }
.card p, .tier p, .panel p, .link-card p { font-size: 0.84rem; line-height: 1.75; color: var(--text-2); }

/* ══ Grids — auto-fit everywhere so an odd item never orphans ═══════════ */
.cards, .tiers, .steps, .panels, .capabilities {
  display: grid; gap: 1.5rem; margin-top: 2rem;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.panels { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
/* An odd-count panel grid (isaac.html ships seven theses) strands its last
   item alone in the final row at every column count. span-last promotes it
   to a full-width closing panel instead — deliberate cadence, not an orphan. */
.panels.span-last > .panel:last-child { grid-column: 1 / -1; }

/* ══ B5 — MECHANISM ═════════════════════════════════════════════════════ */
.mechanism .steps { margin-top: 2.2rem; }
.step { padding-top: 1.4rem; border-top: 1px solid var(--glass-border); }
.step-num { font-family: var(--mono); font-weight: 700; font-size: 0.85rem; color: var(--mercury-blue); margin-bottom: 0.8rem; }
.step p { font-size: 0.84rem; line-height: 1.75; color: var(--text-2); }

/* Stack layers (7-stack, engine layers) — reused, not reinvented */
.stack { margin-top: 2rem; border-radius: var(--r-card); overflow: hidden; border: 1px solid var(--glass-border); }
.stack-item, .layer { display: flex; align-items: stretch; border-bottom: 1px solid var(--glass-border); background: var(--glass-bg); }
.stack-item:last-child, .layer:last-child { border-bottom: none; }
.stack-num, .layer-name {
  width: 180px; flex-shrink: 0; padding: 1rem 1.5rem;
  font-weight: 600; font-size: 0.85rem;
  display: flex; align-items: center;
  border-right: 1px solid var(--glass-border);
}
.stack-content, .layer-desc { flex: 1; padding: 1rem 1.5rem; font-size: 0.82rem; color: var(--text-2); line-height: 1.65; display: flex; align-items: center; }
.stack-content h4 { font-weight: 600; font-size: 0.85rem; margin-bottom: 0.2rem; }
.stack-content p { font-family: var(--micro); font-size: 0.74rem; color: var(--text-3); }
.stack-bar, .layer-bar { width: 3px; background: linear-gradient(180deg, var(--mercury-blue), var(--mercury-violet)); }

/* Depth 3 is where the site stops being a page and starts being a readout:
   the row labels go mono and lock to a uniform tracking, which is what turns
   a stack of glass cards into a table you scan. */
body[data-depth="3"] .stack-num,
body[data-depth="3"] .layer-name,
body[data-depth="3"] .price-label {
  font-family: var(--mono); font-size: 0.72rem;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-2);
}

/* The compounding-loop band (salvaged from system.html, promoted to shared) */
.loopband {
  border: 1px solid rgba(167,139,250,0.18); border-radius: var(--r-lg);
  padding: 2rem 1.5rem 1.6rem;
  background: linear-gradient(135deg, rgba(167,139,250,0.05), rgba(125,211,252,0.03));
}
.loop { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 0.5rem 0.4rem; }
.loop .lstep { font-size: 0.78rem; color: var(--text-2); background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: 999px; padding: 0.45rem 0.9rem; }
.loop .lstep b { color: var(--mercury-blue); font-weight: 600; }
.loop .sep { color: var(--mercury-violet); font-family: var(--mono); font-size: 0.85rem; }
.loop-return { text-align: center; font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.1em; color: var(--mercury-violet); margin-top: 1rem; }
.loop-note { text-align: center; font-size: 0.86rem; color: var(--text-2); line-height: 1.75; max-width: 62ch; margin: 1.2rem auto 0; }
.loop-note b { color: var(--text-1); }

/* ── The GAUGE — a readout row with a proportion bar. ─────────────────────
   Two posts were hand-building this out of eleven inline declarations per
   row. It is the instrument register RULE 02 §4 asks for, so it gets a real
   block. The fill length is a data-attribute, not a style attribute: the
   proportion is DATA about the row, and keeping it out of style="" is what
   lets the gate stay absolute. Tone classes carry the accent; there is no
   colour here that is not already a token. */
.gauge {
  margin: 2rem 0; padding: 1.5rem;
  background: var(--glass-bg); border: 1px solid var(--glass-border);
  border-radius: var(--r-card);
}
.gauge-k {
  font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--mercury-violet); margin-bottom: 1rem;
}
.gauge-rows { display: grid; gap: 0.4rem; font-family: var(--mono); font-size: 0.75rem; }
.gauge-row { display: flex; gap: 1rem; align-items: center; --gauge-c: var(--mercury-blue); }
.gauge-row.teal { --gauge-c: var(--mercury-teal); }
.gauge-row.violet { --gauge-c: var(--mercury-violet); }
.gauge-n { min-width: 6.5rem; color: var(--gauge-c); }
.gauge-n.wide { min-width: 9rem; }
.gauge-t { color: var(--text-2); }
.gauge-bar { flex: 1; height: 8px; border-radius: 4px; background: var(--gauge-c); }
.gauge-row[data-fill="25"] .gauge-bar { background: linear-gradient(90deg, var(--gauge-c), transparent 25%); }
.gauge-row[data-fill="45"] .gauge-bar { background: linear-gradient(90deg, var(--gauge-c), transparent 45%); }
.gauge-row[data-fill="70"] .gauge-bar { background: linear-gradient(90deg, var(--gauge-c), transparent 70%); }
.gauge-row[data-fill="95"] .gauge-bar { background: linear-gradient(90deg, var(--gauge-c), transparent 95%); }
.gauge figcaption { font-family: var(--micro); font-size: 0.7rem; line-height: 1.7; color: var(--text-3); margin-top: 1rem; }

/* A plain two-ends row (a before/after pair, a from→to) and the small data
   table two posts use. Both were inline; both are hairline-ruled, which is
   the structural line language of RULE 02 §4 — so they get denser with
   depth for free, through --glass-border. */
.split-row {
  display: flex; justify-content: space-between; align-items: center;
  gap: 1rem; width: 100%; padding-top: 1.5rem;
  border-top: 1px solid var(--glass-border);
}
.data-table {
  width: 100%; border-collapse: collapse;
  font-size: 0.85rem; margin: 1rem 0;
}
.data-table .cell,
td.cell, th.cell {
  padding: 0.4rem 0.6rem; text-align: left;
  border-bottom: 1px solid var(--glass-border);
}
/* The catalog wall (patterns.html) is 57 rows of the table above, which is
   the first time a data table has had to carry links and a provenance
   column. Two declarations rather than 57 inline ones. NO new colours and
   no new vocabulary: the link colour is the identical declaration `.note a`
   already carries, and the last column is the "runs in" label — the mono
   receipt voice that `.eyebrow` and `.receipt-k` already speak in, not the
   prose voice. Left uncoloured so `.data-table a` still wins inside it. */
.data-table a { color: var(--mercury-blue); }
.data-table a:hover { color: var(--text-1); }
.data-table td:last-child { font-family: var(--micro); font-size: 0.78rem; }

/* ── The architecture entry (patterns.html v2). ────────────────────────────
   patterns.html stopped being a 57-row table and became 25 named system
   STRUCTURES, each carrying a diagram. A table row cannot hold a diagram, so
   the row became an entry: name, one sentence, the structure, the moves, the
   receipt. ZERO new colours and zero new type: `.arch-dia` is `.note pre`'s
   declaration set (the glass-panelled readout that already carries every
   code exhibit on the site), `.arch-runs` is the `.receipt-k` mono label
   voice, and the hairline between entries is `--glass-border`, the
   structural line language of RULE 02 §4 — so the whole block densifies with
   the depth gradient for free. The ASCII in `.arch-dia` is deliberate: the
   self-hosted mono is subset to latin-1, so box-drawing glyphs would fall
   back to a system face mid-diagram and break the grid. */
.arch { border-top: 1px solid var(--glass-border); padding: 1.7rem 0 0.4rem; }
.arch:first-of-type { border-top: 0; padding-top: 0.6rem; }
.arch h3 { font-size: 1.02rem; margin-bottom: 0.45rem; }
.arch-what {
  font-size: 0.92rem; line-height: var(--lh-prose);
  color: var(--text-2); max-width: 70ch; margin-bottom: 1.1rem;
}
.arch-dia {
  background: var(--glass-bg); border: 1px solid var(--glass-border);
  border-radius: var(--r-md); padding: 1.1rem 1.25rem;
  overflow-x: auto; margin: 0 0 1.1rem;
}
.arch-dia code {
  display: block; font-family: var(--mono); font-size: 0.71rem;
  line-height: 1.75; color: var(--text-1); white-space: pre;
}
.arch .fig { margin: 0 0 1.1rem; }
.arch-moves { list-style: none; margin: 0 0 0.9rem; padding: 0; max-width: 74ch; }
.arch-moves li {
  position: relative; padding-left: 1.05rem; margin-bottom: 0.3rem;
  font-size: 0.83rem; line-height: 1.7; color: var(--text-2);
}
.arch-moves li::before {
  content: "\2014"; position: absolute; left: 0; color: var(--text-3);
}
.arch-moves b { color: var(--text-1); font-weight: 600; }
.arch-runs {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-3);
}
.arch-runs a { color: var(--mercury-blue); }
.arch-runs a:hover { color: var(--text-1); }

/* The closing disciplines band: 57 name+one-liner entries, dense, secondary.
   A definition list rather than a table because there is no second column
   worth ruling — the receipt rides at the end of the line in the same mono
   label voice `.arch-runs` uses. */
.disc { margin: 1.2rem 0 0; padding: 0; list-style: none; max-width: 82ch; }
.disc li {
  font-size: 0.81rem; line-height: 1.72; color: var(--text-2);
  padding: 0.42rem 0; border-bottom: 1px solid var(--glass-border);
}
.disc b { color: var(--text-1); font-weight: 600; }
.disc .disc-in {
  font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-3); white-space: nowrap;
}
.disc .disc-in a { color: var(--mercury-blue); }
.disc .disc-in a:hover { color: var(--text-1); }

/* .post-nav — the closing block on 40 posts. It was USED forty times and
   DEFINED nowhere: the class did nothing and each post re-declared its
   layout inline. Defined once, here. */
/* Every one of the 40 sits inside <article class="note">, which already owns
   the measure — so this carries layout only, no second width constraint. */
.post-nav {
  display: flex; flex-direction: column; align-items: center;
  gap: 1.5rem; text-align: center; margin-top: 2rem;
}

/* .duo — the before/after comparison two posts build by hand. The negative
   and positive keys use --mercury-rose and --ok, which are already tokens;
   they replace a #cc4444 / #44cc44 pair that existed in no palette. */
.duo { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; margin: 2rem 0; }
.duo .k-neg { color: var(--mercury-rose); margin-bottom: 0.5rem; }
.duo .k-pos { color: var(--ok); margin-bottom: 0.5rem; }

/* .railed — a left-ruled aside (a question to ask, a check to run). The
   sibling of .caveat, which is a fully-boxed one. */
.railed {
  background: var(--glass-bg); border-left: 3px solid var(--mercury-violet);
  padding: 1.5rem; margin: 2rem 0;
}
.callout {
  margin: 2.5rem 0; padding: 1.5rem; text-align: center;
  border: 1px solid var(--glass-border); border-radius: var(--r-card);
}
.fig-wide { margin: 2rem auto; max-width: 900px; text-align: center; }

/* .badge — the inline [RUNS TODAY] / [ROADMAP] marker one post uses to keep
   its own claims honest paragraph by paragraph. Exactly the mono micro-label
   register of RULE 02 §4, so it belongs in the stylesheet, not inline. It is
   NOT live-data styling: no pulse, no glow. */
.badge {
  font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.06em;
  color: var(--mercury-blue);
}
.badge.roadmap { color: var(--mercury-violet); }

/* ══ B6 — LADDER (the IA spine, rendered as content) ════════════════════ */
.ladder { display: block; max-width: 780px; margin-top: 2rem; }
.rung {
  display: flex; align-items: center; gap: 1.1rem;
  padding: 0.95rem 1.3rem;
  border: 1px solid var(--glass-border); border-radius: var(--r-sm);
  margin-bottom: 0.6rem; background: var(--glass-bg);
  transition: all 0.25s;
}
a.rung:hover { border-color: rgba(125,211,252,0.25); background: rgba(125,211,252,0.04); transform: translateX(3px); }
.rung[aria-current="step"] {
  border-color: rgba(94,234,212,0.35); background: rgba(94,234,212,0.05);
}
.rung[aria-current="step"] .rung-n { color: var(--mercury-teal); }
.rung-n { font-family: var(--mono); font-size: 0.72rem; color: var(--mercury-blue); flex-shrink: 0; width: 1.6rem; }
.rung-t { font-weight: 600; font-size: 0.9rem; flex-shrink: 0; color: var(--text-1); }
.rung-d { font-family: var(--micro); font-size: 0.77rem; color: var(--text-3); margin-left: auto; text-align: right; }

/* ══ B4 — RECEIPTS (4-up → 2-up → 1-up; never orphan a 4th card) ═══════ */
.receipts { padding: var(--sec-y) 1.5rem; }
/* The section blurb. It was only ever defined INSIDE .receipts, so the same
   class on the corpus-index tag sections rendered as unstyled body copy —
   unscoped base first, the .receipts variant unchanged below it. */
.receipts-lede { font-size: calc(var(--lede-size) - 0.04rem); color: var(--text-2); max-width: 62ch; line-height: var(--lh-lede); margin-bottom: 1.4rem; }
.receipts .receipts-lede { font-size: calc(var(--lede-size) - 0.04rem); color: var(--text-2); max-width: 62ch; line-height: var(--lh-lede); margin-bottom: 0.5rem; }
.receipts .receipts-lede a { color: var(--mercury-blue); }
.receipts-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.3rem; margin-top: 2rem; }
.receipts.compact .receipts-grid { grid-template-columns: repeat(2, 1fr); }
.receipt {
  display: block; background: var(--glass-bg); border: 1px solid var(--glass-border);
  border-radius: var(--r-md); padding: 1.4rem; transition: all 0.3s;
}
.receipt:hover { border-color: rgba(125,211,252,0.2); transform: translateY(-2px); }
.receipt-k { font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--mercury-teal); margin-bottom: 0.6rem; }
.receipt h3 { font-size: 0.95rem; margin-bottom: 0.5rem; }
.receipt p { font-size: 0.78rem; line-height: 1.65; color: var(--text-2); }
.receipt p b { color: var(--text-1); font-weight: 600; }
.receipt-go { font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--mercury-blue); margin-top: 0.8rem; display: block; }

/* ══ B7 — HONEST (mandatory on every RUNG) ═════════════════════════════ */
.honest {
  background: linear-gradient(135deg, rgba(167,139,250,0.08), rgba(125,211,252,0.05));
  border-top: 1px solid rgba(167,139,250,0.15);
  border-bottom: 1px solid rgba(167,139,250,0.15);
  padding: var(--sec-y) 1.5rem;
}
.honest .eyebrow, .honest .tag { color: var(--mercury-violet); }
.honest .eyebrow::before, .honest .tag::before { background: var(--mercury-violet); }
.honest h2 { color: var(--text-1); }
.honest p { font-size: calc(var(--lede-size) + 0.01rem); line-height: var(--lh-prose); max-width: 62ch; color: var(--text-2); margin-bottom: 1rem; }
.honest p:last-child { margin-bottom: 0; }

/* ══ B8 — PRICE ════════════════════════════════════════════════════════ */
.price-block {
  border: 1px solid var(--glass-border); border-radius: var(--r-lg);
  padding: 2.2rem; background: var(--glass-bg);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  max-width: 620px;
}
/* A .pricing-table inside the 620px block left its description column ~176px
   wide — three words a line on learn.html, worse on run.html's five rows.
   .wide is for a price-block that CARRIES a table. */
.price-block.wide { max-width: 880px; }
.price-block .anchor {
  font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.06em;
  color: var(--text-3); margin-bottom: 0.6rem;
}
.price-block .anchor s { color: var(--text-3); opacity: 0.7; }
.price-block .price {
  font-weight: 700; font-size: clamp(1.9rem, 4vw, 2.6rem); line-height: 1.1; margin-bottom: 0.4rem;
  background: linear-gradient(135deg, var(--mercury-blue), var(--mercury-teal));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.price-block .price .period { font-size: 0.8rem; -webkit-text-fill-color: var(--text-3); font-weight: 400; }
.price-block ul { list-style: none; font-size: 0.82rem; line-height: 2.1; color: var(--text-2); margin-top: 1rem; }
.price-block ul li::before { content: '— '; color: var(--mercury-blue); opacity: 0.6; }

/* Price table rows (the price-stack device) */
.pricing-table { margin-top: 2rem; border-radius: var(--r-card); overflow: hidden; border: 1px solid var(--glass-border); }
.price-row {
  display: flex; align-items: stretch;
  border-bottom: 1px solid var(--glass-border);
  background: var(--glass-bg); backdrop-filter: blur(8px); transition: all 0.3s;
}
.price-row:last-child { border-bottom: none; }
.price-row:hover { background: rgba(255,255,255,0.05); }
.price-row.highlight { background: rgba(125,211,252,0.04); }
.price-label { width: 220px; flex-shrink: 0; padding: 1.4rem; font-weight: 600; font-size: 0.9rem; display: flex; align-items: center; border-right: 1px solid var(--glass-border); }
.price-amount {
  width: 180px; flex-shrink: 0; padding: 1.4rem;
  font-family: var(--mono); font-weight: 700; font-size: 1.25rem;
  background: linear-gradient(135deg, var(--mercury-blue), var(--mercury-teal));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
  display: flex; align-items: center; border-right: 1px solid var(--glass-border);
}
.price-amount .period { font-size: 0.7rem; -webkit-text-fill-color: var(--text-3); font-weight: 400; margin-left: 0.3rem; }
.price-desc { flex: 1; padding: 1.4rem; font-size: 0.8rem; color: var(--text-2); line-height: 1.6; display: flex; align-items: center; }

/* Tier card internals (retained) */
.tier-label { font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.15em; text-transform: uppercase; color: var(--mercury-blue); margin-bottom: 0.5rem; }
.tier-name { font-weight: 700; font-size: 1.2rem; margin-bottom: 0.3rem; }
.tier-price {
  font-weight: 700; font-size: 2rem; margin-bottom: 0.3rem;
  background: linear-gradient(135deg, var(--mercury-blue), var(--mercury-teal));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.tier-price .period { font-size: 0.8rem; -webkit-text-fill-color: var(--text-3); font-weight: 400; }
.tier-min { font-size: 0.7rem; color: var(--text-3); margin-bottom: 1.4rem; }
.tier ul { list-style: none; font-size: 0.8rem; line-height: 2.1; color: var(--text-2); }
.tier ul li::before { content: '— '; color: var(--mercury-blue); opacity: 0.5; }
.tier-note { font-size: 0.74rem; color: var(--mercury-teal); margin-top: 1.4rem; border-top: 1px solid var(--glass-border); padding-top: 1rem; }
/* The price sheet (pricing.html) — the old page hand-built these as inline
   styles on a page-local .price-sheet. Same sheet, real tokens: a stacked
   single column of .tier cards at reading measure. */
.tier-sheet { max-width: 780px; }
.tier-sheet .tier { margin-bottom: 1.5rem; }
.tier-sheet .eyebrow { margin-top: 2.6rem; }
.tier-sheet .eyebrow:first-child { margin-top: 0; }
.tier .anchor { font-size: 0.8rem; color: var(--text-2); line-height: 1.7; margin: 0.3rem 0; }
.tier a { color: var(--mercury-blue); }
.tier a:hover { color: var(--text-1); }
.tier-badge {
  position: absolute; top: 0; right: 0;
  background: linear-gradient(135deg, var(--mercury-blue), var(--mercury-violet));
  color: var(--void); font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 0.4rem 1rem; border-radius: 0 var(--r-lg) 0 var(--r-sm);
}

/* Guarantee */
.guarantee {
  margin-top: 2rem; border: 1px solid rgba(125,211,252,0.2); padding: 2.2rem;
  text-align: center; border-radius: var(--r-lg);
  background: rgba(125,211,252,0.03); backdrop-filter: blur(12px);
}
.guarantee h3 {
  font-weight: 700; font-size: 1.25rem; margin-bottom: 0.8rem;
  background: linear-gradient(135deg, var(--mercury-blue), var(--mercury-teal));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}
.guarantee p { font-size: 0.86rem; color: var(--text-2); line-height: 1.8; max-width: 58ch; margin: 0 auto; }

/* ══ B9 — FINAL CTA (one link, one destination) ════════════════════════ */
.final-cta { text-align: center; padding: calc(var(--sec-y) * 1.3) 1.5rem; }
.final-cta h2 { margin: 0 auto 1.4rem; max-width: none; text-align: center; }
.final-cta p { font-size: 0.92rem; color: var(--text-2); margin: 0 auto 1.6rem; max-width: 58ch; line-height: 1.8; }
.final-cta .eyebrow, .final-cta .tag { justify-content: center; }

/* ══ NOTE (type 4 article body) ════════════════════════════════════════ */
.note { max-width: 68ch; margin: 0 auto; padding: calc(var(--sec-y) * 0.9) 1.5rem; }
.note h1 { max-width: none; }
.note .dek { font-size: calc(var(--lede-size) + 0.06rem); line-height: var(--lh-lede); color: var(--text-2); margin-bottom: 2.5rem; }
.note p { font-size: calc(var(--lede-size) + 0.01rem); line-height: var(--lh-prose); color: var(--text-2); margin-bottom: 1.4rem; }
.note h2 { margin-top: 2.8rem; }
.note h3 { margin-top: 2rem; }
.note ul, .note ol { color: var(--text-2); font-size: calc(var(--lede-size) + 0.01rem); line-height: var(--lh-prose); margin: 0 0 1.4rem 1.2rem; }
.note a { color: var(--mercury-blue); }
.note a:hover { color: var(--text-1); }
/* Pull-quote — display-serif slot 3 (RULE 02 §2). The synthetic oblique the
   italic declaration used to force is gone: Fraunces ships roman only, and a
   slanted roman is the cheapest-looking thing a serif can do. */
.note blockquote {
  border-left: 2px solid var(--mercury-violet); padding-left: 1.3rem;
  margin: 2rem 0; color: var(--text-1);
  font-family: var(--display); font-weight: 400;
  font-size: 1.08rem; line-height: 1.6; letter-spacing: -0.005em;
}
.note code { font-family: var(--mono); font-size: 0.86em; color: var(--mercury-teal); background: var(--glass-bg); padding: 0.1em 0.35em; border-radius: 4px; }
.note pre { background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--r-md); padding: 1.2rem; overflow-x: auto; margin-bottom: 1.4rem; }
.note pre code { background: none; padding: 0; color: var(--text-1); }
.note .caveat {
  border: 1px solid rgba(167,139,250,0.2); background: rgba(167,139,250,0.04);
  border-radius: var(--r-md); padding: 1.3rem 1.5rem; margin: 2rem 0;
  font-size: 0.88rem; line-height: 1.8; color: var(--text-2);
}
.note-meta { font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.08em; color: var(--text-3); margin-bottom: 2rem; }
.note-next {
  display: block; max-width: 68ch; margin: 0 auto; padding: 1.4rem 1.5rem;
  border-top: 1px solid var(--glass-border); font-size: 0.85rem; color: var(--text-2);
}
.note-next b { color: var(--mercury-blue); }

/* ══ Status box ════════════════════════════════════════════════════════ */
.status {
  border: 1px solid rgba(125,211,252,0.15); padding: 1.8rem; border-radius: var(--r-lg);
  background: rgba(125,211,252,0.03); margin-bottom: 2rem; backdrop-filter: blur(12px);
}
.status h3 { color: var(--mercury-blue); font-size: 1.1rem; margin-bottom: 0.6rem; }
.status p { font-size: 0.84rem; color: var(--text-2); line-height: 1.7; }

/* ══ B10 — FOOTER ══════════════════════════════════════════════════════ */
/* The hairline runs FULL-BLEED (the nav's does; a border that stopped at the
   1100px container made the page's top and bottom rules disagree). Content
   still aligns to the container via the max() padding. */
.footer {
  padding: 2rem max(1.5rem, calc((100% - var(--wrap)) / 2)) 4rem;
  border-top: 1px solid var(--glass-border);
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  font-family: var(--micro); font-size: 0.72rem; color: var(--text-3); flex-wrap: wrap;
}
.footer .tagline { font-style: italic; }
.footer a { color: var(--text-3); border-bottom: 1px solid transparent; }
.footer a:hover { color: var(--mercury-violet); border-color: var(--mercury-violet); }

/* ══ BREAKPOINT 2 — 900px : grids → 2 col, nav → hamburger ═════════════ */
@media (max-width: 900px) {
  /* The gradient SURVIVES the breakpoint. Flattening --sec-y to one mobile
     value here would delete the depth cue on every phone, which is where
     most of the descent actually happens — so each depth just shrinks. */
  body { --sec-y: 3.5rem; --claim-y: 3rem; }
  body[data-depth="0"] { --sec-y: 4.6rem; --claim-y: 4.2rem; }
  body[data-depth="2"] { --sec-y: 3rem; --claim-y: 2.6rem; }
  body[data-depth="3"] { --sec-y: 2.4rem; --claim-y: 2.2rem; }
  section, .honest, .receipts, .claim { padding-left: 1.25rem; padding-right: 1.25rem; }
  .receipts-grid { grid-template-columns: repeat(2, 1fr); }
  .price-row { flex-direction: column; }
  .price-label, .price-amount { width: 100%; border-right: none; border-bottom: 1px solid var(--glass-border); }
  .stack-item, .layer { flex-direction: column; }
  .stack-num, .layer-name { width: 100%; border-right: none; border-bottom: 1px solid var(--glass-border); }
  /* In the stacked direction the 3px accent column collapsed to an invisible
     0-height sliver; it becomes the row's top accent line instead. */
  .stack-bar, .layer-bar { width: 100%; height: 3px; background: linear-gradient(90deg, var(--mercury-blue), var(--mercury-violet)); }
  .claim-duo > .wrap { grid-template-columns: 1fr; gap: 2.2rem; }
  .portrait { justify-self: start; max-width: 280px; }
  .final-cta { padding: calc(var(--sec-y) * 1.2) 1.25rem; }
  .footer { flex-direction: column; text-align: center; padding: 2rem 1.25rem 3rem; }
  .nav, .header { padding: 1rem 1.25rem; }

  /* Hamburger — only ever active once nav.js has stamped data-nav */
  .nav[data-nav] .nav-toggle, .header[data-nav] .nav-toggle { display: block; }
  .nav[data-nav="collapsed"] .nav-links,
  .header[data-nav="collapsed"] .nav-links { display: none; }
  .nav[data-nav="open"] .nav-links, .header[data-nav="open"] .nav-links {
    display: flex; flex-direction: column; align-items: flex-start;
    gap: 1rem; width: 100%; margin-left: 0; padding-top: 1rem;
    border-top: 1px solid var(--glass-border);
  }
  .nav-links { margin-left: 0; }
  .rung-d { display: none; }
  .rung { flex-wrap: wrap; }
}

/* ══ BREAKPOINT 3 — 600px : grids → 1 col ══════════════════════════════ */
@media (max-width: 600px) {
  .receipts-grid,
  .receipts.compact .receipts-grid,
  .cards, .tiers, .steps, .panels, .capabilities,
  .duo { grid-template-columns: 1fr; }
  .gauge-row { flex-wrap: wrap; }
  .gauge-n, .gauge-n.wide { min-width: 100%; }
  h1 { max-width: none; }
  .price-block { padding: 1.6rem; }
  .note { padding: calc(var(--sec-y) * 0.75) 1.25rem; }
}

/* ══ Motion & contrast preferences ═════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  html { scroll-behavior: auto; }
}

/* ══════════════════════════════════════════════════════════════════════
   THE DOOR — index.html band 0. B2 .claim ⊕ B3 .artifact, FUSED.

   Why a new band exists at all (RULE 01 mechanic 1): the door must open
   INTO something alive. Stacked as two bands, the claim occupies the first
   screen and the running artifact falls below the fold — the visitor reads
   a sentence ABOUT the engine instead of watching it. This band puts them
   side by side so the claim is cashed by the thing next to it.

   It composes existing blocks and introduces nothing new: the stage is a
   plain .artifact-frame, the copy column is .eyebrow/h1/.lede/.cta-primary,
   and every value below is an existing custom property. No new colour, no
   new font-family, no fourth breakpoint.

   DEVIATION RECORDED (THE DOOR SPEC §1 asked for the stack to flip at
   1000px): this stylesheet declares three breakpoints ONCE — 1100 / 900 /
   600 — so the flip reuses 900 rather than minting a fourth. Between 900
   and 1000 the two columns are tight but not broken.
   ══════════════════════════════════════════════════════════════════════ */
.door { padding: calc(var(--claim-y) * 0.72) 1.5rem calc(var(--claim-y) * 0.6); }
.door > .wrap {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  gap: 2.75rem;
  align-items: center;
  /* First screen: the stage AND the CTA must both be visible without
     scrolling, or mechanic 1 is only half-served. svh where supported, so a
     mobile URL bar cannot push the CTA under the fold. The allowance is the
     door band's own padding — THE DOOR IS CHROMELESS now (ratchet law,
     Isaac 2026-08-07: index is a one-way entrypoint, so it carries no nav;
     its sanctioned moves are all in the page). */
  min-height: calc(100vh - 8rem);
  min-height: calc(100svh - 8rem);
}
/* Grid children default to min-width:auto and refuse to shrink below their
   content — which is what makes a 16:9 embed blow the column out. */
.door-stage, .door-copy { min-width: 0; }
.door-copy .eyebrow { margin-bottom: 1.1rem; }
/* THE LOPSIDEDNESS FIX (Isaac's word). Depth-0's h1 tops out at 5.4rem —
   sized for a full-width claim, not for the door's ~28rem copy column, where
   it wrapped to a near-word-per-line tower ~2x the stage's height and the
   engine read as a postage stamp beside it. The door's claim is a COLUMN
   claim: it caps lower here so the two columns weigh the same. This is a
   band-composition rule, not a hole in the density gradient — the door is
   still the largest type on the site. */
.door-copy h1 { max-width: 15ch; font-size: clamp(2.6rem, 4.8vw, 4.2rem); }
.door-copy .lede { max-width: 46ch; margin-bottom: 1.9rem; }
.door .artifact-prov { max-width: none; }

/* The consent line. It is the door's smallest type and its most load-bearing
   sentence: the door invites, it never traps (RULE 01 register). */
.door-mini {
  font-family: var(--micro);
  font-size: 0.74rem; line-height: 1.75; color: var(--text-3);
  max-width: 46ch; margin-top: 1.5rem;
}
.door-mini a { color: var(--text-2); border-bottom: 1px solid var(--glass-border); }
.door-mini a:hover { color: var(--mercury-blue); border-color: var(--mercury-blue); }

/* THE ENTER AFFORDANCE IS NOT HERE — ON PURPOSE. It lives INSIDE the iframe
   (the lab's own #door-enter, shown only under ?embed=door), which is the
   spec's frame contract: this page owns the frame, the lab owns everything
   inside it, and neither reskins the other. An interim parent-side overlay
   existed while the door mode was still landing; it was REMOVED the moment
   ?embed=door shipped, because an overlay would swallow the exhibit's own
   hover affordance — reskinning it by stealth, which is the one thing the
   world-template law forbids. Do not re-add a .door-veil. */

@media (max-width: 900px) {
  /* .door (0,1,0) outranks the breakpoint's `section` (0,0,1) side-padding
     rule, so the door has to restate it or it sits 0.25rem wider than every
     other band on the page. */
  .door { padding: calc(var(--claim-y) * 0.55) 1.25rem calc(var(--claim-y) * 0.6); }
  .door > .wrap { grid-template-columns: 1fr; gap: 2rem; min-height: 0; }
  .door-stage { order: -1; }     /* the artifact stays FIRST when it stacks */
  .door-copy h1, .door-copy .lede, .door-mini { max-width: none; }
}

/* MERGE NOTE (door builder 1): a second, near-identical .door block was
   appended here by a parallel writer. Two blocks for one band is the exact
   defect this stylesheet exists to prevent, so the two were folded into the
   single block above — keeping that block's calc(100svh - 9rem) first-screen
   sizing and its inset focus outline. Nothing was lost; do not re-append. */

/* ══ .deck — the VSL hero on ai-transformation.html ═══════════════════════
   The deck stands in for the video until one is cut. The slides are NOT all
   16:9, and .artifact-frame is 16/9 with object-fit: cover — right for video,
   wrong here: the obstacles slide is 1524x1600, so cover cut ~46% off it.
   So: contain, and the frame takes the CURRENT slide's ratio (the script sets
   --deck-ar from each image's width/height). No transition on aspect-ratio —
   it does not interpolate reliably and leaves the frame on the old ratio.
   With JS off, slide 1 shows and the controls stay hidden. */
.deck { aspect-ratio: var(--deck-ar, 16 / 9); }
.deck .deck-slide { object-fit: contain; }
.deck-slide { opacity: 0; visibility: hidden; transition: opacity .25s; }
.deck-slide:first-child { opacity: 1; visibility: visible; }
.deck.is-live .deck-slide { opacity: 0; visibility: hidden; }
.deck.is-live .deck-slide.is-on { opacity: 1; visibility: visible; }
.deck-nav { display: none; align-items: center; gap: 1rem; margin-top: 0.8rem; }
.deck-nav.is-live { display: flex; }
.deck-btn {
  font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-2);
  background: var(--glass-bg); border: 1px solid var(--glass-border);
  border-radius: var(--r-sm); padding: 0.5rem 1rem; cursor: pointer;
  transition: all .2s;
}
.deck-btn:hover { color: var(--text-1); border-color: rgba(125,211,252,0.25); }
.deck-count { font-family: var(--micro); font-size: 0.72rem; color: var(--text-3); }

/* ══ .lead-form — the form on ai-transformation.html ══════════════════════
   Five fields; revenue is the only gate. Plain GET to cal.com so it works
   with JavaScript disabled. No new colours and no new type: the fields reuse
   --glass-bg/--glass-border and the labels are the --micro label voice. */
.lead-form { max-width: 620px; }
.lead-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.lead-field { display: block; margin-bottom: 1rem; }
.lead-field > span {
  display: block; font-family: var(--micro); font-size: 0.72rem;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-3); margin-bottom: 0.4rem;
}
.lead-field input, .lead-field select {
  width: 100%; font-family: inherit; font-size: 0.9rem; color: var(--text-1);
  background: var(--glass-bg); border: 1px solid var(--glass-border);
  border-radius: var(--r-sm); padding: 0.75rem 0.9rem; transition: border-color .2s;
}
.lead-field input::placeholder { color: var(--text-3); }
.lead-field input:focus, .lead-field select:focus { outline: none; border-color: var(--ink); }
.lead-field select option { background: var(--void); color: var(--text-1); }
.lead-form .cta-primary { cursor: pointer; margin-top: 0.6rem; }
@media (max-width: 900px) { .lead-row { grid-template-columns: 1fr; } }

/* .vsl — the video in the landing page's hero slot. A 16:9 film fills the
   frame, so unlike .deck (whose slides were not all 16:9) it needs no
   per-item ratio; object-fit: cover would crop, so it is contain. */
.artifact-frame.vsl > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: var(--void); }

/* .book-duo — the form, and the person you are booking, side by side
   (Isaac's order, 2026-09-16: the right half of the form band was empty).
   ZERO new colours, type or blocks: the rail is the existing .portrait
   exhibit plus the .eyebrow label voice and .lede prose, and the grid is
   .claim-duo's shape with the portrait stacked ABOVE the copy instead of
   beside it — a rail is a column, not a duo.
   ⛔ THE COLLAPSE RULE FOLLOWS THE BASE RULE, DELIBERATELY. A
   grid-template-columns written AFTER the 900px breakpoint wins over it at
   equal specificity and silently defeats the collapse — that is exactly how
   the hero shipped as an unreadable 13-line h1 earlier today.
   ⛔ AND THE RAIL IS A BYLINE, NOT A TOWER. Shipped first as a 260px poster
   portrait over three paragraphs: 785px of rail beside a 374px form — 411px
   of dead space, measured, which is what Isaac saw. The portrait is now 92px
   inline with the name, and the copy is two paragraphs, so the rail's height
   is of the form's order. Re-measure the pair after any copy change. */
.book-duo {
  display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(280px, 1fr);
  gap: 3.5rem; align-items: start;
}
.book-duo .book-form, .book-duo .booker { min-width: 0; }
.booker-id { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem; }
.booker-id .portrait-frame { width: 92px; flex: none; border-radius: var(--r-sm); }
.booker-who { margin: 0; line-height: 1.45; }
.booker-who strong { display: block; color: var(--text-1); font-weight: 500; font-size: 1.05rem; }
.booker-who span {
  font-family: var(--micro); font-size: 0.72rem;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-3);
}
.booker .eyebrow { margin-bottom: 0.8rem; }
.booker .lede { font-size: 0.92rem; line-height: 1.75; margin-bottom: 0.9rem; }
.booker .lede:last-child { margin-bottom: 0; }
@media (max-width: 900px) {
  .book-duo { grid-template-columns: 1fr; gap: 2.4rem; }
  /* The form is the point of the page, so on a phone the rail goes BELOW it
     rather than pushing the fields down a screen. */
  .booker { order: 2; }
}

/* .hero-duo — the landing page's hero band: the FILM and the PERSON, side by
   side. Isaac, 2026-09-19: "my photo needs to be the thing on the site."
   It is .book-duo's exact grid, with one difference that is the whole point:
   the portrait is FULL WIDTH of its column rather than the 92px byline thumb,
   because here the photo is a primary element and not a signature on a form.
   ZERO new colours, type or blocks — .portrait-frame, .eyebrow and .lede are
   the existing vocabulary, and the ratio comes from .portrait-frame itself.
   ⛔ The collapse rule follows the base rule, deliberately — same trap
   .book-duo documents above: a grid-template-columns written after the
   breakpoint wins at equal specificity and silently defeats the collapse. */
/* ⛔ THE FILM AND THE PERSON ARE STACKED, NOT PAIRED SIDE BY SIDE.
   They were two columns of one grid row, and that arrangement CANNOT balance:
   a 16:9 film in a 631px column is 355px tall, while a full-width portrait
   plus two paragraphs is ~898px. Top-aligned, that left a 545px hole under
   the film; centred, it left 271px above AND 272px below it. Both measured,
   both wrong — the geometry has no good value because the premise is wrong.

   Stacked, each element gets the width it deserves: the FILM runs the full
   container and becomes 1100×619 — the dominant object on the page, which is
   what a page built around a VSL is for — and the PERSON sits beneath it as a
   portrait-and-bio row. Isaac's ruling ("my photo needs to be the thing on
   the site") is kept: the portrait is large and immediately under the film,
   not shrunk to a byline thumb. */
.hero-duo { margin-top: 2.5rem; }
.hero-duo > * { min-width: 0; }
.hero-duo .vsl { width: 100%; }

.hero-duo .booker {
  display: grid; grid-template-columns: 260px minmax(0, 1fr);
  gap: 0 2.5rem; align-items: start; margin-top: 2.5rem;
}
.hero-duo .booker > * { grid-column: 2; }
/* ⛔ `grid-row: 1 / -1` DOES NOT WORK HERE. The bio's rows are IMPLICIT (no
   grid-template-rows), and `-1` only names the last EXPLICIT line — so the
   portrait landed in row 1 alone, sized that row to its own 325px, and shoved
   the bio text 230px down the column. Span a count instead. */
.hero-duo .portrait-frame {
  grid-column: 1; grid-row: 1 / span 20;
  width: 100%; border-radius: var(--r-card); margin-bottom: 0;
}
.hero-duo .booker-who { margin-bottom: 1.5rem; }
.hero-duo .lede { font-size: 0.95rem; line-height: 1.75; margin-bottom: 0.9rem; }
.hero-duo .lede:last-child { margin-bottom: 0; }
@media (max-width: 900px) {
  /* On a phone the film still leads and the person still follows — that is
     now the desktop order too, so only the portrait row has to collapse. */
  .hero-duo .booker { grid-template-columns: 1fr; gap: 1.5rem; }
  .hero-duo .booker > * { grid-column: 1; }
  .hero-duo .portrait-frame { grid-row: auto; max-width: 260px; }
}

/* .vsl-ui — the film's own controls. ⛔ THE NATIVE BAR PRINTS THE RUNTIME and
   no attribute suppresses it (controlsList has no token for the time display),
   so vsl.js removes `controls` and draws these instead: play/pause, a seek
   line, mute, fullscreen — and NO CLOCK. Isaac, 2026-09-18: "why the fuck
   would you tell them how long the VSL is".
   ZERO new colours or type: --text-1/-2, --mercury-blue, --glass-border and
   --r-sm are all existing tokens. If JS is off the <video> keeps its own
   `controls` and the film still plays. */
.artifact-frame.vsl.vsl-custom > video { cursor: pointer; }
.vsl-ui {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.7rem 0.9rem;
  background: linear-gradient(to top, rgba(2,4,10,0.92), rgba(2,4,10,0));
  opacity: 0; transition: opacity 160ms ease;
}
.artifact-frame.vsl:hover .vsl-ui,
.artifact-frame.vsl:focus-within .vsl-ui,
.artifact-frame.vsl:not(.is-playing) .vsl-ui { opacity: 1; }
.vsl-ui button {
  flex: none; width: 30px; height: 30px; padding: 0;
  display: grid; place-items: center;
  background: none; border: 0; cursor: pointer; color: rgba(255,255,255,0.8);
  transition: color 120ms ease;
}
.vsl-ui button:hover { color: var(--paper); }
.vsl-ui svg { width: 20px; height: 20px; fill: currentColor; }
.vsl-icon-pause, .vsl-icon-quiet { display: none; }
.is-playing .vsl-icon-play { display: none; }
.is-playing .vsl-icon-pause { display: block; }
.is-muted .vsl-icon-loud { display: none; }
.is-muted .vsl-icon-quiet { display: block; }
.vsl-track {
  flex: 1 1 auto; height: 4px; border-radius: var(--r-sm); cursor: pointer;
  background: rgba(255,255,255,0.3); position: relative; touch-action: none;
}
.vsl-track:focus-visible { outline: 2px solid var(--paper); outline-offset: 4px; }
.vsl-fill {
  position: absolute; inset: 0 auto 0 0; width: 0;
  background: var(--paper); border-radius: var(--r-sm);
}
/* "Watch with sound" — the silent autoplay's one way in: a paper button on
   the film's centre that inverts to ink on hover, the same button the page
   uses everywhere (RULE 02 §7). While the film is a silent preview its own
   controls stay hidden, so the only thing to press is this. */
.vsl-sound {
  position: absolute; left: 50%; top: 50%; z-index: 3; transform: translate(-50%, -50%);
  display: inline-flex; align-items: center; gap: 0.6rem;
  padding: 0.95rem 1.5rem; border: 1px solid var(--ink); border-radius: 14px;
  background: var(--paper); color: var(--ink); cursor: pointer;
  font-family: var(--mono); font-weight: 600; font-size: 0.95rem; white-space: nowrap;
  transition: background 160ms ease, color 160ms ease;
}
.vsl-sound:hover, .vsl-sound:focus-visible { background: var(--ink); color: var(--paper); }
.vsl-sound svg { width: 20px; height: 20px; fill: currentColor; }
@media (max-width: 600px) { .vsl-sound { padding: 0.55rem 0.9rem; font-size: 0.8rem; gap: 0.4rem; } .vsl-sound svg { width: 16px; height: 16px; } }
.artifact-frame.vsl.is-preview .vsl-ui { opacity: 0; pointer-events: none; }

/* ══════════════════════════════════════════════════════════════════════════
   THE FUNNEL BLOCKS — the stack, the three doors, the FAQ.
   Spec: docs/funnel-design-2026-09-20.md §1, §1b.

   ⛔ ZERO NEW COLOURS AND ZERO NEW TYPE, per RULE 02's restraint law. Every
   value below resolves to a token that already exists: the cards are the
   `.glass` treatment this sheet already declares, the accent is
   --mercury-blue, the rules are --glass-border, and the micro-labels are
   --micro so they flip to mono with the depth gradient for free.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── THE STACK — slide 7's six checks ─────────────────────────────────────── */
.stack-sec { padding: var(--sec-y) 1.25rem; }
/* ⛔ GLYPHS, NOT A BENTO. This was a bordered 2×3 of text cells with a tick —
   which reads as a spec table, and a spec table asks to be READ. The picture
   has to carry the claim so the label lands on a belief already formed. No
   cell borders: the boxes made six ideas look like one inventory. */
.stack {
  list-style: none; margin: 3rem 0 2.5rem; padding: 0;
  display: grid; gap: 3rem 2rem; grid-template-columns: 1fr;
  /* ⛔ RESET THE LEGACY `.stack` ABOVE (line ~551, a different component that
     shares the name): it carries a border, a radius and overflow:hidden, and
     since this rule never redeclared them the glyph grid kept rendering
     inside a card — the exact box this change exists to remove. */
  border: 0; border-radius: 0; overflow: visible; background: none;
}
.stack > li {
  margin: 0; padding: 0; background: none; border: 0;
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 0.9rem;
}
.stack .g {
  width: 72px; height: 72px; flex: none;
  fill: none; stroke: var(--text-2); stroke-width: 2.4;
  stroke-linecap: round; stroke-linejoin: round;
}
/* the accent marks the ANSWER in each glyph — the picked bar, the found cell,
   the last stop. One accent per picture, so the eye lands where the claim is. */
.stack .g .a { stroke: var(--mercury-blue); }
.stack .g .af { stroke: var(--mercury-blue); fill: rgba(125, 211, 252, 0.14); }
.stack > li strong { color: var(--text-1); font-weight: 500; font-size: 1.02rem; }
.stack > li span {
  color: var(--text-3); font-size: 0.9rem; line-height: 1.6; max-width: 24ch;
}
@media (min-width: 640px) { .stack { grid-template-columns: 1fr 1fr; } }
@media (min-width: 960px) { .stack { grid-template-columns: repeat(3, 1fr); } }

/* EACH GLYPH DRAWS ITSELF AS ITS BEAT LANDS — one belief per scroll, which is
   the whole reason the section is a track. 200 exceeds every path length here,
   so one dasharray covers all of them. */
html.js .stack > li .g > * { stroke-dasharray: 200; stroke-dashoffset: 200; }
html.js .stack > li.on .g > * {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 760ms cubic-bezier(.22,.61,.36,1) 140ms;
}
html.js .stack > li .g .af { fill-opacity: 0; }
html.js .stack > li.on .g .af { fill-opacity: 1; transition: fill-opacity 500ms ease 600ms; }
@media (prefers-reduced-motion: reduce) {
  html.js .stack > li .g > * { stroke-dasharray: none; stroke-dashoffset: 0; }
  html.js .stack > li .g .af { fill-opacity: 1; }
}

/* ══ SLIDES 1 · 2 · 4 · 5 — THE DECK'S METHOD, DRAWN ═══════════════════════
   Isaac, 2026-09-20: "it looks like garbage and is unclear in the sections
   you added because you just copied bad placeholder slides and stuff. show
   it dude."
   (Superseded, kept: these four sections shipped as a pill row, three text
   cards, a numbered list and three bullet blocks — the slides TRANSCRIBED.
   A transcription of a slide is not the slide: the deck's pictures carry the
   argument and the text only names it, so copying the text ships the labels
   and drops the claim. Every one is now an actual drawing.)
   Inline SVG, token colours only (RULE 02 §5). No icon font, no runtime dep. */

/* THE SHARED DIAGRAM SURFACE — one set of defaults for all four pictures. */
.dia { display: block; width: 100%; height: auto; overflow: visible; }
.dia text {
  font-family: var(--body); fill: var(--text-2);
  text-anchor: middle; dominant-baseline: middle;
}
.dia .arw path {
  fill: none; stroke: var(--text-3); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ── SLIDE 1 — THE RAIL. The claim is the ORDER, so one track, five stops. ── */
.frame-sec { padding: var(--sec-y) 1.25rem; }
.frame-dia { margin: 2.75rem 0 2rem; }
.frame-dia .rail { stroke: var(--glass-border); stroke-width: 2; }
.frame-dia .fnode circle {
  fill: var(--void); stroke: var(--glass-border); stroke-width: 2;
}
.frame-dia .fnode .glyph {
  fill: none; stroke: var(--text-2); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.frame-dia .fnode text {
  font-size: 15px; fill: var(--text-1); letter-spacing: 0.01em;
}
/* the last move is where the work ends up, so it is the one that is lit */
.frame-dia .fnode:last-of-type circle { stroke: var(--mercury-blue); }
.frame-dia .fnode:last-of-type .glyph { stroke: var(--mercury-blue); fill: rgba(125,211,252,0.14); }

/* ── SLIDE 2 — ONE SET OF BOXES, TRANSFORMING ON ITS OWN CLOCK ──────────
   A single 2.6s timeline walks the four boxes through all three states:
   undrawable → drawn → two of them replaced. It was driven by `data-phase`
   off scroll position, which meant a fast scroll skipped the middle state
   entirely — and the middle state is the whole argument. */
.box-sec { padding: var(--sec-y) 1.25rem; }
.box-stage { margin: 2.5rem 0 0; }
.box-states {
  list-style: none; margin: 0 0 2rem; padding: 0;
  display: flex; gap: 2.25rem;
  font-family: var(--micro); font-size: 0.74rem; letter-spacing: 0.1em;
  text-transform: uppercase;
}
.box-states > li { margin: 0; color: var(--text-3); opacity: 0.3; }
.box-dia { max-width: 1000px; }
.box-dia .cell rect { fill: none; stroke-width: 2; }
/* ⛔ TWO RECTS, NOT ONE ANIMATED DASH. `stroke-dasharray` does not
   interpolate between dashed and solid in any browser, so the undrawable box
   and the drawn box are separate shapes that CROSSFADE — which is also the
   truer statement: one is REPLACED by the other. */
.box-dia .cell .ghost { stroke: var(--text-3); stroke-dasharray: 7 7; opacity: 0.55; }
.box-dia .cell .solid { stroke: var(--text-2); opacity: 0; }
.box-dia .cell .q  { font-size: 24px; fill: var(--text-3); opacity: 0.8; }
.box-dia .cell .nm { font-size: 17px; fill: var(--text-1); opacity: 0; }
.box-dia .cell .spark { fill: var(--mercury-blue); stroke: none; opacity: 0; }
.box-dia .arw path { opacity: 0.4; }
.box-note {
  color: var(--text-2); font-size: 0.92rem; line-height: 1.65;
  margin: 1.5rem 0 0; opacity: 0;
}

@keyframes boxGhostOut { 0%, 26% { opacity: .55 } 38%, 100% { opacity: 0 } }
@keyframes boxSolidIn  { 0%, 26% { opacity: 0 }   38%, 100% { opacity: 1 } }
@keyframes boxQOut     { 0%, 24% { opacity: .8 }  34%, 100% { opacity: 0 } }
@keyframes boxNameIn   { 0%, 28% { opacity: 0 }   40%, 100% { opacity: 1 } }
@keyframes boxArrows   { 0%, 26% { opacity: .4 }  42%, 100% { opacity: 1 } }
@keyframes boxSwap {
  0%, 62%   { stroke: #94a3b8; fill: rgba(125,211,252,0) }
  78%, 100% { stroke: #7dd3fc; fill: rgba(125,211,252,0.13) }
}
@keyframes boxSpark { 0%, 66% { opacity: 0 } 82%, 100% { opacity: 1 } }
@keyframes boxNote  { 0%, 72% { opacity: 0 } 88%, 100% { opacity: 1 } }
/* the three labels light in turn, so the state has a NAME while it is showing */
@keyframes boxLit1 { 0%, 24% { opacity: 1; color: #7dd3fc } 34%, 100% { opacity: .3; color: #6c7c92 } }
@keyframes boxLit2 { 0%, 26% { opacity: .3; color: #6c7c92 } 38%, 60% { opacity: 1; color: #7dd3fc } 72%, 100% { opacity: .3; color: #6c7c92 } }
@keyframes boxLit3 { 0%, 64% { opacity: .3; color: #6c7c92 } 78%, 100% { opacity: 1; color: #7dd3fc } }

.play .box-dia .cell .ghost { animation: boxGhostOut 2600ms linear both; }
.play .box-dia .cell .solid { animation: boxSolidIn  2600ms linear both; }
.play .box-dia .cell .q     { animation: boxQOut     2600ms linear both; }
.play .box-dia .cell .nm    { animation: boxNameIn   2600ms linear both; }
.play .box-dia .arw path    { animation: boxArrows   2600ms linear both; }
.play .box-dia .cell.swap .solid { animation: boxSolidIn 2600ms linear both, boxSwap 2600ms linear both; }
.play .box-dia .cell.swap .spark { animation: boxSpark 2600ms linear both; }
.play .box-note { animation: boxNote 2600ms linear both; }
.play .box-states > li:nth-child(1) { animation: boxLit1 2600ms linear both; }
.play .box-states > li:nth-child(2) { animation: boxLit2 2600ms linear both; }
.play .box-states > li:nth-child(3) { animation: boxLit3 2600ms linear both; }

/* ── SLIDE 3 — THE CLIMB ACROSS FIVE CHASMS, and the ten obstacles ──────── */
.climb-sec { padding: var(--sec-y) 1.25rem; }
.climb-dia { margin: 2.75rem 0 1.5rem; }
.climb-dia rect { fill: none; stroke: var(--glass-border); stroke-width: 1.5; }
.climb-dia .here rect { stroke-dasharray: 6 6; }
.climb-dia .there rect { stroke: rgba(125, 211, 252, 0.45); fill: rgba(125, 211, 252, 0.07); }
.climb-dia .lbl {
  font-family: var(--micro); font-size: 12.5px; letter-spacing: 0.1em;
  text-transform: uppercase; fill: var(--text-2);
}
.climb-dia .there .lbl { fill: var(--text-1); }
.climb-dia .l1 { font-size: 13px; fill: var(--text-3); }
.climb-dia .ground path {
  fill: none; stroke: var(--text-2); stroke-width: 3; stroke-linecap: round;
}
/* ⛔ THE CHASMS ARE DRAWN AS GAPS THAT ARE BRIDGED, not as holes. The slide's
   claim is that they get CROSSED — the arc over each gap is the whole point,
   and a dashed hole with no span would say the opposite. */
.climb-dia .chasm path {
  fill: none; stroke: var(--mercury-blue); stroke-width: 2;
  stroke-dasharray: 5 5; stroke-linecap: round;
}
.climb-dia .chasm text {
  font-family: var(--micro); font-size: 11px; letter-spacing: 0.08em;
  text-transform: uppercase; fill: var(--text-3);
}

/* the ten, each one a thing an owner SAYS and the thing that answers it */
.obst { list-style: none; margin: 2.5rem 0 2rem; padding: 0; counter-reset: ob; }
.obst > li {
  counter-increment: ob; margin: 0;
  display: grid; grid-template-columns: 1fr; gap: 0.45rem;
  padding: 1.15rem 0; border-bottom: 1px solid var(--glass-border);
}
.obst > li:first-child { border-top: 1px solid var(--glass-border); }
.obst > li b {
  color: var(--text-1); font-weight: 500; font-size: 0.98rem;
}
.obst > li b::before {
  content: counter(ob, decimal-leading-zero);
  font-family: var(--micro); font-size: 0.72rem; color: var(--text-3);
  margin-right: 0.7rem;
}
/* the quote is the PAIN, in their own words, so it is set as speech */
.obst > li q {
  quotes: '\201C' '\201D'; font-style: italic;
  color: var(--text-2); font-size: 0.97rem; line-height: 1.6;
}
/* and the answer arrives accented, because that is the turn */
.obst > li span {
  color: var(--text-2); font-size: 0.92rem; line-height: 1.6;
  display: flex; gap: 0.55rem;
}
.obst > li span::before { content: '→'; color: var(--mercury-blue); flex: none; }
@media (min-width: 940px) {
  .obst > li {
    grid-template-columns: minmax(0, 0.55fr) minmax(0, 1fr) minmax(0, 1fr);
    gap: 0 1.75rem; align-items: baseline;
  }
}
.climb-close {
  font-family: var(--display); font-size: clamp(1.2rem, 2.2vw, 1.6rem);
  color: var(--text-1); line-height: 1.3; margin: 0;
}
html.js .obst > li { opacity: 0; transform: translateY(14px); }
html.js .obst > li.on {
  opacity: 1; transform: none;
  transition: opacity 440ms cubic-bezier(.22,.61,.36,1),
              transform 440ms cubic-bezier(.22,.61,.36,1);
}
html.js .climb-dia .chasm path,
html.js .climb-dia .ground path { opacity: 0; }
html.js .climb-dia.on .chasm path,
html.js .climb-dia.on .ground path { opacity: 1; transition: opacity 600ms ease 120ms; }
/* ⛔ EVERY TIMELINE OFF, EVERY END STATE ON. The pictures ARE the argument;
   only their arrival was motion, so nothing of the argument is lost here. */
@media (prefers-reduced-motion: reduce) {
  .frame-dia .rail, .method-dia .arw path, .method-dia .loop path,
  .arc-dia .slope, .climb-dia .ground path, .climb-dia .chasm path {
    stroke-dasharray: none !important; stroke-dashoffset: 0 !important;
    animation: none !important;
  }
  .method-dia .loop .head, .method-dia .loop text { opacity: 1 !important; animation: none !important; }
  html.js .frame-dia .fnode, html.js .method-dia .mnode,
  html.js .arc-dia .stop, html.js .obst > li {
    opacity: 1 !important; transform: none !important; animation: none !important;
  }
  /* ⛔ the pop lives on the CIRCLE, never the group — so the reset does too */
  html.js .frame-dia .fnode circle, html.js .method-dia .mnode circle,
  html.js .arc-dia .stop circle { transform: none !important; animation: none !important; }
  /* the box reads its FINAL state with no timeline */
  .box-dia .cell .ghost, .box-dia .cell .q, .box-dia .cell .solid,
  .box-dia .cell .nm, .box-dia .cell .spark, .box-dia .arw path,
  .box-note, .box-states > li { animation: none !important; }
  .box-dia .cell .ghost, .box-dia .cell .q { opacity: 0 !important; }
  .box-dia .cell .solid, .box-dia .cell .nm, .box-dia .arw path,
  .box-note, .box-states > li { opacity: 1 !important; }
  .box-dia .cell.swap .solid { stroke: var(--mercury-blue) !important; fill: rgba(125,211,252,0.13) !important; }
  .box-dia .cell.swap .spark { opacity: 1 !important; }
}

/* ── SLIDE 4 — THE LOOP. A list cannot say "repeat"; the return arc can. ── */
.method-sec { padding: var(--sec-y) 1.25rem; }
.method-dia { margin: 2.75rem 0 2rem; }
.method-dia .mnode circle {
  fill: var(--void); stroke: var(--glass-border); stroke-width: 2;
}
.method-dia .num {
  font-family: var(--micro); font-size: 15px; fill: var(--mercury-blue);
}
.method-dia .nm { font-size: 16px; fill: var(--text-1); }
.method-dia .sub { font-size: 12.5px; fill: var(--text-3); }
.method-dia .loop path {
  fill: none; stroke: var(--mercury-blue); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; opacity: 0.75;
}
.method-dia .loop text {
  font-family: var(--micro); font-size: 12px; letter-spacing: 0.14em;
  text-transform: uppercase; fill: var(--mercury-blue);
}

/* ── SLIDE 5 — THE CLIMB. Seven stages rising through three bands. ── */
.arc-sec { padding: var(--sec-y) 1.25rem; }
.arc-dia { margin: 2.75rem 0 2.5rem; }
.arc-dia .band rect {
  fill: rgba(255, 255, 255, 0.022); stroke: var(--glass-border); stroke-width: 1;
}
.arc-dia .band rect.last { stroke: rgba(125, 211, 252, 0.35); }
.arc-dia .slope {
  fill: none; stroke: var(--text-3); stroke-width: 2.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.arc-dia .stop circle { fill: var(--void); stroke: var(--text-2); stroke-width: 2.5; }
.arc-dia .stop text { font-size: 14px; fill: var(--text-2); }
.arc-dia .stop.peak circle { stroke: var(--mercury-blue); fill: rgba(125,211,252,0.18); }
.arc-dia .stop.peak text { fill: var(--text-1); font-size: 15px; }
.arc-dia .bandname text {
  font-family: var(--micro); font-size: 12px; letter-spacing: 0.12em;
  text-transform: uppercase; fill: var(--text-3);
}

/* the detail under the climb — the stage rows and their CHIPS */
.arc { list-style: none; margin: 0 0 2rem; padding: 0; display: grid; gap: 1.25rem; }
.arc > li {
  margin: 0; padding: 1.75rem;
  border: 1px solid var(--glass-border); border-radius: var(--r-card);
  background: var(--glass-bg);
}
.arc-phase {
  font-family: var(--micro); font-size: 0.78rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--mercury-blue); margin: 0 0 0.5rem;
}
.arc-aim {
  font-family: var(--display); font-size: clamp(1.15rem, 2vw, 1.5rem);
  color: var(--text-1); line-height: 1.3; margin: 0 0 1.4rem;
}
.arc > li > ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.1rem; }
.arc > li > ul > li { margin: 0; display: grid; grid-template-columns: 1fr; gap: 0.5rem; }
.arc > li > ul > li b { color: var(--text-1); font-weight: 500; font-size: 0.98rem; }
/* ⛔ CHIPS, NOT A MIDDOT RUN. Six items separated by · read as one long
   sentence and scan as noise; as discrete marks they read as an inventory,
   which is what they are. */
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.chips > i {
  font-style: normal; font-family: var(--micro); font-size: 0.7rem;
  letter-spacing: 0.04em; color: var(--text-3);
  border: 1px solid var(--glass-border); border-radius: var(--r-sm);
  padding: 0.32rem 0.6rem; white-space: nowrap;
}
.arc-own {
  margin: 1.3rem 0 0; padding-top: 1.1rem;
  border-top: 1px solid var(--glass-border);
  color: var(--text-2); font-size: 0.9rem; line-height: 1.7;
}
.arc-own b { color: var(--text-1); font-weight: 500; }
.arc-result {
  margin: 1.3rem 0 0; padding-top: 1.1rem;
  border-top: 1px solid var(--glass-border);
  font-family: var(--micro); font-size: 0.76rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-3); line-height: 1.7;
}
@media (min-width: 900px) {
  .arc > li > ul > li { grid-template-columns: minmax(0, 0.4fr) minmax(0, 1fr); gap: 0 1.5rem; align-items: baseline; }
}

/* ══ THE TIMELINE — the diagrams PLAY when you arrive ═════════════════════
   Isaac: "scrolling at normal speed just blows past everything. i dont even
   see everything spawn."
   (Superseded, kept: every beat here was mapped to a fraction of its track's
   scroll travel, and the lines were drawn by a --p scrub head. That reads
   beautifully at the speed the author scrolls and vanishes at any other: an
   ordinary flick is over 1000px, so the whole sequence fired inside a few
   frames. Lengthening the track only moves the cliff further down.)
   Each section now runs a FIXED ~2s timeline the moment it is 45% on screen,
   so the animation takes the same time whatever the reader's wheel did, and
   the section stays pinned long enough to be watched. */

@keyframes diaFade   { from { opacity: 0 } to { opacity: 1 } }
@keyframes diaPop    { from { transform: scale(0.4) } to { transform: scale(1) } }
@keyframes diaDraw   { from { stroke-dashoffset: var(--len) } to { stroke-dashoffset: 0 } }
@keyframes diaRise   { from { opacity: 0; transform: translateY(16px) } to { opacity: 1; transform: none } }

/* every drawable line starts undrawn and is drawn BY THE TIMELINE */
.frame-dia .rail,
.method-dia .arw path,
.method-dia .loop path,
.arc-dia .slope,
.climb-dia .ground path,
.climb-dia .chasm path {
  stroke-dasharray: var(--len, 1000);
  stroke-dashoffset: var(--len, 1000);
}
.frame-dia .rail        { --len: 900; }
.method-dia .arw path   { --len: 620; }
.method-dia .loop path  { --len: 1000; }
.arc-dia .slope         { --len: 900; }
.climb-dia .ground path { --len: 360; }
.climb-dia .chasm path  { --len: 400; }
.method-dia .loop .head, .method-dia .loop text { opacity: 0; }

/* the nodes start absent; --i is their stagger index, set once by reveal.js */
html.js .frame-dia .fnode,
html.js .method-dia .mnode,
html.js .arc-dia .stop { opacity: 0; }
html.js .frame-dia .fnode circle,
html.js .method-dia .mnode circle,
html.js .arc-dia .stop circle {
  transform-box: fill-box; transform-origin: center; transform: scale(0.4);
}

/* ── SLIDE 1, the rail: the track draws, then the five stops land ── */
.play .frame-dia .rail { animation: diaDraw 820ms ease-out both; }
.play .frame-dia .fnode {
  animation: diaFade 360ms ease-out both calc(240ms + var(--i) * 170ms);
}
.play .frame-dia .fnode circle {
  animation: diaPop 520ms cubic-bezier(.34,1.56,.64,1) both calc(240ms + var(--i) * 170ms);
}

/* ── SLIDE 4, the loop: chain draws, stops land, THEN the arc comes back ── */
.play .method-dia .arw path { animation: diaDraw 900ms ease-out both 120ms; }
.play .method-dia .mnode {
  animation: diaFade 360ms ease-out both calc(200ms + var(--i) * 160ms);
}
.play .method-dia .mnode circle {
  animation: diaPop 520ms cubic-bezier(.34,1.56,.64,1) both calc(200ms + var(--i) * 160ms);
}
/* ⛔ THE RETURN ARC CLOSES LAST — it says "repeat", and it cannot say that
   before the thing to repeat has finished arriving (the fifth stop lands at
   840ms). Its delay is not a taste; it is that ordering. */
.play .method-dia .loop path { animation: diaDraw 760ms ease-in-out both 1080ms; }
.play .method-dia .loop .head { animation: diaFade 300ms ease-out both 1780ms; }
.play .method-dia .loop text  { animation: diaFade 400ms ease-out both 1840ms; }

/* ── SLIDE 5, the climb: the slope rises, then the seven stages land ── */
.play .arc-dia .slope { animation: diaDraw 1100ms ease-out both 80ms; }
.play .arc-dia .stop {
  animation: diaFade 340ms ease-out both calc(280ms + var(--i) * 130ms);
}
.play .arc-dia .stop circle {
  animation: diaPop 480ms cubic-bezier(.34,1.56,.64,1) both calc(280ms + var(--i) * 130ms);
}

/* ── SLIDE 3, the chasms: the ground draws, then each bridge is thrown ── */
.play .climb-dia .ground path { animation: diaDraw 700ms ease-out both 100ms; }
.play .climb-dia .chasm path  { animation: diaDraw 900ms ease-out both 620ms; }
html.js .obst > li { opacity: 0; }
.play .obst > li {
  animation: diaRise 420ms cubic-bezier(.22,.61,.36,1) both calc(900ms + var(--i) * 95ms);
}

/* ── THE LEDGER — what the $38,000 anchor is actually made of ─────────────
   Three columns: the deliverable, what it is, and WHO ELSE you would hire for
   it. The third column is the pain and it is deliberately the quietest type
   on the row — it should register as a running tally of other people's
   invoices, not shout. ZERO new colours or type: --text-1/-2/-3, --micro and
   --glass-border are the existing vocabulary. */
.ledger-sec { padding: var(--sec-y) 1.25rem; }
.ledger { list-style: none; margin: 2.75rem 0 2rem; padding: 0; }
.ledger > li {
  display: grid; grid-template-columns: 1fr; gap: 0.35rem;
  padding: 1.15rem 0; border-bottom: 1px solid var(--glass-border);
}
.ledger > li:first-child { border-top: 1px solid var(--glass-border); }
.ledger > li b { color: var(--text-1); font-weight: 500; font-size: 1.02rem; }
.ledger > li span { color: var(--text-2); font-size: 0.93rem; line-height: 1.65; }
.ledger > li i {
  font-style: normal; font-family: var(--micro); font-size: 0.72rem;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-3);
}
@media (min-width: 900px) {
  .ledger > li {
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.25fr) minmax(0, 0.6fr);
    gap: 0 2rem; align-items: baseline;
  }
  .ledger > li i { text-align: right; }
}
/* one deliverable per beat — the list is the knowledge, and it is DISPLAYED,
   not summarised */
html.js .ledger > li { opacity: 0; transform: translateY(18px); }
html.js .ledger > li.on {
  opacity: 1; transform: none;
  transition: opacity 460ms cubic-bezier(.22,.61,.36,1),
              transform 460ms cubic-bezier(.22,.61,.36,1);
}
@media (prefers-reduced-motion: reduce) {
  html.js .ledger > li { opacity: 1 !important; transform: none !important; }
}

/* ══ THE SIMPLE PAGE — ai-transformation.html, in THE BLUEPRINT SYSTEM ═══════
   RULE 02. The page is drawn like a diagram on a drafting sheet: big cards
   with an ink outline, hairline dividers inside them, and a small square NODE
   wherever a divider meets the card's edge — the handles a diagram editor
   shows on a selected shape. Bands alternate: plain paper (the blueprint
   columns show through), GRAIN (paper with a noise tooth), and INK (black,
   the one inverted band, for the final action). Nothing else on the page is
   decorated. */

/* the pill: a label in a hairline capsule, sitting on the eyebrow's connector */
.eyebrow > .pill {
  border: 1px solid var(--ink); border-radius: 999px;
  padding: 0.38rem 0.9rem; background: var(--paper); line-height: 1;
}

/* ── THE PARCELS (*USER*'s ruling) — a little easter egg ─────────────────────
   Ten parcels on each of the two INNER blueprint columns (never the outer ones,
   beside the cards). The page is cut into ten equal stretches and each parcel
   shuttles back and forth inside ITS OWN stretch — so two parcels can never meet,
   anywhere — at staggered phases, so there is always one somewhere to catch. They
   live IN THE PAGE, not the viewport: scrolling passes them, they never follow it.
   The layer sits behind everything (z -1), so a parcel only shows crossing a gap.
   Phones (the inner columns sit at the screen edge there) and reduced motion: none. */
body { position: relative; }            /* so the layer spans the whole document */
.bp-parcels { position: absolute; inset: 0; z-index: -1; pointer-events: none; overflow: hidden; }
.bp-parcels .lane { position: absolute; top: 0; bottom: 0; width: 0; }
.bp-parcels .lane:nth-child(1) { left: calc(50% - 183px); }
.bp-parcels .lane:nth-child(2) { left: calc(50% + 183px); }
.bp-parcels i {
  position: absolute; left: -9px; width: 18px; height: 18px;
  background: var(--pkg) center / contain no-repeat;
  animation: parcel-shuttle 12s ease-in-out infinite alternate;
  animation-delay: var(--d);
}
/* each parcel's own stretch: from --a to --b, a tenth of the page apart */
.bp-parcels i:nth-child(1) { --a: 0%;  --b: 10%;  --d: 0s; }
.bp-parcels i:nth-child(2) { --a: 10%; --b: 20%;  --d: -7s; }
.bp-parcels i:nth-child(3) { --a: 20%; --b: 30%;  --d: -3s; }
.bp-parcels i:nth-child(4) { --a: 30%; --b: 40%;  --d: -10s; }
.bp-parcels i:nth-child(5) { --a: 40%; --b: 50%;  --d: -5s; }
.bp-parcels i:nth-child(6) { --a: 50%; --b: 60%;  --d: -1s; }
.bp-parcels i:nth-child(7) { --a: 60%; --b: 70%;  --d: -8s; }
.bp-parcels i:nth-child(8) { --a: 70%; --b: 80%;  --d: -4s; }
.bp-parcels i:nth-child(9) { --a: 80%; --b: 90%;  --d: -11s; }
.bp-parcels i:nth-child(10) { --a: 90%; --b: 100%; --d: -6s; }
.bp-parcels .lane:nth-child(2) i { animation-direction: alternate-reverse; animation-duration: 14.5s; }
@keyframes parcel-shuttle { from { top: var(--a); } to { top: calc(var(--b) - 18px); } }
@media (max-width: 900px), (prefers-reduced-motion: reduce) { .bp-parcels { display: none; } }

/* ── the bands ─────────────────────────────────────────────────────────── */
.bp-sec { padding: var(--sec-y) 1.5rem; }
.bp-band { background-color: var(--paper); background-image: var(--grain);
           border-top: 1px solid var(--ink); border-bottom: 1px solid var(--ink); }
.bp-ink  { background: var(--ink); color: var(--paper); }
.bp-ink h2 { color: var(--paper); }
.bp-ink .lede { color: rgba(255,255,255,0.72); }
.bp-ink .door-fine { color: rgba(255,255,255,0.72); }
.bp-ink .cta-primary { background: var(--ink); color: var(--paper); border-color: var(--paper); }
.bp-ink .cta-primary:hover { background: var(--paper); color: var(--ink); }

/* ── the card ──────────────────────────────────────────────────────────── */
.bp-card {
  position: relative; background: var(--paper);
  border: 1px solid var(--ink); border-radius: var(--r-bp);
}
.bp-pad { padding: clamp(1.6rem, 4vw, 3.4rem); }
/* a horizontal divider across the card, a node at each end on the outline */
.bp-rule { position: relative; height: 0; border-top: 1px solid var(--ink); }
.bp-rule::before, .bp-rule::after, .bp-split > .bp-cell + .bp-cell::before,
.bp-split > .bp-cell + .bp-cell::after {
  content: ''; position: absolute; width: 11px; height: 11px;
  background: var(--paper); border: 1px solid var(--ink); z-index: 1;
}
.bp-rule::before { left: -6px; top: -6px; }
.bp-rule::after  { right: -6px; top: -6px; }
/* a card split into two cells by a vertical divider, nodes top and bottom */
.bp-split { display: grid; grid-template-columns: 1.35fr 1fr; }
.bp-split > .bp-cell { position: relative; min-width: 0; padding: clamp(1.4rem, 3vw, 2.6rem); }
.bp-split > .bp-cell + .bp-cell { border-left: 1px solid var(--ink); }
.bp-split > .bp-cell + .bp-cell::before { left: -6px; top: -6px; }
.bp-split > .bp-cell + .bp-cell::after  { left: -6px; bottom: -6px; }
@media (max-width: 900px) {
  .bp-split { grid-template-columns: 1fr; }
  .bp-split > .bp-cell + .bp-cell { border-left: 0; border-top: 1px solid var(--ink); }
  .bp-split > .bp-cell + .bp-cell::before { left: -6px; top: -6px; }
  .bp-split > .bp-cell + .bp-cell::after  { left: auto; right: -6px; top: -6px; bottom: auto; }
}

/* ── the hero ──────────────────────────────────────────────────────────── */
.bp-hero { padding: clamp(1.2rem, 3vw, 2.2rem) 1.5rem var(--sec-y); }
.bp-hero h1 { font-size: clamp(2.2rem, 4vw, 3.7rem); line-height: 1.04; letter-spacing: -0.035em; margin-bottom: 1.2rem; }
.bp-hero h1 span { color: var(--text-3); }
.bp-hero .lede { margin-bottom: 1.3rem; max-width: 52ch; }
/* the split: copy left, the drawing right — no divider between them (bugster.dev's
   hero: one sheet, the picture sitting on it), and the drawing bleeds off the
   card's right edge, clipped to the card's corner, because the rows never end */
.hero-split { display: grid; grid-template-columns: 1.15fr 1fr; align-items: center; }
.hero-copy { min-width: 0; padding: clamp(1.6rem, 3.4vw, 3rem); }
.hero-art { min-width: 0; align-self: stretch; overflow: hidden; display: flex; align-items: center;
  border-top-right-radius: calc(var(--r-bp) - 1px); }
.hero-art img, .hero-art video { display: block; width: 130%; max-width: none; height: auto; }
/* who it's for — heads the film: a mono key, then the avatar in prose */
.hero-for { margin: 0 0 1.4rem; max-width: 72ch;
  font-size: 1rem; line-height: 1.6; color: var(--text-2); }
.hero-for-k { display: block; margin-bottom: 0.3rem; font-family: var(--mono); font-size: 0.72rem;
  font-weight: 600; letter-spacing: 0.02em; color: var(--ink); }
@media (max-width: 900px) {
  .hero-split { grid-template-columns: 1fr; }
  .hero-art { border-top-right-radius: 0; }
  .hero-art img, .hero-art video { width: 135%; }
}
/* ⛔ THE FILM IS THE PAGE — it fills the card's lower sheet edge to edge */
.solo-vsl { width: 100%; margin: 0; border-radius: var(--r-lg); }
.solo-cta { margin: 2rem 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.6rem; }
.solo-cta .cta-primary { display: inline-block; }
.solo-cta .door-fine { max-width: 46ch; margin: 0; }

/* ── what I do ─────────────────────────────────────────────────────────── */
.what-sec h2 { max-width: 26ch; margin: 0 0 2.2rem; }
.what-loop { border-radius: var(--r-lg); }
/* the loop sits centred in its cell, level with the middle of the levels list */
.what-sec .bp-split > .bp-cell:first-child { display: flex; align-items: center; }
.what-loop > video { object-fit: contain; background: var(--ink); }
.what-levels-head { font-family: var(--mono); font-weight: 600; margin: 0 0 1rem; color: var(--ink); }
.what-steps { margin: 0; padding: 0; }
.what-steps .lvl {
  border-top: 3px solid var(--line); padding: 0.85rem 0 0.95rem;
  color: var(--text-2); font-size: 0.95rem; line-height: 1.6;
}
.what-steps .lvl-solo, .what-steps .lvl-again { border-top-color: var(--loop-gold); }
.what-steps .lvl-org  { border-top-color: var(--loop-cyan); }
.what-steps .lvl-auto { border-top-color: var(--loop-violet); }
.what-steps .lvl span {
  display: block; margin-bottom: 0.2rem;
  font-family: var(--mono); font-size: 0.86rem; font-weight: 600; color: var(--ink);
}

/* ── THE PINNED STACK — What I do → What you get → Who helps you ────────────
   Each .pin scrolls fully into view and then STAYS (sticky, its top measured by
   stack.js), and the next .sheet slides up over it: opaque paper carrying its own
   blueprint columns, an ink edge on top, at least a screen tall — so when the
   stack ends and the pins let go, everything under the last sheet is already off
   screen. Later siblings paint above earlier ones. */
.pin-stack { position: relative; }
.pin-stack > .pin { position: sticky; top: var(--pin-top, 0px); }
.pin-stack > .sheet {
  background: var(--bp-columns), var(--paper);
  border-top: 1px solid var(--ink);
  min-height: calc(100vh - var(--nav-h, 0px));
}
.pin-stack > .sheet:not(.pin) { position: relative; }
.pin-stack > :nth-child(1) { z-index: 0; }
.pin-stack > :nth-child(2) { z-index: 1; }
.pin-stack > :nth-child(3) { z-index: 2; }
.pin-stack > :nth-child(4) { z-index: 3; }

/* ── what you get — stage one's map ─────────────────────────────────────── */
.what-get h2 { max-width: 26ch; margin: 0 0 2.2rem; }
.get-top { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.get-bottom { grid-template-columns: 1.35fr 1fr; }
.get-k { font-family: var(--mono); font-weight: 600; color: var(--ink); margin: 0; }
/* the icon tile, Bugster's way: a line icon in a small outlined square beside the label */
.get-head { display: flex; align-items: center; gap: 0.75rem; margin: 0 0 0.8rem; }
.get-ico {
  flex: none; display: grid; place-items: center; width: 38px; height: 38px;
  border: 1px solid var(--ink); border-radius: var(--r-sm); background: var(--paper);
}
.get-ico svg { width: 20px; height: 20px; fill: none; stroke: var(--ink); stroke-width: 1.6;
               stroke-linecap: round; stroke-linejoin: round; }
.get-v { color: var(--text-2); font-size: 0.95rem; line-height: 1.65; margin: 0; max-width: 46ch; }
/* a TINTED CELL (*USER*'s ruling on ai-transformation.html): the card's cell on its hue's soft ground,
   its heading and icon in the accent — the What you get cards (department gold · software cyan · AI
   teams violet · SOPs green) and the What it becomes stages (gold → cyan → violet). A tinted cell at
   the card's corner takes the corner's radius, so the ground never squares off the outline. */
.cell-tint { background: var(--t-bg); }
.cell-tint .get-k { color: var(--t-ac); }
.cell-tint .get-ico { border-color: var(--t-ac); }
.cell-tint .get-ico svg { stroke: var(--t-ac); }
.bp-card > .bp-split:first-child > .cell-tint:first-child { border-top-left-radius: calc(var(--r-bp) - 1px); }
.bp-card > .bp-split:first-child > .cell-tint:last-child { border-top-right-radius: calc(var(--r-bp) - 1px); }
.bp-card > .bp-split:last-child > .cell-tint:first-child { border-bottom-left-radius: calc(var(--r-bp) - 1px); }
.bp-card > .bp-split:last-child > .cell-tint:last-child { border-bottom-right-radius: calc(var(--r-bp) - 1px); }
/* THE LEAKS — each health item is a disclosure that opens into its tree (*USER*'s structure):
   the numbers · but · so we · so that you · instead of. Closed, it shows the item and its one
   hook; open, a vertical ink trunk with a tick to each branch — the blueprint drawing a tree. */
.leaks { border-top: 1px solid var(--ink); margin-top: 0.4rem; }
.leak { border-bottom: 1px solid var(--line); }
.leak summary {
  list-style: none; cursor: pointer; display: flex; align-items: baseline; gap: 0.9rem;
  padding: 0.8rem 1.8rem 0.8rem 0; position: relative;
}
.leak summary::-webkit-details-marker { display: none; }
.leak summary::after {
  content: '+'; position: absolute; right: 0.2rem; top: 0.7rem;
  font-family: var(--mono); font-size: 1.05rem; color: var(--ink);
}
.leak[open] summary::after { content: '\2212'; }
.leak summary:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.leak-name { font-family: var(--mono); font-weight: 600; color: var(--ink); flex: none; }
.leak-hook { color: var(--text-3); font-size: 0.86rem; }
.leak-tree { margin: 0.1rem 0 1.1rem 0.35rem; border-left: 1px solid var(--ink); }
.leak-tree p {
  position: relative; margin: 0; padding: 0.45rem 0 0.45rem 1.35rem;
  color: var(--text-2); font-size: 0.92rem; line-height: 1.6;
}
.leak-tree p::before {                                   /* the branch tick */
  content: ''; position: absolute; left: 0; top: 1.05rem; width: 0.9rem; border-top: 1px solid var(--ink);
}
.leak-tree p span {
  display: block; font-family: var(--mono); font-size: 0.74rem; font-weight: 600; color: var(--ink);
  margin-bottom: 0.1rem;
}
.leak-tree .leak-dream { color: var(--ink); font-weight: 500; }
/* each branch wears its meaning, on the LABEL and its TICK only — the sentence stays readable ink:
   but = the problem (red) · before = where you are (red) · so we = the action (amber) ·
   after = the dream (green). The numbers stay ink. */
/* ⛔ scoped under .leak-tree, or `.leak-tree p span` (more specific) keeps every label ink */
.leak-tree .leak-but span, .leak-tree .leak-before span { color: var(--signal); }
.leak-tree .leak-we span { color: var(--leak-act); }
.leak-tree .leak-dream span { color: var(--leak-good); }
.leak-tree .leak-but::before, .leak-tree .leak-before::before { border-top-color: var(--signal); border-top-width: 2px; }
.leak-tree .leak-we::before { border-top-color: var(--leak-act); border-top-width: 2px; }
.leak-tree .leak-dream::before { border-top-color: var(--leak-good); border-top-width: 2px; }
.leak-tree cite {
  display: block; margin-top: 0.25rem; font-style: normal;
  font-family: var(--mono); font-size: 0.7rem; color: var(--text-3);
}
.leak-tree cite i { font-style: italic; }
@media (prefers-reduced-motion: no-preference) {
  .leak[open] .leak-tree { animation: leak-open 180ms ease-out; }
}
@keyframes leak-open { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
/* the health cell carries the trees, so it takes the wider column */
.get-bottom { grid-template-columns: 1.8fr 1fr; }

/* ⛔ restate the stack AFTER the column counts above, or the 3-up survives onto phones */
@media (max-width: 900px) {
  .get-top, .get-bottom { grid-template-columns: 1fr; }
  /* stacked, a corner cell spans both corners of its edge and the inner cells none */
  .bp-card > .bp-split:first-child > .cell-tint:first-child { border-top-right-radius: calc(var(--r-bp) - 1px); }
  .bp-card > .bp-split:first-child > .cell-tint:last-child:not(:first-child) { border-top-right-radius: 0; }
  .bp-card > .bp-split:last-child > .cell-tint:last-child { border-bottom-left-radius: calc(var(--r-bp) - 1px); }
  .bp-card > .bp-split:last-child > .cell-tint:first-child:not(:last-child) { border-bottom-left-radius: 0; }
}
@media (max-width: 600px) { .leak summary { flex-direction: column; gap: 0.15rem; } }

/* ── what it becomes — the destination ─────────────────────────────────── */
.becomes-sec h2 { max-width: 30ch; margin: 0 0 2.2rem; }
.becomes-result {
  margin: 0.9rem 0 0; padding-top: 0.7rem; border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: 0.84rem; font-weight: 600; color: var(--ink); line-height: 1.5;
}
.becomes-state { margin: 0.2rem 0 0; font-size: 1.15rem; line-height: 1.5; color: var(--text-2); }
.rail-to .becomes-state { color: var(--ink); font-weight: 500; }
/* the stages read as ONE sequence: an arrow node sits on each divider between them, mid-height —
   the same square node the dividers carry at the card's edge, now pointing the way */
.becomes-steps > .bp-cell { position: relative; }
.step-arrow {
  position: absolute; left: -14px; top: 50%; transform: translateY(-50%); z-index: 1;
  width: 27px; height: 27px; display: grid; place-items: center;
  background: var(--paper); border: 1px solid var(--ink);
  font-family: var(--mono); font-size: 0.9rem; line-height: 1; color: var(--ink);
}
/* the rail under the stages: where it starts at one end, where it ends at the other, and one
   long ink arrow between them, spanning the whole sequence */
.becomes-rail { display: grid; grid-template-columns: minmax(0, 22ch) 1fr minmax(0, 22ch); align-items: center; gap: 1.6rem; }
.rail-to { text-align: right; }
.rail-line { position: relative; height: 0; border-top: 1px solid var(--ink); }
.rail-line::before { content: ""; position: absolute; left: -5px; top: -6px; width: 9px; height: 9px;
  background: var(--paper); border: 1px solid var(--ink); }
.rail-line::after { content: ""; position: absolute; right: -1px; top: -6px;
  border-left: 11px solid var(--ink); border-top: 5.5px solid transparent; border-bottom: 5.5px solid transparent; }
.becomes-terminus p {
  margin: 0; max-width: 40ch; font-family: var(--mono); font-weight: 700;
  font-size: clamp(1.15rem, 2.2vw, 1.6rem); line-height: 1.35; letter-spacing: -0.02em; color: var(--ink);
}
/* ⛔ AFTER the rail's and the steps' own rules, or their desktop layout survives onto phones.
   Stacked, the arrows turn to point DOWN and sit on the hairline between the stages; the rail
   runs top to bottom. */
@media (max-width: 900px) {
  .step-arrow { left: 50%; top: -14px; transform: translateX(-50%) rotate(90deg); }
  .becomes-rail { grid-template-columns: 1fr; gap: 0.9rem; }
  .rail-to { text-align: left; }
  .rail-line { height: 40px; width: 0; margin-left: 5px; border-top: 0; border-left: 1px solid var(--ink); }
  .rail-line::before { left: -6px; top: -5px; }
  .rail-line::after { right: auto; top: auto; left: -6px; bottom: -1px;
    border-left: 5.5px solid transparent; border-right: 5.5px solid transparent; border-top: 11px solid var(--ink); border-bottom: 0; }
}

/* ── who you'll be talking to ───────────────────────────────────────────── */
.about-split { grid-template-columns: 300px minmax(0, 1fr); }
.about-split .portrait-frame { width: 100%; aspect-ratio: 4 / 5; }
.about-split .booker-who { margin-bottom: 1.2rem; }
.about-split .booker-who strong { font-family: var(--mono); font-size: 1.15rem; color: var(--ink); }
.about-split .lede { font-size: 0.98rem; line-height: 1.75; margin-bottom: 1rem; max-width: 58ch; }
.about-split .lede:last-child { margin-bottom: 0; }
/* the About card holds the split AND, under a divider, the story — so the split is
   no longer the card itself and must not draw a second outline */
.about-card .about-split { border: 0; }
.about-story { padding-top: clamp(1.4rem, 3vw, 2.4rem); }
.about-story-head { font-family: var(--mono); font-weight: 600; color: var(--ink); margin: 0 0 0.9rem; }
.about-story p:not(.about-story-head) {
  max-width: 68ch; color: var(--text-2); font-size: 0.98rem; line-height: 1.75; margin: 0 0 0.9rem;
}
.about-story p:last-child { margin-bottom: 0; }
/* ⛔ this must restate the stack: `.about-split` comes after `.bp-split`'s own 900px
   rule, so without it the 300px portrait column survives onto phones and the
   page scrolls sideways (measured: 459px of page on a 390px screen). */
@media (max-width: 900px) {
  .about-split { grid-template-columns: 1fr; }
  .about-split .portrait-frame { max-width: 260px; }
}

/* ── the final action ──────────────────────────────────────────────────── */
.bp-close { text-align: center; }
.bp-close h2 { margin: 0 auto 1.2rem; max-width: 22ch; font-family: var(--mono); font-weight: 700; letter-spacing: -0.035em; }
.bp-close .solo-cta { justify-content: center; }
.bp-close .door-fine { text-align: center; }

/* ── THE THREE DOORS ──────────────────────────────────────────────────────── */
.offer-sec { padding: var(--sec-y) 1.25rem; }
.doors {
  list-style: none; margin: 2.25rem 0 1.75rem; padding: 0;
  display: grid; gap: 1.25rem;
}
/* ⛔ THE TWO DOORS ARE THE SAME HEIGHT. `align-items: start` let the retainer
   card stop 105px short of the founding card, so the cheaper door read as a
   smaller, lesser object floating beside the real one — a visual discount on
   a $2,000 product. Stretching costs nothing and the cards square up. */
@media (min-width: 820px) { .doors { grid-template-columns: 1.15fr 1fr; align-items: stretch; } }

.door {
  background: var(--glass-bg); border: 1px solid var(--glass-border);
  border-radius: var(--r-card); padding: 1.75rem;
  backdrop-filter: blur(var(--blur)); -webkit-backdrop-filter: blur(var(--blur));
}
/* the founding deal carries the accent edge — it is the primary and the
   retainer's job is to price it (§1). */
.door-primary { border-color: rgba(125,211,252,0.35); }

.door-name {
  font-family: var(--micro); font-size: 0.78rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-2); margin: 0 0 0.75rem;
}
.door-price {
  font-family: var(--display); font-size: clamp(2.1rem, 5vw, 2.9rem);
  color: var(--text-1); line-height: 1; margin: 0 0 1.1rem;
  display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap;
}
.door-price span {
  font-family: var(--body); font-size: 0.85rem; letter-spacing: 0.02em;
  color: var(--text-3); font-weight: 400;
}
.door-what {
  color: var(--text-2); font-size: 0.95rem; line-height: var(--lh-prose);
  margin: 0 0 1.5rem;
}
.door .cta-primary,
.close-doors .cta-primary { width: 100%; text-align: center; }
/* ⛔ THE RETAINER IS A $2,000 PURCHASE AND MUST READ AS A BUTTON.
   The site-wide `.cta-secondary` is a small underlined TEXT LINK, which is
   right everywhere else and wrong here — rendered beside the filled primary it
   read as a footnote, not a door. This override is scoped to `.door` so no
   other page moves. The hierarchy is kept the honest way: same shape, same
   size, FILLED beats OUTLINED. */
.door .cta-secondary,
.close-doors .cta-secondary {
  font-family: var(--mono); font-size: 0.8rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-1); background: transparent;
  border: 1px solid var(--glass-border); border-bottom: 1px solid var(--glass-border);
  padding: 1rem 2.4rem; border-radius: var(--r-sm);
  display: block; width: 100%; text-align: center;
  transition: border-color 0.2s, color 0.2s;
}
.door .cta-secondary:hover,
.close-doors .cta-secondary:hover { color: var(--mercury-blue); border-color: var(--mercury-blue); }

/* ── THE TERMINUS ────────────────────────────────────────────────────────
   The last screen of the driven walk. It carries no new vocabulary: the two
   buttons, the names and the fine print are the #offer block's, and the only
   declarations here are LAYOUT. See the HTML comment above .close-sec for why
   a repeated CTA is not a second offer. */
.close-sec { padding: var(--sec-y) 1.25rem; border-top: 1px solid var(--glass-border); }
.close-lede {
  font-family: var(--display); font-size: clamp(1.5rem, 3.2vw, 2.1rem);
  color: var(--text-1); line-height: 1.25; margin: 0 0 2.2rem;
}
.close-doors {
  list-style: none; margin: 0 0 1.6rem; padding: 0;
  display: grid; grid-template-columns: 1fr 1fr; gap: 1.6rem;
}
.close-doors > li { margin: 0; }
@media (max-width: 720px) { .close-doors { grid-template-columns: 1fr; } }
/* ⛔ THE DISCLOSURE SITS WITH THE MONEY, NOT IN THE TERMS (§2). */
.door-fine {
  color: var(--text-3); font-size: 0.82rem; line-height: 1.6;
  margin: 0.9rem 0 0;
}
.door-note {
  color: var(--text-3); font-size: 0.82rem; line-height: 1.6;
  margin: 0.9rem 0 0; padding-top: 0.9rem;
  border-top: 1px solid var(--glass-border);
}

/* ⛔ SOLD OUT — the card STAYS, struck. A removed card teaches nothing; a
   struck one proves the cap was real and makes the retainer the obvious move
   (§3). Add `sold-out` to the <li> and put the reopen date in `.door-note`. */
.door.sold-out { opacity: 0.55; }
.door.sold-out .door-price { text-decoration: line-through; text-decoration-thickness: 2px; }
.door.sold-out .cta-primary, .door.sold-out .cta-secondary { pointer-events: none; opacity: 0.4; }
.door.sold-out .door-name::after {
  content: ' — sold out'; color: var(--mercury-rose); text-decoration: none;
}

/* the call is the EXIT, not a peer — smaller, last, lower contrast, and never
   the word FREE next to a price (§1). */
.door-exit { color: var(--text-3); font-size: 0.92rem; line-height: 1.7; margin: 0; }
.door-exit a { color: var(--mercury-blue); border-bottom: 1px solid rgba(125,211,252,0.25); }
.door-exit a:hover { border-bottom-color: var(--mercury-blue); }

/* ── THE FAQ — native <details>, no JS ────────────────────────────────────── */
.faq-sec { padding: var(--sec-y) 1.25rem; }
.faq {
  margin-top: 2.25rem;
  border-top: 1px solid var(--glass-border);
}
.faq details { border-bottom: 1px solid var(--glass-border); }
.faq summary {
  cursor: pointer; list-style: none; padding: 1.15rem 2.5rem 1.15rem 0;
  color: var(--text-1); font-size: 1rem; line-height: 1.55; position: relative;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: '+'; position: absolute; right: 0.25rem; top: 1rem;
  color: var(--mercury-blue); font-size: 1.25rem; line-height: 1;
  transition: transform 160ms ease;
}
.faq details[open] summary::after { transform: rotate(45deg); }
.faq summary:hover { color: var(--mercury-blue); }
.faq summary:focus-visible { outline: 2px solid var(--mercury-blue); outline-offset: 3px; }
.faq details p {
  color: var(--text-2); font-size: 0.95rem; line-height: var(--lh-prose);
  margin: 0 0 1.3rem; padding-right: 2.5rem; max-width: 68ch;
}
.faq details p a { color: var(--mercury-blue); border-bottom: 1px solid rgba(125,211,252,0.25); }
@media (prefers-reduced-motion: reduce) { .faq summary::after { transition: none; } }


/* ── SLIDE 6 — THE DIY COST ANCHOR. Itemised, and it SUMS. ───────────────── */
.cost-sec { padding: var(--sec-y) 1.25rem; }
/* ⛔ FULL WRAP WIDTH, NOT 46rem. MEASURED at a real 1440 viewport: a 46rem
   table inside the 1100px wrap sat 170 left / 534 RIGHT — a 534px hole down
   one side while .stack, .doors and .faq all ran the full 1100. That is the
   asymmetry Isaac saw. Full width also makes the numbers right-align to the
   same edge as everything else, so it reads as an invoice, which is the
   feeling this section wants. */
.cost {
  list-style: none; margin: 2.25rem 0 1.75rem; padding: 0;
  border-top: 1px solid var(--glass-border);
}
.cost > li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1.5rem;
  padding: 0.95rem 0; border-bottom: 1px solid var(--glass-border);
}
.cost > li > span { color: var(--text-2); font-size: 0.97rem; }
.cost > li > b {
  font-family: var(--mono); font-size: 0.92rem; font-weight: 500;
  color: var(--text-1); white-space: nowrap;
}
.cost > li > b > i { color: var(--text-3); font-style: normal; font-size: 0.82rem; }
/* the total is the anchor — it is the number the cascade is measured against */
.cost .cost-total { border-bottom: 0; padding-top: 1.3rem; }
.cost .cost-total > span { color: var(--text-1); font-size: 1.02rem; }
.cost .cost-total > b {
  font-family: var(--display); font-size: clamp(1.5rem, 3.6vw, 2.1rem);
  color: var(--mercury-rose); letter-spacing: -0.01em;
}

/* ── SLIDES 8–11 — THE CASCADE. ⛔ THIS IS THE SETUP THE PAGE WAS MISSING.
   Without it "and I'm still not charging that" is a punchline with no joke
   in front of it. Each rung is struck as the next arrives, which is what the
   deck does one slide at a time. ─────────────────────────────────────────── */
/* ══ THE PINNED STAGE. Isaac: "force the experience exactly. control." ══
   The SECTION is a tall scroll TRACK; the STAGE inside it is sticky and
   viewport-tall. Scrolling the track does not move the stage — it advances
   which rung has arrived. 340vh over four beats is roughly a screen of scroll
   per beat, which is the "slower scroll": you cannot flick past the cascade,
   and you are never held against your input either.
   ⛔ `html:not(.js)` collapses the whole thing back to ordinary flow. */
/* the cascade now uses the SHARED track/stage below — its height comes from
   --track, which reveal.js derives from the item count. */
/* ⛔ SAME — full wrap. See .cost above. */
.cascade { list-style: none; margin: 2.5rem 0 0; padding: 0; }
.cascade > li {
  display: grid; grid-template-columns: minmax(0, 15rem) 1fr;
  align-items: baseline; gap: 0.4rem 3rem; padding: 1.1rem 0;
  border-bottom: 1px solid var(--glass-border);
}
.cascade > li:last-child { border-bottom: 0; }
.cascade > li > b {
  font-family: var(--display); font-size: clamp(2rem, 5.5vw, 3.2rem);
  color: var(--text-1); line-height: 1; letter-spacing: -0.02em;
}
.cascade > li > span { color: var(--text-2); font-size: 0.95rem; line-height: 1.7; }
.cascade > li > span em { color: var(--text-1); font-style: italic; }
/* struck = superseded by the rung below it.
   ⛔ STRUCK IS NOT HIDDEN. A crossed-out price still has to be READ — the
   descent 38 → 19 → 9 IS the section, and a number nobody can make out
   delivers none of it. This was `--text-3`, which combined with the 0.45
   `.past` opacity below rendered $19,000 and $9,000 as barely-there smudges
   at 1440×900. The LINE marks them superseded; the colour must not. */
.cascade .struck > b {
  color: var(--text-2);
  text-decoration: line-through; text-decoration-thickness: 3px;
  text-decoration-color: var(--mercury-rose);
}
@media (max-width: 620px) { .cascade > li { grid-template-columns: 1fr; } }

/* the punch line sits directly above the doors and is the cascade's last rung */
.cascade-punch { margin-bottom: 2.25rem; }


/* ══════════════════════════════════════════════════════════════════════════
   THE PROGRESSIVE REVEAL.  Isaac, 2026-09-20: "Use scroll effects to make sure
   that the intended value stack happens. Animate it. Its so simple dude. Make
   the experience."

   ⛔ THE DECK IS LITERALLY CALLED `AI_Transformation_Sales_Deck_Progressive_
   Reveal.pptx` AND IT SHIPPED STATIC. Slides 8→13 exist because each rung
   ARRIVES one at a time; a page that prints all three at once has thrown away
   the mechanism and kept the text.

   ⛔ NO LIBRARY, NO SCROLL-JACKING. IntersectionObserver adds `.in`; CSS does
   the rest. The page never steals the scroll — it only reveals as you arrive.

   ⛔ AND IT DEGRADES OPEN, NEVER CLOSED. `.reveal` is only made transparent
   inside `html.js`, which reveal.js sets on itself. JS off, or the observer
   unsupported → every element renders normally. A reveal that hides content
   when its script fails is a page that ships blank.
   ══════════════════════════════════════════════════════════════════════════ */
html.js .reveal { opacity: 0; transform: translateY(18px); }
html.js .reveal.in { opacity: 1; transform: none;
  transition: opacity 620ms cubic-bezier(.22,.61,.36,1), transform 620ms cubic-bezier(.22,.61,.36,1); }
/* the stagger is set per-item by reveal.js as --d, so a row's delay is its
   position in its own list rather than a hardcoded nth-child ladder */
html.js .reveal.in { transition-delay: var(--d, 0ms); }

/* THE TOTAL LANDS LAST AND LANDS HARDER — it is the anchor the whole cascade
   is measured against, so it arrives after the rows that sum to it. */
html.js .cost-total.reveal { transform: translateY(24px) scale(0.96); }
html.js .cost-total.reveal.in { transform: none; }

/* ⛔ THE STRIKE IS A BAR, NOT `text-decoration` — text-decoration cannot be
   animated, and the strike ARRIVING is the whole point of the rung. The bar
   wipes left-to-right as the rung enters. */
.cascade .struck > b { position: relative; text-decoration: none; }
.cascade .struck > b::after {
  /* 46%, not 52% — measured on a real 1440 screenshot, where a 52% bar on
     display-size digits crosses their BOTTOM rather than striking through
     them. 4px because the number is ~60px tall and 3px reads as a hairline. */
  content: ''; position: absolute; left: -0.06em; right: -0.06em; top: 46%;
  height: 4px; background: var(--mercury-rose); border-radius: 2px;
  transform: scaleX(0); transform-origin: left center;
}
html.js .cascade .struck.in > b::after {
  transform: scaleX(1);
  transition: transform 520ms cubic-bezier(.22,.61,.36,1);
  transition-delay: calc(var(--d, 0ms) + 260ms);
}
/* JS off: the bar is simply already drawn, so the rung still reads as struck */
html:not(.js) .cascade .struck > b::after { transform: scaleX(1); }

@media (prefers-reduced-motion: reduce) {
  html.js .reveal, html.js .reveal.in,
  html.js .cost-total.reveal, html.js .cost-total.reveal.in {
    opacity: 1; transform: none; transition: none;
  }
  .cascade .struck > b::after { transform: scaleX(1); transition: none; }
}


/* ── THE DRIVEN RUNGS — `.on` is set by reveal.js from scroll POSITION ───── */
html.js .cascade > li,
html.js .cascade-punch { opacity: 0; transform: translateY(26px); }
html.js .cascade > li.on,
html.js .cascade-punch.on {
  opacity: 1; transform: none;
  transition: opacity 520ms cubic-bezier(.22,.61,.36,1),
              transform 520ms cubic-bezier(.22,.61,.36,1);
}
/* ⛔ ONLY THE SUPERSEDED *DISCOUNT* RUNGS DIM — NEVER THE ANCHOR.
   Seen at a real 1440 (the pane renders ~398px, so this was invisible until
   it was screenshotted at full size): with `.past` on every rung, $38,000 had
   faded to near-unreadable by the time the punch line landed — and the punch
   line REFERS to it. "And I'm still not charging that" is meaningless if
   "that" is the faintest thing on screen. The anchor holds; the two struck
   rungs recede, which is what makes it read as a descent instead of a list. */
html.js .cascade > li.struck.on.past { opacity: 0.75; transition: opacity 420ms ease; }

/* the strike wipes as its own rung lands, not on a global timer */
html.js .cascade .struck.on > b::after {
  transform: scaleX(1);
  transition: transform 460ms cubic-bezier(.22,.61,.36,1) 220ms;
}

.cascade-punch {
  font-family: var(--display); font-size: var(--h2-size);
  color: var(--text-1); margin: 2.5rem 0 0; line-height: 1.25;
}

@media (prefers-reduced-motion: reduce) {
  .cascade-sec { height: auto !important; padding: var(--sec-y) 0 calc(var(--sec-y) * 0.4); }
  .cascade-pin { position: static !important; min-height: 0; padding: 0 1.25rem; }
  html.js .cascade > li, html.js .cascade-punch { opacity: 1 !important; transform: none !important; transition: none; }
  html.js .cascade > li.on.past { opacity: 1 !important; }
  .cascade .struck > b::after { transform: scaleX(1) !important; transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE CAPTURE MODAL — native <dialog>, no library.
   `<dialog>` is deliberate for the same reason `<details>` carries the FAQ:
   focus trapping, Escape-to-close and the backdrop are the browser's job and
   it does them better than a hand-rolled overlay would.
   ══════════════════════════════════════════════════════════════════════════ */
.capture {
  /* ⛔ inset:0 + margin:auto IS the centering. The UA stylesheet centres a
     modal <dialog> that way, and declaring `width` without restoring them
     leaves it hard against the top-left — which is exactly how it shipped
     until it was screenshotted at full size. */
  inset: 0; margin: auto;
  width: min(560px, calc(100vw - 2rem)); max-height: calc(100vh - 3rem);
  padding: 0; border: 1px solid var(--ink); border-radius: var(--r-bp);
  background: var(--paper); color: var(--text-1); overflow: auto;
}
.capture::backdrop { background: rgba(255,255,255,0.82); backdrop-filter: blur(4px); }
.capture .lead-form { padding: 2.25rem; position: relative; }
.capture h2 { font-size: clamp(1.4rem, 3vw, 1.85rem); margin: 0.35rem 0 0.6rem; }
.capture .lede { margin-bottom: 1.75rem; }
.capture .cta-primary { width: 100%; text-align: center; margin-top: 0.5rem; }
.capture .door-fine { text-align: center; }
.capture-x {
  position: absolute; top: 1rem; right: 1.1rem;
  background: none; border: 0; color: var(--text-3);
  font-size: 1.6rem; line-height: 1; cursor: pointer; padding: 0.25rem 0.5rem;
}
.capture-x:hover { color: var(--text-1); }
.qualify[hidden] { display: none; }

/* ── CONSENT — unbundled, never pre-checked ──────────────────────────────── */
.consent {
  display: flex; gap: 0.8rem; align-items: flex-start;
  margin: 1.5rem 0 0.5rem; padding: 1rem 1.1rem;
  border: 1px solid var(--glass-border); border-radius: var(--r-sm);
  color: var(--text-2); font-size: 0.86rem; line-height: 1.6; cursor: pointer;
}
.consent input { margin-top: 0.22rem; flex: none; width: 1rem; height: 1rem; accent-color: var(--mercury-blue); }
.consent:hover { border-color: var(--ink); }
/* the call path REQUIRES it, so it is marked as required rather than the
   viewer discovering it on submit */
.capture.consent-required .consent::after {
  content: 'required'; position: absolute; right: 2.4rem;
  font-family: var(--micro); font-size: 0.68rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-3);
}
.capture.consent-required .consent { position: relative; padding-right: 6rem; }

/* a button that has to look like a text link (the call exit) */
.link-btn {
  background: none; border: 0; padding: 0; font: inherit; cursor: pointer;
  color: var(--ink); border-bottom: 1px solid var(--glass-border);
}
.link-btn:hover { border-bottom-color: var(--mercury-blue); }

/* ── THE PER-CHANNEL CONSENTS ────────────────────────────────────────────── */
.consents { border: 0; margin: 1.75rem 0 0.5rem; padding: 0; }
.consents legend {
  font-family: var(--micro); font-size: 0.72rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-2); padding: 0; margin-bottom: 0.5rem;
}
.consents-note { color: var(--text-3); font-size: 0.82rem; line-height: 1.6; margin: 0 0 0.9rem; }
.consents-note strong { color: var(--text-2); }
.consents .consent { margin: 0 0 0.6rem; }
.consents .consent span strong { color: var(--text-1); font-weight: 500; }
.phone-field { margin-top: 1rem; }
.phone-field[hidden] { display: none; }
.phone-field span em { color: var(--text-3); font-style: normal; text-transform: none; letter-spacing: 0; }


/* ══════════════════════════════════════════════════════════════════════════
   THE DRIVEN WALK — every section is a TRACK with a sticky STAGE.
   Isaac: "force/control the entire experience. never relinquish the scroll
   control to go down faster until they hit the absolute bottom."

   ⛔ THE CONTROL IS DISTANCE, NOT INTERCEPTION. Each section is as tall as its
   content deserves (--track, set by reveal.js from the item count) and the
   stage inside is sticky, so scrolling the track advances the content instead
   of leaving it. The wheel is never touched — there is just more page between
   them and the bottom, and every beat is on the way down.

   ⛔ COLLAPSES COMPLETELY without JS or under reduced-motion: no track, no
   pin, everything visible. See the two blocks at the end.
   ══════════════════════════════════════════════════════════════════════════ */
/* ⛔ HOLD LONG ENOUGH TO BE WATCHED. The timeline runs ~2s whatever the
   scroll did, so a pinned section must stay on screen at least that long.
   --track is typed here because these no longer derive a height from an
   item count. ⛔ ONLY the diagram-only sections are pinned: climb and arc
   carry long lists under their pictures and cannot fit a 100vh stage. */
html.js .frame-sec  { --track: 200vh; }
html.js .box-sec    { --track: 230vh; }
html.js .method-sec { --track: 210vh; }

html.js .frame-sec,
html.js .box-sec,
html.js .method-sec,
html.js .cost-sec,
html.js .stack-sec,
html.js .ledger-sec,
html.js .cascade-sec {
  position: relative; height: var(--track, 300vh); padding: 0;
}
html.js .frame-sec > .wrap,
html.js .box-sec > .wrap,
html.js .method-sec > .wrap,
html.js .cost-sec > .wrap,
html.js .stack-sec > .wrap,
html.js .ledger-sec > .wrap,
html.js .cascade-pin {
  position: sticky; top: 0; min-height: 100vh;
  display: flex; flex-direction: column; justify-content: center;
  padding: 3rem 1.25rem;
}
html.js .frame-sec > .wrap,
html.js .box-sec > .wrap,
html.js .method-sec > .wrap,
html.js .cost-sec > .wrap,
html.js .stack-sec > .wrap,
html.js .ledger-sec > .wrap { width: 100%; max-width: none; }
html.js .frame-sec > .wrap > *,
html.js .box-sec > .wrap > *,
html.js .method-sec > .wrap > *,
html.js .cost-sec > .wrap > *,
html.js .stack-sec > .wrap > *,
html.js .ledger-sec > .wrap > * { max-width: var(--wrap); width: 100%; margin-left: auto; margin-right: auto; }

/* the items themselves arrive as their beat lands */
html.js .cost > li,
html.js .stack > li { opacity: 0; transform: translateY(20px); }
html.js .cost > li.on,
html.js .stack > li.on {
  opacity: 1; transform: none;
  transition: opacity 480ms cubic-bezier(.22,.61,.36,1),
              transform 480ms cubic-bezier(.22,.61,.36,1);
}
/* ⛔ THE COST TOTAL IS NOT DIMMED WHEN SUPERSEDED — it is the anchor the whole
   cascade is measured against, same rule as $38,000 in the cascade. */
html.js .cost > li.cost-total.on.past { opacity: 1; }

/* ── THE FAQ, ONE AT A TIME ──────────────────────────────────────────────
   ⛔ EVERY ANSWER IS WALKED THROUGH, NOT SKIMMED PAST. The current question is
   open and lit; the ones already read recede but stay legible so the reader
   can see how far they have come; the ones ahead are dim. `open` is set on the
   native <details>, so keyboard and screen-reader behaviour is unchanged. */
/* ⛔ THE FAQ IS A PLAIN ACCORDION, AND THAT IS THE RULING.
   Isaac, 2026-09-20: "the faq section is worse than it was before because now
   it just stops you dead and you get lost force scrolling the faq… maybe just
   leave it as a normal faq accordion lmao. we need to control the scroll
   experience UNTIL the faq… OR we need to not control scrolling at all."
   (Superseded, kept: this was a pinned 13.4-screen track — first a sliding
   reel, then a one-card deck with a counter. Both worked exactly as specified
   and both were wrong, for a reason no amount of polish reaches: AN FAQ IS
   LOOKUP, NOT NARRATIVE. The argument above it is a thing you WATCH, so
   pacing it is control; a question list is a thing you CONSULT, and pacing
   that is a wall. Forcing a walk through twenty answers stopped the reader
   dead at the exact point they were closest to buying.)
   ⇒ THE CONTROLLED WALK NOW ENDS AT THE OFFER. Everything above the doors is
   paced; the FAQ is an ordinary accordion the reader opens at will. */

@media (prefers-reduced-motion: reduce) {
  html.js .frame-sec, html.js .box-sec, html.js .climb-sec,
  html.js .method-sec, html.js .arc-sec,
  html.js .cost-sec, html.js .stack-sec, html.js .ledger-sec,
  html.js .cascade-sec {
    height: auto !important; padding: var(--sec-y) 1.25rem !important;
  }
  html.js .cost-sec > .wrap, html.js .stack-sec > .wrap,
  html.js .ledger-sec > .wrap, html.js .cascade-pin,
  html.js .frame-sec > .wrap, html.js .box-sec > .wrap,
  html.js .climb-sec > .wrap, html.js .method-sec > .wrap,
  html.js .arc-sec > .wrap {
    position: static !important; min-height: 0; padding: 0;
  }
  html.js .cost > li, html.js .stack > li { opacity: 1 !important; transform: none !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   THE PERSONAL PAGE (isaac-wostrel-rubin.html) — the same Blueprint System;
   only these few pieces are its own. RULE 02 §14.
   ══════════════════════════════════════════════════════════════════════════ */
.me-hero { grid-template-columns: 1.5fr 1fr; }
.me-hero h1 { margin-bottom: 1.2rem; }
.me-hero .lede { max-width: 54ch; margin-bottom: 1.6rem; }
.me-portrait { display: flex; align-items: center; }
.me-portrait .portrait-frame { width: 100%; margin: 0; }
.me-portrait img { display: block; width: 100%; height: auto; }
.me-jump { display: flex; flex-wrap: wrap; align-items: center; gap: 0.8rem 1.4rem; }
.me-link { font-family: var(--mono); font-weight: 600; font-size: 0.92rem; color: var(--ink);
  text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1px; }
.me-link:hover { text-decoration-thickness: 2px; }
.me-intro { margin: 0 0 1.2rem; max-width: 70ch; color: var(--text-2); line-height: 1.7; }
.me-more { margin-top: 1.8rem; display: flex; flex-wrap: wrap; gap: 0.8rem 2rem; }
.me-story h2 { font-size: clamp(1.3rem, 2.2vw, 1.7rem); margin: 0 0 1rem; }
.me-story .lede, .me-owe .lede { font-size: 1rem; line-height: 1.75; margin-bottom: 1rem; }
.me-story .lede:last-child, .me-owe .lede:last-child { margin-bottom: 0; }
.me-owe .lede { max-width: 72ch; }

/* the paper: its abstract's own sentences left, its status and claim right */
.paper-abs { font-size: 1.1rem; line-height: 1.7; color: var(--ink); margin: 0 0 1rem; max-width: 60ch; }
.paper-card .me-jump { margin-top: 1.4rem; }
.paper-meta .get-k { margin-top: 1rem; }
.paper-meta .get-k:first-child { margin-top: 0; }
.paper-meta .get-v { margin-top: 0.2rem; }

/* the book: a cover (the one place ink fills a card — it is the book's own object, like a film),
   the contents by their glyphs, then passages of Chapter One set as quotations */
.book-card { grid-template-columns: 1fr 1.25fr; }
.book-cover {
  background: var(--ink); color: var(--paper); border-radius: var(--r-lg);
  aspect-ratio: 2 / 3; max-width: 360px; margin: 0 auto; padding: 2rem 1.6rem;
  display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 0.5rem;
}
.book-cover img { width: 58%; height: auto; margin: 1.2rem 0; }
.book-title { margin: 0; font-family: var(--mono); font-weight: 700; font-size: 1.35rem; letter-spacing: -0.01em; line-height: 1.2; }
.book-sub { margin: 0; font-family: var(--mono); font-size: 0.82rem; opacity: 0.8; }
.book-quote { margin: 0; font-style: italic; font-size: 0.92rem; opacity: 0.85; }
.book-author { margin: 0.4rem 0 0; font-family: var(--mono); font-size: 0.78rem; opacity: 0.8; }
.book-toc { list-style: none; margin: 0.8rem 0 0; padding: 0; }
.book-toc li { display: grid; grid-template-columns: 2.4rem 1fr; gap: 0.6rem; padding: 0.45rem 0; border-bottom: 1px solid var(--line); }
.toc-n { font-family: var(--mono); color: var(--text-3); }
.toc-t { font-family: var(--mono); font-weight: 600; color: var(--ink); }
.book-note { margin: 1rem 0 0; font-size: 0.9rem; color: var(--text-2); line-height: 1.6; }
.book-read { margin-top: 1.6rem; }
.book-read .bp-pad > * { max-width: 72ch; }
.book-excerpt { margin: 1rem 0 1.6rem; padding: 0.2rem 0 0.2rem 1.4rem; border-left: 2px solid var(--ink); }
.book-excerpt p { margin: 0 0 0.9rem; font-size: 1.02rem; line-height: 1.75; color: var(--text-1, var(--ink)); }
.book-excerpt p:last-child { margin-bottom: 0; }

/* the code: one outlined card per repository, the whole card is the link */
.repo-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
.repo { display: flex; flex-direction: column; gap: 0.5rem; padding: 1.3rem 1.3rem 1.2rem;
  background: var(--paper); border: 1px solid var(--ink); border-radius: var(--r-lg);
  color: var(--ink); text-decoration: none; transition: background 160ms ease; }
.repo:hover { background: var(--line); }
.repo-name { margin: 0; font-family: var(--mono); font-weight: 700; font-size: 1.02rem; }
.repo-desc { margin: 0; font-size: 0.9rem; line-height: 1.6; color: var(--text-2); flex: 1; }
.repo-note { margin: 0; font-size: 0.86rem; color: var(--ink); font-weight: 500; }
.repo-go { margin: 0.4rem 0 0; font-family: var(--mono); font-size: 0.74rem; color: var(--text-3); overflow-wrap: anywhere; }
.bp-ink .door-fine a { color: var(--paper); text-decoration: underline; text-underline-offset: 3px; }

/* ⛔ AFTER every column rule above, or the desktop grids survive onto phones */
@media (max-width: 900px) {
  .me-hero, .book-card { grid-template-columns: 1fr; }
  .me-portrait .portrait-frame { max-width: 320px; }
}
@media (max-width: 600px) { .repo-grid { grid-template-columns: 1fr; } }

/* ══════════════════════════════════════════════════════════════════════════
   THE FRAMEWORK FUNNEL (framework.html) — a lead-magnet page on the same system. RULE 02 §15.
   Its top-bar button is INK, not red: red means Book a call, and this page's action is the opt-in.
   ══════════════════════════════════════════════════════════════════════════ */
.nav-get {
  flex: none; white-space: nowrap; text-decoration: none;
  font-family: var(--mono); font-weight: 700; font-size: 0.82rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--paper); background: var(--ink); border: 1px solid var(--ink);
  border-radius: var(--r-md); padding: 0.72rem 1.25rem; transition: background 0.15s, color 0.15s;
}
.nav-get:hover { background: var(--paper); color: var(--ink); }
.fw-hero h1 { max-width: 24ch; margin-bottom: 1.2rem; }
.fw-hero .lede { max-width: 60ch; margin-bottom: 0; }
.fw-art { display: block; width: 100%; height: auto; }
/* the opt-in as ONE row under the film: name · email · the button */
.fw-form { max-width: none; }
.fw-form .lead-row { grid-template-columns: 1fr 1fr auto; align-items: end; }
.fw-form .lead-field { margin-bottom: 0; }
.fw-form .cta-primary { margin-top: 0; white-space: nowrap; }
.fw-form .door-fine { margin-top: 0.8rem; }
/* the lead magnet, shown as the thing: its contents page, drawn as a sheet of paper */
.fw-doc-cell { display: flex; align-items: center; justify-content: center; }
.fw-doc {
  width: 100%; max-width: 340px; aspect-ratio: 8.5 / 11; padding: 1.8rem 1.6rem;
  background: var(--paper); border: 1px solid var(--ink); border-radius: 6px;
  display: flex; flex-direction: column;
}
.fw-doc-k { margin: 0; font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.04em; color: var(--text-3); }
.fw-doc-t { margin: 0.6rem 0 1.2rem; font-family: var(--mono); font-weight: 700; font-size: 1.3rem; line-height: 1.2; color: var(--ink); }
.fw-doc-list { margin: 0; padding-left: 1.3rem; flex: 1; }
.fw-doc-list li { padding: 0.32rem 0; border-bottom: 1px solid var(--line); font-size: 0.9rem; color: var(--text-2); }
.fw-doc-foot { margin: 1rem 0 0; font-family: var(--mono); font-size: 0.68rem; color: var(--text-3); }
.fw-big { margin: 0.4rem 0 1rem; font-family: var(--mono); font-weight: 700; font-size: clamp(1.05rem, 1.8vw, 1.3rem); line-height: 1.35; color: var(--ink); }
.fw-shape { display: grid; grid-template-columns: 2rem 1fr; gap: 0.4rem; margin: 0.8rem 0; font-size: 1rem; line-height: 1.6; color: var(--ink); }
.fw-shape span { font-family: var(--mono); font-weight: 700; color: var(--ink); }
.fw-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem; }
.fw-part { background: var(--paper); border: 1px solid var(--ink); border-radius: var(--r-lg); padding: 1.3rem; }
.fw-part .get-v { margin-top: 0.4rem; }
.fw-part-last { grid-column: 1 / -1; }
.fw-n { margin: 0 0 0.5rem; font-family: var(--mono); font-weight: 700; font-size: 1.6rem; line-height: 1; color: var(--text-3); }
.fw-again { margin-top: 1.8rem; }
.fw-list { margin: 0.5rem 0 1.2rem; padding-left: 1.1rem; }
.fw-list li { margin: 0.4rem 0; line-height: 1.6; color: var(--text-2); }
.fw-comm .get-k, .fw-for .get-k { margin-top: 0; }
.fw-comm .get-v + .get-k { margin-top: 1.2rem; }
@media (max-width: 900px) {
  .fw-form .lead-row { grid-template-columns: 1fr; gap: 1rem; }
  .fw-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 600px) {
  .fw-grid { grid-template-columns: 1fr; }
  .nav-get { padding: 0.6rem 0.8rem; font-size: 0.72rem; }
}
/* the autonomy ladder — the triad across, then the grades as rows */
.fw-also { margin: 1.2rem 0 0; }
.fw-grades { list-style: none; margin: 0; padding: 0; }
.fw-grades li { display: grid; grid-template-columns: 6rem 15rem 1fr; gap: 0.4rem 1.2rem; align-items: baseline;
  padding: 0.9rem 0; border-bottom: 1px solid var(--line); }
.fw-g { font-family: var(--mono); font-weight: 700; color: var(--ink); }
.fw-g-t { font-family: var(--mono); font-weight: 600; font-size: 0.92rem; color: var(--ink); }
.fw-g-d { color: var(--text-2); line-height: 1.6; }
.fw-rule-line { margin: 1.2rem 0 0; font-size: 1.02rem; line-height: 1.6; color: var(--text-2); }
.fw-rule-line strong { color: var(--ink); }
.fw-h2b { margin-top: 3.2rem; }
.fw-sub { margin: -0.6rem 0 1.6rem; max-width: 60ch; color: var(--text-2); line-height: 1.6; }
@media (max-width: 900px) { .fw-grades li { grid-template-columns: 1fr; gap: 0.2rem; } }

/* ══════════════════════════════════════════════════════════════════════════
   THE WORKSHEET (framework-worksheet.html) — the lead magnet itself: fill-in tables and lines.
   It prints: under @media print the chrome drops out and the cards lose nothing.
   ══════════════════════════════════════════════════════════════════════════ */
.ws-by { margin: 1rem 0 0; font-family: var(--mono); font-size: 0.8rem; color: var(--text-3); }
.ws .fw-big { margin-top: 0; }
.ws-choice { margin: 0.5rem 0; line-height: 1.6; color: var(--ink); }
.ws-box { display: inline-block; width: 0.95rem; height: 0.95rem; border: 1px solid var(--ink); vertical-align: -2px; margin-right: 0.45rem; }
.ws-scroll { overflow-x: auto; margin-top: 1rem; }
.ws-table { width: 100%; min-width: 620px; border-collapse: collapse; font-size: 0.86rem; }
.ws-table th { text-align: left; font-family: var(--mono); font-weight: 600; font-size: 0.74rem; color: var(--ink);
  padding: 0.55rem 0.6rem; border: 1px solid var(--ink); background: var(--paper); vertical-align: bottom; }
.ws-table td { height: 2.6rem; padding: 0.4rem 0.6rem; border: 1px solid var(--ink); }
.ws-yn, .ws-c { text-align: center; color: var(--text-3); font-family: var(--mono); white-space: nowrap; }
.ws-c { font-size: 1.1rem; }
.ws-ladder { min-width: 760px; }
.ws-qs li { font-weight: 500; color: var(--ink); }
.ws-lines { margin: 0.4rem 0 1.4rem; }
.ws-lines span { display: block; height: 2.2rem; border-bottom: 1px solid var(--text-3); }
.ws-lines-tall span { height: 2.8rem; }
.ws-checks { list-style: none; margin: 0.8rem 0 0; padding: 0; }
.ws-checks li { display: flex; gap: 0.2rem; padding: 0.55rem 0; border-bottom: 1px solid var(--line); line-height: 1.55; color: var(--text-2); }
.ws-checks .ws-box { flex: none; margin-top: 0.25rem; }
@media print {
  .nav, .bp-parcels, .ws-next, .footer { display: none; }
  body::before { display: none; }
  .bp-band { background: var(--paper); border: 0; }
  .bp-sec { padding: 0.6rem 0; }
  .ws-part { break-inside: avoid-page; }
  .ws-scroll { overflow: visible; }
  .ws-table { min-width: 0; }
}
/* the AIOS anatomy, the ChainCompiler levels, TOME — the ladder rows and arrow-node steps, reused */
.fw-grades li { grid-template-columns: 7.5rem 15rem 1fr; }
.fw-grades code { font-family: var(--mono); font-size: 0.85em; color: var(--ink); overflow-wrap: anywhere; }
.fw-cosmos strong { color: var(--ink); font-weight: 600; }
.fw-cosmos .get-v { max-width: 90ch; }
.fw-repo { margin-top: 1.4rem; }
.fw-tome .get-v { margin-top: 0.3rem; }
@media (max-width: 900px) { .fw-grades li { grid-template-columns: 1fr; } }

/* ══ SECTION TINTS (RULE 02 §1, *USER*'s ruling: the sections must be told apart by colour) ═══════
   A tinted section trades the grain for a soft ground in its hue, a 4px top edge in its accent, and
   carries the accent on its label and its card headings. The white cards sit on the tint — that
   contrast is what separates the sections. Hue = meaning: red the problem · gold the framework ·
   violet the theory · cyan what you build · green growth and community. Utility sections stay plain. */
.tint-red    { --t-bg: var(--tint-red-bg);    --t-ac: var(--tint-red); }
.tint-gold   { --t-bg: var(--tint-gold-bg);   --t-ac: var(--tint-gold); }
.tint-violet { --t-bg: var(--tint-violet-bg); --t-ac: var(--tint-violet); }
.tint-cyan   { --t-bg: var(--tint-cyan-bg);   --t-ac: var(--tint-cyan); }
.tint-green  { --t-bg: var(--tint-green-bg);  --t-ac: var(--tint-green); }
.bp-sec.tinted { background-color: var(--t-bg); background-image: none;
  border-top: 4px solid var(--t-ac); border-bottom: 1px solid var(--ink); }
.tinted .eyebrow { color: var(--t-ac); }
/* the section's tab — the square at the head of its label — is FILLED in the accent */
.tinted .eyebrow::before {
  background:
    linear-gradient(var(--t-ac), var(--t-ac)) 11px 50% / calc(100% - 11px) 1px no-repeat,
    linear-gradient(var(--t-ac), var(--t-ac)) 0 0 / 11px 11px no-repeat;
}
/* the nodes where a card's dividers meet its edge, and the arrow nodes, take the accent too */
.tinted .bp-rule::before, .tinted .bp-rule::after,
.tinted .bp-split > .bp-cell + .bp-cell::before, .tinted .bp-split > .bp-cell + .bp-cell::after {
  background: var(--t-ac); border-color: var(--t-ac);
}
.tinted .step-arrow { border-color: var(--t-ac); color: var(--t-ac); }
/* the card icons (the .get-ico tiles, Lucide line shapes) take the accent */
.tinted .get-ico { border-color: var(--t-ac); }
.tinted .get-ico svg { stroke: var(--t-ac); }
.tinted .eyebrow > .pill { border-color: var(--t-ac); color: var(--t-ac); font-weight: 600; }
.tinted .get-k, .tinted .fw-g, .tinted .fw-n, .tinted .repo-name, .tinted .toc-n { color: var(--t-ac); }
/* the bootcamp: the map, then the client's path as four arrowed steps, then the tools grouped by job */
.fw-path { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.fw-path > .bp-cell { padding-top: 1.1rem; }
.fw-step-k { margin: 0; font-family: var(--mono); font-weight: 700; font-size: 0.82rem; color: var(--ink); }
.tinted .fw-step-k { color: var(--t-ac); }
.fw-step-t { margin: 0.25rem 0 0.5rem; font-family: var(--mono); font-weight: 700; font-size: 1.1rem; color: var(--ink); }
.fw-tools-k { margin: 2.4rem 0 1rem; }
.fw-group { margin: 0 0 0.9rem; padding-bottom: 0.6rem; border-bottom: 1px solid var(--line);
  font-family: var(--mono); font-weight: 700; font-size: 1.02rem; color: var(--ink); }
.fw-part .get-k + .get-v { margin-bottom: 0.9rem; }
.fw-part .get-v:last-child { margin-bottom: 0; }
@media (max-width: 900px) { .fw-path { grid-template-columns: 1fr; } }
/* icons on the framework page: a tile above each path step, one in each tool-group heading,
   and a small inline one leading each ladder row (it inherits the row label's colour) */
.fw-step-ico { margin-bottom: 0.7rem; }
.fw-group { display: flex; align-items: center; gap: 0.7rem; }
.fw-ico { display: inline-flex; vertical-align: -3px; margin-right: 0.45rem; }
.fw-ico svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round; }
.fw-part .get-head { margin-bottom: 0.3rem; }
.bp-cell > .get-head:first-child, .bp-cell > .step-arrow + .get-head { margin-bottom: 0.5rem; }
