/* ============================================================================
   Timbrel — the "Printed Signal" fusion system.

   Extracted 2026-08-09 from _restyle/fusion-sell-v2.html, where all 1,252 lines
   of it lived inline. The comments below were written against that file and are
   kept verbatim: almost every one records a defect this system already shipped
   once, and re-deriving them costs more than carrying them.

   WHAT BELONGS HERE: tokens, the reset, the chrome (nav, footer, seal), and any
   component a second page could plausibly use. WHAT DOES NOT: rules that exist
   for one page's one section. Those stay inline on that page. The line to hold
   is that nine pages must share ONE system — the failure mode this file exists
   to prevent is nine pages each carrying their own copy of it, which is how one
   system quietly becomes nine.

   Read ../DESIGN.md (the negative law: what may never appear on this site) and
   _restyle/FUSION-SYSTEM.md (the positive system, the traps, the owner
   overrides) before changing anything in here.

   Four inks, and the roles are not decoration — do not reassign them:
     cream --paper  what you take away: actions, the artifact, the file
     mint  --mint   the engine running: live state, focus, a clean result
     gold  --gold   YOU: only what is specifically the visitor's own voice
     red   --flag   the machine's tells: nothing else, ever
   ============================================================================ */

:root{
  --ink:#06231f; --ink-2:#0a2e27; --ink-3:#041916;
  --paper:#f4f0e4; --cream:#faf7ef; --sage:#e3efe2;
  /* on-ink-3 was #6f9682 — 4.44:1 on the raised ink-2 panels, just under AA */
  --on-ink:#eaf7ee; --on-ink-2:#a8c9b8; --on-ink-3:#7da089;
  --t1:#06231f; --t2:#2e4a40; --t3:#5d6f66;
  /* THREE INKS — roles, not swatches.
     cream = what you take away (actions, the artifact, the file)
     mint  = the engine running (live state, focus, the anti-AI filter)
     gold  = you (anything that is specifically the visitor's own voice)

     The law was declared here and then broken six ways: gold carried the
     exhibit label, the machine's struck tells, the ledger descriptions, the
     press-note kicker, a figure caption and a "most chosen" flag — none of
     which is the visitor's voice. Gold is now spent ONLY on the fingerprint
     and the filename of the visitor's own file. Everything the filter strikes
     is mint, because striking is the engine running. */
  --mint:#8af0a8; --mint-deep:#167a52;
  /* FOURTH ROLE, added 2026-08-09 — the machine's tells, and nothing else.
     The eight flagged phrases in Exhibit A were mint, on the reasoning that
     striking them is "the engine running". That reasoning is internal: the
     reader has no access to it and reads GREEN AS GOOD, so the page was
     highlighting its own villain in its approval colour. Gold could not take
     the job either — gold is the visitor's own voice, and the machine's tells
     are the opposite of that. So the tells get their own ink: a proofing red,
     the colour an editor's pen has marked errors in for a century, and a normal
     riso plate. It appears ONLY on the machine's tells — never on the visitor,
     never on the product, never as decoration. 5.47:1 on ink. */
  --flag:#e9705a;
  --flag-wash:rgba(233,112,90,.15);
  --flag-wash-hi:rgba(233,112,90,.26);
  --gold:#e6b873;        /* 9.04:1 on ink */
  --gold-deep:#8f5f1c;   /* was #b07a2e — 3.46:1 on cream failed small text; now 5.28:1 */
  --gold-line:rgba(230,184,115,.3);
  --line:rgba(244,240,228,.16); --line-2:rgba(244,240,228,.34);
  --rule:rgba(6,35,31,.14); --rule-2:rgba(6,35,31,.3);
  --disp:'Archivo',system-ui,sans-serif;
  --serif:'Fraunces',Georgia,serif;
  --mono:'JetBrains Mono',ui-monospace,monospace;
  /* TYPE ROLE SCALE — added 2026-08-05.
     The page had accumulated 48 distinct font sizes, including eight doing
     "small mono label" and eight doing "body copy". Sizes .93 / .94 / .95rem
     cannot signal three different jobs, so the clusters collapse onto named
     roles. Roles are named for purpose; one-offs that are genuinely their own
     role (the seal's ring text, the guarantee numeral, the code block) keep
     their own values and are not forced into the ramp.
     Steps are ≥10% apart so each is legible as a different job. */
  --t-display-xl:clamp(2.3rem,6.4vw,4.9rem);  /* hero H1, once per page */
  --t-display-l:clamp(2rem,4.1vw,3.15rem);    /* major dark beats */
  --t-display-m:clamp(1.7rem,3.5vw,2.6rem);   /* section heads on ink */
  --t-serif-l:clamp(1.8rem,3.4vw,2.75rem);    /* light-band h2 */
  --t-serif-s:1.12rem;                        /* light-band h3 */
  --t-lede:1.12rem;                           /* was 1.02 — only 2% over body,
                                                 so the size step did nothing */
  --t-body:1rem;                              /* running prose; the 16px floor */
  --t-body-s:.9rem;                           /* genuinely secondary prose */
  --t-label-lg:.72rem;                        /* eyebrows, nav, buttons — 11.52px */
  --t-label:.69rem;                           /* mono labels, credits, captions — 11.04px,
                                                 held above the 11px functional-text floor;
                                                 these carry heavy tracking and land on mobile */
  --t-head-s:.95rem;                          /* small uppercase card/row heads */
  --t-data:1.02rem;                           /* mono readout values */
  /* Light-on-dark needs compensating on all three axes. Cream on ink blooms,
     so ink-ground prose gets a touch more tracking and leading than the same
     copy would need on the cream bands, where these reset to normal. */
  --track-on-ink:.006em;
  --lh-prose:1.72;
  --e-out:cubic-bezier(.22,1,.36,1);
  /* just enough to take the edge off. Anything above ~4px starts reading as SaaS. */
  --r:3px; --r-lg:4px;
}
/* opt this document into cross-document view transitions; the destination
   opts in too and shares the `press-seal` name, so the stamp you press
   becomes the page you land on. Ignored by browsers that don't support it. */
@view-transition{navigation:auto}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{font-family:var(--disp);background:var(--ink);color:var(--on-ink);line-height:1.55;
  font-size:16px;-webkit-font-smoothing:antialiased;overflow-x:clip;position:relative}
/* print scanline over everything */
body::after{content:"";position:fixed;inset:0;pointer-events:none;z-index:60;opacity:.5;
  background:repeating-linear-gradient(0deg,rgba(244,240,228,.028) 0 1px,transparent 1px 3px)}
img{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
/* Keyboard focus. Only the seal had a visible ring; the other 44 focusable
   elements were relying on the UA default over a dark custom surface. */
:focus-visible{outline:2px solid var(--mint);outline-offset:3px;border-radius:2px}
.band-light :focus-visible{outline-color:var(--mint-deep)}
.faq summary:focus-visible{outline-offset:-2px}
::selection{background:var(--mint);color:var(--ink)}
/* visually hidden but still reachable by assistive tech, e.g. a live-region
   announcement for a swap component too far off-screen to show visibly */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.wrap{max-width:1200px;margin:0 auto;padding:0 clamp(20px,4vw,50px)}

/* ---------- shared type ---------- */
.h-disp{font-variation-settings:'wdth' 122;font-weight:800;text-transform:uppercase;
  line-height:1.04;color:var(--paper)}
.kick{font-family:var(--mono);font-size:var(--t-label-lg);letter-spacing:.42em;text-transform:uppercase;
  color:var(--mint)}
.eyebrow{font-family:var(--mono);font-size:var(--t-label-lg);letter-spacing:.34em;text-transform:uppercase;
  color:var(--mint);display:block}
.lede{color:var(--on-ink-2);max-width:620px;font-size:var(--t-lede);line-height:1.6;
  letter-spacing:var(--track-on-ink)}
/* One axis, per DESIGN.md's emphasis budget. This carried BOTH 'wdth' 125 and
   weight 900 against an H1 at 118/600 — two signals on one phrase. */
em.accent{font-style:normal;font-weight:900}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;gap:10px;font-family:var(--mono);font-weight:700;
  font-size:.74rem;letter-spacing:.14em;text-transform:uppercase;padding:15px 24px;
  border:1.5px solid var(--paper);color:var(--paper);border-radius:var(--r);
  transition:background .15s,color .15s,transform .15s var(--e-out),box-shadow .15s var(--e-out)}
.btn:hover{transform:translate(-2px,-2px);box-shadow:3px 3px 0 var(--mint-deep)}
.btn-solid{background:var(--paper);color:var(--ink)}
.btn-solid:hover{background:var(--mint);border-color:var(--mint);box-shadow:3px 3px 0 rgba(244,240,228,.35)}
.btn-ink{border-color:var(--ink);color:var(--ink)}
.btn-ink:hover{background:var(--ink);color:var(--mint);box-shadow:3px 3px 0 var(--mint)}
/* padding-block gives these a real touch height; at .72rem the box was 19-26px
   tall on a phone, well under the 44px target floor. */
.arrow{display:inline-block;font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--mint);border-bottom:1px solid rgba(138,240,168,.4);
  padding:14px 0 11px;transition:border-color .18s,color .18s}
.arrow:hover{color:var(--paper);border-color:var(--paper)}
/* #167a52 on the sage band measured 4.495:1 — a rounding error short of AA.
   #12603f clears it at 5.6:1 without changing the hue role. */
.arrow-ink{color:#12603f;border-bottom-color:rgba(18,96,63,.4)}
.arrow-ink:hover{color:var(--ink);border-color:var(--ink)}
/* Every arguing section now leaves an on-path action beside the off-path one.
   The secondary reads quieter so the pair is not two equal choices. */
.side-acts{display:flex;flex-wrap:wrap;gap:10px clamp(20px,3vw,34px);align-items:baseline}
.arrow-2{color:var(--on-ink-3);border-bottom-color:var(--line-2)}
.arrow-2:hover{color:var(--on-ink);border-color:var(--on-ink-3)}
.arrow-ink.arrow-2{color:var(--t3);border-bottom-color:var(--rule-2)}
.arrow-ink.arrow-2:hover{color:var(--t1);border-color:var(--t3)}
.flood-act{margin-top:26px}
.mz-act{margin-top:18px}

/* ---------- nav ---------- */
.nav{border-bottom:1px solid var(--line);position:relative;z-index:20}
.nav .wrap{display:flex;align-items:center;justify-content:space-between;height:64px}
.nav .logo{display:inline-flex;align-items:center;padding:12px 0}
.nav .logo img{height:20px;filter:brightness(0) invert(.95)}
.nav-links{display:flex;align-items:center;font-family:var(--mono);font-size:var(--t-label-lg);
  letter-spacing:.16em;text-transform:uppercase}
.nav-links a{padding:8px 12px;color:var(--on-ink-2);transition:color .15s;white-space:nowrap}
.nav-links a:hover{color:var(--mint)}
.nav .btn{padding:9px 15px;border-width:1px;margin-left:8px}
/* Below 1040px the section links used to be display:none with no hamburger
   behind them, leaving a 20,000px page with no navigation at all. They now
   drop to a scrolling strip on a second row; the CTA holds the first row. */
@media(max-width:1040px){
  .nav .wrap{flex-wrap:wrap;height:auto;padding-top:12px;row-gap:2px}
  .nav-links{order:3;width:100%;overflow-x:auto;overflow-y:hidden;
    -webkit-overflow-scrolling:touch;scrollbar-width:none;
    margin:0 calc(-1 * clamp(20px,4vw,50px));padding:0 clamp(20px,4vw,50px) 10px;
    -webkit-mask-image:linear-gradient(to right,#000 0,#000 calc(100% - 36px),transparent 100%);
    mask-image:linear-gradient(to right,#000 0,#000 calc(100% - 36px),transparent 100%)}
  .nav-links::-webkit-scrollbar{display:none}
  /* JS (initNavFade in fusion.js) removes the fade once scrolled to the true
     end, so it never permanently obscures the last link. Without JS the fade
     stays — an honest signal that there's more, since overflow-x:auto still
     scrolls it by touch/trackpad with no script running. */
  .nav-links.at-end{-webkit-mask-image:none;mask-image:none}
  /* flex:none is load-bearing and was missing. Without it the links are
     shrinkable flex items, so instead of overflowing into the scroll this rule
     sets up, they get squeezed narrower than their own `white-space:nowrap`
     text and the glyphs spill out of the boxes — at 390px the row rendered as
     "THE FILEHE DRIFVOICESHOW IT WORKAQ". It passed every overflow check we
     had, because the items shrinking is exactly what keeps scrollWidth equal
     to clientWidth. Found by looking at it, 2026-08-09. */
  .nav-links a{padding:14px 10px;font-size:var(--t-label-lg);min-width:44px;
    text-align:center;flex:none}
  .nav .nav-cta{margin-left:0}
}
/* was .64rem = 10.24px, the only functional control under the 11px floor */
@media(max-width:560px){.nav .btn{padding:13px 14px;letter-spacing:.06em;font-size:var(--t-label-lg)}}

/* ---------- readout (V1 table, re-skinned, no chrome) ---------- */
.readout{background:var(--ink-2);border-bottom:1px solid var(--line)}
.readout .wrap{padding:0}
/* Two cells, deliberately unbalanced (DESIGN.md: no even stat rows) — was
   four equal columns repeating "Living dimensions · 13" from the hero-bar
   stat pair 25 lines above. Format moved into the dossier spec list below,
   where it isn't a duplicate. */
.rd{display:grid;grid-template-columns:.74fr 1.26fr;border-left:1px solid var(--line)}
.rd .cell{padding:26px clamp(16px,2.4vw,28px);border-right:1px solid var(--line);border-radius:var(--r)}
/* min-height holds both values on one baseline: "Signals listened for"
   wraps at mobile and used to drop its numeral ~28px below its neighbour. */
.rd .k{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.2em;text-transform:uppercase;
  color:var(--on-ink-3);min-height:2.9em}
/* on-ink-3 (inherited from .k) @ .62 opacity measured 2.84:1 on ink — below
   4.5:1 AA. Same defect class as build.html's .vp-note (2026-08-10 opacity
   sweep): on-ink-2 @ .7 opacity clears it at ~5.2:1, verified live. */
.rd .k .sp{display:block;letter-spacing:.14em;color:var(--on-ink-2);opacity:.7}
.rd .v{font-family:var(--disp);font-variation-settings:'wdth' 112;font-weight:700;
  font-size:1.45rem;margin-top:9px;color:var(--paper);letter-spacing:-.01em}
.rd .v small{font-family:var(--mono);font-size:var(--t-label-lg);font-weight:400;letter-spacing:.1em;
  color:var(--on-ink-3);margin-left:4px}
.rd .v.mono{font-family:var(--mono);font-size:var(--t-data);font-weight:500;letter-spacing:.02em}
/* gold = yours. The fingerprint is the one value on this strip that belongs to
   the visitor rather than to the product, so it is the one value in gold. */
.rd .v.you{color:var(--gold)}
/* ---------- the filter, in action ----------
   Replaces a sentence-rhythm chart that sat in the highest-attention strip on
   the page and required chart literacy nobody has yet. These words need no
   decoder: anyone who uses AI recognises them on sight, and seeing them struck
   out lands the differentiator inside the first screen. */
.rd-filter{border-top:1px solid var(--line);border-left:1px solid var(--line);
  border-right:1px solid var(--line);padding:18px clamp(16px,2.4vw,28px) 24px;
  border-radius:0 0 var(--r) var(--r)}
.rd-filter .k{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.2em;text-transform:uppercase;
  color:var(--on-ink-3);display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap}
.rd-filter .k .live{color:var(--mint)}
.tells{display:flex;flex-wrap:wrap;gap:10px clamp(18px,3vw,34px);margin-top:16px}
.tell{position:relative;font-family:var(--disp);font-variation-settings:'wdth' 108;
  font-weight:600;font-size:clamp(.95rem,1.6vw,1.22rem);color:var(--on-ink-3);
  white-space:nowrap;transition:color .18s}
/* the strike is drawn, not text-decoration, so it can sit optically. Red, not
   mint, since 2026-08-09: these ARE the machine's tells, so they take the same
   ink the flagged phrases take 900px below. A list can be struck without
   costing legibility — a paragraph cannot, which is why the exhibit is
   highlighted instead. */
.tell::after{content:"";position:absolute;left:-2px;right:-2px;top:54%;height:2px;
  background:var(--flag);opacity:.95;transform:scaleX(1);transform-origin:left center}
.rd-filter:hover .tell{color:var(--on-ink-2)}
@media(max-width:520px){.tells{gap:8px 18px}}
@media(max-width:440px){.rd{grid-template-columns:1fr}
  .rd .cell{border-top:1px solid var(--line)}
  .rd .cell:first-child{border-top:0}}

/* ---------- light editorial band ---------- */
/* ---------- light bands: paper, not a flat fill ----------
   The dark half of this page is printed artwork; the light half was four flat
   swatches. These bands now carry a warm top-to-bottom stock gradient and a
   faint halftone dot field, so cream reads as paper of the same press. */
.band-light{position:relative;color:var(--t1);
  background:linear-gradient(180deg,#fdfbf5 0%,var(--cream) 38%,#f2efe4 100%)}
/* the sage band's gradient never goes darker than --sage: at #dcebdb the small
   mono indices dropped to 4.31:1 and gold-deep to 4.44:1 */
.band-light.sage{background:linear-gradient(180deg,#f1f8f0 0%,var(--sage) 62%,var(--sage) 100%)}
/* z-index:-1 is load-bearing, not decoration: without it this positioned
   pseudo-element (z-index:auto) paints ABOVE normal-flow static content per
   CSS stacking rules, despite being "::before". `.band-light > .wrap` below
   worked around this for the homepage/research/voices templates (which use
   .wrap as the direct child), but blog.css's templates use different direct-
   child classes (.post-list, article-wrap, <article>) that never got that
   same lift — so the dot layer sat on top of blog cards, hero images and
   body text, only losing the stacking fight when something like a hover
   transform gave the content its own stacking context. z-index:-1 fixes the
   pseudo-element itself so no future .band-light child needs to opt in. */
.band-light::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.55;
  z-index:-1;
  background-image:radial-gradient(rgba(6,35,31,.07) 1px,transparent 1.15px);
  background-size:7px 7px}
.band-light > .wrap{position:relative;z-index:1}
.band-light h2{font-family:var(--serif);font-weight:600;font-size:clamp(1.8rem,3.4vw,2.75rem);
  line-height:1.14;letter-spacing:-.012em;color:var(--t1);text-transform:none}
.band-light h3{font-family:var(--serif);font-weight:500;font-size:var(--t-serif-s);letter-spacing:-.008em}
.band-light p{color:var(--t2)}
.band-light .idx{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.24em;
  text-transform:uppercase;color:var(--t3)}
.band-light .eyebrow{color:var(--mint-deep)}

/* ---------- vertical rhythm ----------
   A four-step scale, assigned by what a section IS, not by where it falls:
     wide  — the three major beats (manifesto, pricing, close)
     mid   — the two dense anchors (dimensions, dossier)
     base  — the evidence run (research, voices, features, FAQ)
     tight — the one transition (how it works)
   Fluid, so a 390px phone doesn't spend 150px of its screen on band padding. */
:root{
  --s-tight:clamp(48px,7vw,72px);
  --s-base:clamp(60px,9vw,96px);
  --s-mid:clamp(72px,11vw,118px);
  --s-wide:clamp(88px,13vw,152px);
}
section{padding:var(--s-base) 0}
.pad-lg{padding:var(--s-wide) 0}
.pad-md{padding:var(--s-mid) 0}
.pad-sm{padding:var(--s-tight) 0}

/* ---------- ledger rows (light bands) — picked layout C3 ----------
   Rows, not columns. 26px of air above and below every rule, and a dotted
   leader carrying the eye across. Echoes the 13-dimension ledger. */
.led{margin-top:44px;border-top:1px solid var(--rule-2)}
.led .r{display:grid;grid-template-columns:72px 1fr minmax(0,1.5fr);gap:clamp(18px,3vw,44px);
  align-items:baseline;padding:26px 12px 26px 0;border-bottom:1px solid var(--rule);
  transition:background .16s,transform .16s var(--e-out)}
.led .r:hover{background:rgba(255,255,255,.55);transform:translateX(12px)}
.led .r p{font-size:var(--t-body-s)}
.led .r .hd{display:flex;align-items:baseline;gap:12px}
.led .r .dots{flex:1;border-bottom:1px dotted var(--rule-2);transform:translateY(-4px);min-width:20px}
@media(max-width:760px){.led .r{grid-template-columns:64px 1fr;gap:16px}
  .led .r .dots{display:none}.led .r p{grid-column:2;margin-top:6px}}

/* ---------- voices (ink) ---------- */
.brief{border:1.5px solid var(--line-2);display:grid;grid-template-columns:150px 1fr;
  border-radius:var(--r-lg) var(--r-lg) 0 0;
  margin-top:40px;align-items:stretch;overflow:clip}
.brief .tag{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.2em;text-transform:uppercase;
  color:var(--on-ink-3);padding:22px;border-right:1px solid var(--line);display:flex;align-items:center}
.brief .txt{padding:22px 26px;font-family:var(--disp);font-variation-settings:'wdth' 105;
  font-weight:600;font-size:clamp(1rem,1.9vw,1.32rem);color:var(--paper);display:flex;align-items:center}
@media(max-width:640px){.brief{grid-template-columns:1fr}
  .brief .tag{border-right:0;border-bottom:1px solid var(--line);padding-bottom:14px}}
.vcards{display:grid;grid-template-columns:repeat(3,1fr);gap:0;margin-top:0;
  border:1.5px solid var(--line-2);border-top:0;overflow:clip}
.vswap:not(.vs-on) .vcards{border-radius:0 0 var(--r-lg) var(--r-lg)}
/* the cards are buttons now, so every inherited text style has to be restated */
.vcard{display:block;width:100%;text-align:left;background:none;border:0;
  font:inherit;color:inherit;cursor:pointer;
  padding:26px 24px 28px;border-right:1px solid var(--line);
  transition:background .18s,box-shadow .18s}
.vcard:last-child{border-right:0}
.vcard:hover{background:var(--ink-2)}
/* SELECTED STATE, third attempt — reworked 2026-08-09 for the same reason the
   FAQ's was. It was a 3px mint bar across the card's foot, which became a 3px
   bar down its LEFT EDGE below 900px, and a thick coloured border on one side
   of a card is the single most recognisable generated-UI signature there is —
   on the site that sells anti-AI-voice tooling. §6.3 killed exactly this on the
   FAQ and the voice cards kept it, because the detector only reads inline CSS
   on an HTML file and never saw them until the system was extracted.
   Marked the way this system marks anything now: it is PRINTED. The card picks
   up the same halftone dot screen the cream bands use, and a hairline mint rule
   is ruled under the writer's name. No bar, no fill, no shadow. */
.vs-on .vcard.is-on{background:var(--ink-2);
  background-image:radial-gradient(rgba(234,247,238,.06) 1px,transparent 1.15px);
  background-size:7px 7px}
.vs-on .vcard.is-on .who{box-shadow:inset 0 -1px 0 rgba(138,240,168,.55)}
/* padding-bottom is reserved unconditionally (not just on .is-on) so selecting
   a card doesn't grow it 7px and shift the stage further from the tap */
.vcard .who{display:block;font-family:var(--disp);font-weight:700;text-transform:uppercase;
  letter-spacing:.05em;font-size:.88rem;color:var(--paper);padding-bottom:7px}
.vcard .role{display:block;font-family:var(--mono);font-size:var(--t-label);letter-spacing:.1em;
  color:var(--on-ink-3);margin-top:6px;line-height:1.5}
/* the affordance: without it a card is a card, not a control */
.vpick{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);
  font-size:var(--t-label);letter-spacing:.18em;text-transform:uppercase;
  color:var(--on-ink-3);transition:color .18s}
.vpick::after{content:"\2193";font-size:1.05em;transition:transform .25s var(--e-out)}
.vcard:hover .vpick{color:var(--mint)}
.vs-on .vcard.is-on .vpick{color:var(--mint)}
.vs-on .vcard.is-on .vpick::after{transform:translateY(2px)}
.vswap:not(.vs-on) .vpick{display:none}
.dials{margin:20px 0 18px;display:grid;gap:9px}
/* 78px could not hold "Vulnerability" at .0832em tracking: it needed 98px and
   spilled 20px into the track beside it. Both fixed columns are now sized to
   their real longest strings ("Vulnerability" and "analytical") at the reduced
   tracking; the first attempt at this only moved the overflow into .read. */
.dial{display:grid;grid-template-columns:92px 1fr 76px;gap:10px;align-items:center;
  font-family:var(--mono);font-size:var(--t-label);letter-spacing:.05em;text-transform:uppercase;
  color:var(--on-ink-3)}
.dial .track{height:1px;background:var(--line-2);position:relative}
.dial .track i{position:absolute;top:-2px;left:var(--pos);width:5px;height:5px;background:var(--mint)}
.dial .read{text-align:right;color:var(--on-ink-2)}
@media(max-width:900px){.vcards{grid-template-columns:1fr}
  .vcard{border-right:0;border-bottom:1px solid var(--line)}.vcard:last-child{border-bottom:0}}

/* ---------- the reading pane ----------
   Same contract as the exhibit swap: `.vs-on` is script-added and is the only
   thing that turns three stacked responses into one switchable pane, so with
   no JS the section is longer and completely readable. */
.vstage{position:relative;border:1.5px solid var(--line-2);border-top:0;
  border-radius:0 0 var(--r-lg) var(--r-lg);overflow:clip}
.vswap:not(.vs-on) .vstage{border-top:1px solid var(--line)}
.vswap:not(.vs-on) .vpane + .vpane{border-top:1px solid var(--line)}
.vs-on .vstage{display:grid;grid-template-rows:var(--vh,320px);
  transition:grid-template-rows .5s cubic-bezier(.16,1,.3,1)}
.vs-on .vpane{position:absolute;inset:0 0 auto 0;opacity:0;visibility:hidden;pointer-events:none}
.vs-on .vpane.is-on{opacity:1;visibility:visible;pointer-events:auto}
.vs-on .vpane.is-in{z-index:2;animation:printPane 620ms cubic-bezier(.4,0,.15,1) both}
.vs-on .vstage.vs-measure{display:block;grid-template-rows:none;transition:none}
.vs-on .vstage.vs-measure .vpane{position:static;opacity:0;visibility:hidden}
.vs-blade{position:absolute;top:0;bottom:0;left:0;width:2px;z-index:3;opacity:0;
  background:var(--mint);box-shadow:0 0 16px 3px rgba(138,240,168,.45);pointer-events:none}
.vs-blade.run{animation:bladeRun 620ms cubic-bezier(.4,0,.15,1) both}
.vpane{padding:26px clamp(18px,3vw,34px) 28px;background:var(--ink-2)}
.vp-k{display:flex;align-items:baseline;justify-content:space-between;gap:16px;flex-wrap:wrap;
  font-family:var(--mono);font-size:var(--t-label);letter-spacing:.2em;text-transform:uppercase;
  color:var(--paper);padding-bottom:14px;border-bottom:1px solid var(--line);margin-bottom:20px}
.vp-n{color:var(--on-ink-3)}
/* in-fiction: this is product output, so DESIGN.md's voice rules do not apply
   to the words inside it and the em-dashes are the writers' own */
.vp-body p{color:var(--on-ink-2);font-size:var(--t-body);max-width:66ch;
  line-height:var(--lh-prose);letter-spacing:var(--track-on-ink)}
.vp-body p + p{margin-top:15px}
.vp-tell{margin-top:22px;padding-top:15px;border-top:1px dashed var(--line);
  font-family:var(--mono);font-size:var(--t-label);line-height:1.75;letter-spacing:.04em;
  color:var(--on-ink-3);max-width:74ch}
@media(prefers-reduced-motion:reduce){
  .vs-on .vpane.is-in{animation:none}
  .vs-blade{display:none}
  .vs-on .vstage{display:block;grid-template-rows:none;transition:none}
  .vs-on .vpane{position:relative;visibility:visible;opacity:1;pointer-events:auto}
  .vs-on .vpane:not(.is-on){display:none}
}

/* ---------- steps: "the run" — picked layout D1 ----------
   One hairline carries four numbered dots, so the rule does work instead of
   just dividing. The last dot goes ink-and-mint: that's the finished file. */
.run{margin-top:52px;position:relative}
.run .line{position:absolute;top:19px;left:0;right:0;height:1px;background:var(--rule-2)}
.run .g{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(18px,3vw,40px);position:relative}
.run .s .dot{width:38px;height:38px;border-radius:50%;background:var(--cream);
  border:1px solid var(--rule-2);display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-size:var(--t-label-lg);color:var(--t2);position:relative;z-index:2}
.run .s:last-child .dot{background:var(--ink);color:var(--mint);border-color:var(--ink)}
.run .s h3{margin-top:22px}
.run .s p{font-size:var(--t-body-s);margin-top:9px;max-width:30ch}
@media(max-width:820px){.run .g{grid-template-columns:repeat(2,1fr);gap:34px}.run .line{display:none}}
@media(max-width:520px){.run .g{grid-template-columns:1fr}}

/* ---------- feature cards (ink, hard-shadow hover) ---------- */
.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:44px}
.card{border:1px solid var(--line);padding:26px 24px 28px;
  transition:transform .18s var(--e-out),box-shadow .18s var(--e-out),border-color .18s;border-radius:var(--r-lg)}
.card:hover{transform:translate(-3px,-3px);box-shadow:4px 4px 0 var(--mint-deep);
  border-color:var(--line-2)}
.card .n{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.22em;color:var(--on-ink-3)}
.card h3{font-family:var(--disp);font-variation-settings:'wdth' 112;font-weight:800;
  text-transform:uppercase;font-size:var(--t-head-s);letter-spacing:.03em;color:var(--paper);margin-top:14px}
.card p{font-size:var(--t-body-s);color:var(--on-ink-2);margin-top:10px;
  letter-spacing:var(--track-on-ink)}
@media(max-width:900px){.cards{grid-template-columns:1fr}}

/* ---------- ruled key:value ledgers — the house way to present facts ---------- */
.spec{margin-top:30px;border-top:1px solid var(--line)}
.spec .r{display:grid;grid-template-columns:130px 1fr;gap:18px;padding:12px 0;
  border-bottom:1px solid var(--line);font-size:var(--t-body-s)}
.spec .r dt{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.2em;text-transform:uppercase;
  color:var(--on-ink-3);padding-top:3px}
.spec .r dd{color:var(--on-ink)}
.spec .mut{color:var(--on-ink-3)}
.spec .mono{font-family:var(--mono);font-size:.84rem;color:var(--gold)}
.spec .mono .mut{color:var(--on-ink-3)}

.act-terms{margin-top:18px;border-top:1px solid var(--line);max-width:44ch;width:100%}
.act-terms > div{display:flex;align-items:baseline;gap:14px;padding:7px 0;
  border-bottom:1px solid var(--line)}
.act-terms dt{font-family:var(--mono);font-size:var(--t-label-lg);letter-spacing:.1em;
  text-transform:uppercase;color:var(--paper);flex:none;min-width:8.4em}
.act-terms dd{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.06em;
  color:var(--on-ink-3);line-height:1.5}
@media(max-width:1080px){.act-terms{margin-left:auto;margin-right:auto}}
@media(max-width:420px){.act-terms dt{min-width:7.2em}}

/* ---------- faq (ink) ---------- */
.faqs{margin-top:40px;border-top:1px solid var(--line)}
.faq{border-bottom:1px solid var(--line);transition:background .25s var(--e-out)}
/* Open state, third attempt. It was a gold background wash (gold as a fill,
   which DESIGN.md forbids), then a 2px mint bar down the left edge — which is
   the single most recognisable AI-house-style card treatment there is, on the
   page whose whole pitch is not looking like that. The open row is now marked
   the way this page marks anything: it is PRINTED. The ground picks up the
   same halftone dot screen the cream bands use, and a mint rule is ruled under
   the question, so an open entry reads as one that has been marked up on a
   proof sheet. No bar, no fill, no shadow. */
.faq[open]{background-image:radial-gradient(rgba(234,247,238,.06) 1px,transparent 1.15px);
  background-size:7px 7px}
.faq[open] summary{box-shadow:inset 0 -1px 0 rgba(138,240,168,.5)}
.faq .a{overflow:hidden}
.faq[open] summary{color:var(--mint)}
/* the "+" was absolutely positioned at the right edge of a 1200px row, so it
   sat ~1,000px away from the question it belongs to */
.faq summary{list-style:none;cursor:pointer;padding:20px 16px;
  display:flex;align-items:baseline;gap:16px;
  font-family:var(--disp);font-variation-settings:'wdth' 108;font-weight:700;font-size:.98rem;
  color:var(--paper);transition:color .15s}
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{color:var(--mint)}
.faq summary::after{content:"+";font-family:var(--mono);font-size:1.1rem;color:var(--on-ink-3);
  transition:transform .2s var(--e-out);line-height:1;flex:none}
.faq[open] summary::after{transform:rotate(45deg);color:var(--mint)}
/* DESIGN.md: eleven identical collapsed rows is a filing cabinet. The one
   question everybody actually has gets its own treatment. */
.faq--lead{border-top:1px solid var(--line);background:var(--ink-2)}
.faq--lead summary{font-size:1.12rem;padding-top:24px;padding-bottom:22px}
.faq .a{padding:0 clamp(16px,6vw,80px) 24px 16px;color:var(--on-ink-2);font-size:var(--t-body);
  max-width:72ch;line-height:var(--lh-prose);letter-spacing:var(--track-on-ink)}
.faq .a p+p{margin-top:14px}
.faq .a strong{color:var(--paper);font-weight:700}
.faq .a a{color:var(--mint);border-bottom:1px solid rgba(138,240,168,.35)}
.faq .a code{font-family:var(--mono);font-size:.86em}

/* ---------- close + press seal (V5 stamp reframed) ---------- */
.close{border-top:1px solid var(--line);text-align:center;position:relative;overflow:clip}
.close .sub{font-family:var(--mono);font-size:var(--t-label-lg);letter-spacing:.32em;text-transform:uppercase;
  color:var(--on-ink-2);margin-top:20px}
/* ---------- press seal (picked: B2 record label) ----------
   Lives in the hero now, stamped into the ink space at upper right, where a
   seal belongs. Drops into the flow under the CTAs on narrow screens. */
.seal{position:relative;width:140px;height:140px;display:block;flex:none}
.seal svg.ring{position:absolute;inset:0;animation:spin 22s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.seal .ring text{font-family:var(--mono);font-size:10.6px;letter-spacing:.2em;text-transform:uppercase;
  fill:var(--on-ink-2);transition:fill .2s}
.seal:hover .ring text{fill:var(--paper)}
.seal .core{position:absolute;inset:28px;border-radius:50%;overflow:hidden;
  transition:transform .25s var(--e-out)}
.seal .core img{width:100%;height:100%;object-fit:cover}
/* it is a stamp, so it presses IN rather than lifting */
.seal .core{box-shadow:0 0 0 0 rgba(6,35,31,0);transition:transform .18s var(--e-out),box-shadow .18s var(--e-out)}
.seal:hover .core,.seal:focus-visible .core{transform:scale(.945);
  box-shadow:inset 0 0 0 2px rgba(6,35,31,.5),inset 0 4px 10px rgba(6,35,31,.45)}
.seal:active .core{transform:scale(.915);transition-duration:80ms}
/* the press: the disc drives down and ink squeezes out from under it */
.seal.press .core{transform:scale(.86);
  box-shadow:inset 0 0 0 3px rgba(6,35,31,.62),inset 0 7px 16px rgba(6,35,31,.6);
  transition:transform 140ms cubic-bezier(.3,0,.2,1),box-shadow 140ms ease-out}
.seal .bloom{position:absolute;inset:0;border-radius:50%;pointer-events:none;opacity:0;
  background:radial-gradient(circle,var(--paper) 38%,transparent 39%);
  -webkit-mask-image:radial-gradient(circle,#000 1px,transparent 1.4px);
  mask-image:radial-gradient(circle,#000 1px,transparent 1.4px);
  -webkit-mask-size:5px 5px;mask-size:5px 5px}
.seal.press .bloom{animation:bloom 620ms cubic-bezier(.16,1,.3,1) forwards}
@keyframes bloom{
  0%{opacity:.85;transform:scale(.72)}
  100%{opacity:0;transform:scale(2.15)}
}
/* the seal is the shared element the next page grows out of */
.seal .core{view-transition-name:press-seal}
@media(prefers-reduced-motion:reduce){
  .seal.press .core{transition:none}
  .seal.press .bloom{animation:none}
}
.seal:focus-visible{outline:2px solid var(--mint);outline-offset:6px;border-radius:50%}
/* nonessential loop: stops when the hero is offscreen */
.seal.rest .ring{animation-play-state:paused}
.seal .ring text{font-size:9.4px}
.seal .core{inset:24px}
@media(prefers-reduced-motion:reduce){.seal svg.ring{animation:none}}
/* The old divider was cover-cropped to a fixed height and then masked top and
   bottom to hide the crop. The replacement needs neither: it is drawn to fit,
   and its ground is exactly --ink, so it is shown whole at its own ratio. */
/* max-width:none matters: the global `img{max-width:100%}` was quietly capping
   this at the wrap's 1100px, so the old divider never actually bled full-width
   the way its `width:100vw` claimed to. */
.close .divider{margin-top:clamp(48px,7vw,76px);width:100vw;max-width:none;position:relative;
  left:50%;transform:translateX(-50%);height:auto;opacity:.92}

.foot{border-top:1px solid var(--line)}
/* padding-block only: a `padding` shorthand here zeroed the horizontal
   gutter .wrap otherwise sets, so the footer's own content sat 50px left of
   the nav's above it despite both .wrap boxes being aligned. */
.foot .wrap{padding-block:26px 32px;display:flex;justify-content:space-between;align-items:center;
  gap:20px;flex-wrap:wrap;font-family:var(--mono);font-size:var(--t-label);letter-spacing:.2em;
  text-transform:uppercase;color:var(--on-ink-3)}
.foot img{height:17px;filter:brightness(0) invert(.6)}
/* these were 17px-tall touch targets, the smallest on the page. Vertical
   padding only — a negative side margin pushed the row past the wrap gutter
   and put the page into horizontal overflow at 390px. */
.foot .links{display:flex;flex-wrap:wrap;gap:0 18px}
.foot .links a{padding:14px 0;display:inline-block}
.foot .links a:hover{color:var(--mint)}

/* ================= MOTION: INSCRIPTION =================
   Nothing on this page fades in. Things are cut, printed or typed into place,
   left to right, because the product's claim is that a voice gets pressed into
   a permanent object. One focal moment (the hero cut), a supporting print
   language (the ledger), and the voice file typing itself. Everything else is
   feedback. Content is visible by default — the animations only ever take away
   and give back, so a failed script leaves a readable page. */

/* --- focal: the cutting head lays the waveform down ---
   ONE animation drives both the reveal and the blade, through an inherited
   custom property. Two parallel animations drift out of lock; this cannot. */
@property --cut{syntax:'<percentage>';inherits:true;initial-value:100%}
@keyframes cutrun{to{--cut:100%}}

/* ---------- EXHIBIT A/B SWAP ----------
   Progressive by construction: with no JS both panes sit in normal flow and
   both drafts are fully readable, each under its own counts. `.xs-on` is added
   by script and is the ONLY thing that turns the stage into a swap, so a dead
   script degrades to a longer, honest section rather than a blank box. */
.xswap{max-width:820px}
.xs-ctl{padding:14px clamp(16px,2.4vw,22px) 16px;border-bottom:1px solid var(--line)}
.xs-hint{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.06em;
  color:var(--on-ink-3);margin-bottom:11px;line-height:1.6;max-width:82ch}
.xs-hint-mk{color:var(--mint);font-size:1.05em}
/* the segmented toggle: a filled pill that slides under whichever half is on */
.xs-seg{position:relative;display:grid;grid-template-columns:1fr 1fr;gap:0;
  border:1px solid var(--line-2);border-radius:var(--r-lg);overflow:hidden;
  max-width:520px}
.xs-slide{position:absolute;top:0;bottom:0;left:0;width:50%;background:var(--mint);
  transform:translateX(calc(var(--xn,0) * 100%));
  transition:transform .45s cubic-bezier(.16,1,.3,1);pointer-events:none}
.xs-stop{position:relative;z-index:1;display:flex;align-items:center;justify-content:center;
  gap:9px;background:none;border:0;padding:13px 12px;cursor:pointer;min-height:44px;
  font-family:var(--mono);font-size:var(--t-label);letter-spacing:.16em;
  text-transform:uppercase;color:var(--on-ink-2);transition:color .25s;text-align:center}
.xs-stop + .xs-stop{box-shadow:inset 1px 0 0 var(--line-2)}
.xs-stop .xs-l{font-family:var(--disp);font-variation-settings:'wdth' 116;font-weight:800;
  font-size:1rem;letter-spacing:0;color:var(--on-ink-3);transition:color .25s}
.xs-stop:hover{color:var(--paper)}
.xs-stop:hover .xs-l{color:var(--paper)}
/* the active half sits on the mint fill, so it inverts to ink */
.xs-stop.is-on,.xs-stop.is-on .xs-l{color:var(--ink)}
.xs-stop.is-on:hover,.xs-stop.is-on:hover .xs-l{color:var(--ink)}
@media(max-width:560px){
  .xs-stop{flex-direction:column;gap:3px;letter-spacing:.1em;padding:11px 8px}
}
/* no-JS: a toggle for a mechanism that does not exist yet */
.xswap:not(.xs-on) .xs-ctl{display:none}
.xs-stage{position:relative}
/* both panes readable, stacked, until JS arrives */
.xswap:not(.xs-on) .xs-pane + .xs-pane{border-top:1px solid var(--line)}
.xswap:not(.xs-on) .xs-pane::before{content:attr(data-label);display:block;
  padding:13px clamp(16px,2.4vw,28px) 0;font-family:var(--mono);
  font-size:var(--t-label);letter-spacing:.2em;text-transform:uppercase;color:var(--on-ink)}
/* min-height, not height, and no overflow clip: if --h is ever wrong (a font
   that loads late, a width we never measured) the pane spills instead of being
   silently cut off. A visible overlap is a bug you can see; a clipped last
   paragraph is one nobody reports. */
.xs-on .xs-stage{display:grid;grid-template-rows:var(--h,240px);
  transition:grid-template-rows .52s cubic-bezier(.16,1,.3,1)}
.xs-pane{position:relative}
/* OWNER OVERRIDE, 2026-08-09 — gold as a fill again, deliberately and faintly.
   The same bloom the closing headline carries, here marking B as the side the
   reader is meant to want. Kept under the text, not over it, so contrast on the
   prose is untouched. See the note at .you-gold for why this is flagged. */
.xs-pane[data-pane="1"]::before{content:"";position:absolute;inset:0;z-index:0;
  pointer-events:none;background:radial-gradient(120% 85% at 50% 42%,
    rgba(230,184,115,.115) 0%,rgba(230,184,115,.05) 46%,rgba(230,184,115,0) 78%)}
.xs-pane[data-pane="1"] > *{position:relative;z-index:1}
.xs-on .xs-pane{position:absolute;inset:0 0 auto 0;opacity:0;pointer-events:none;
  visibility:hidden}
.xs-on .xs-pane.is-on{opacity:1;pointer-events:auto;visibility:visible}
/* one frame, off-paint: both panes back in flow at their real heights so the
   stage can be given an exact height per pane. Measuring them with `.xs-on`
   removed instead would also pull in the no-JS label, which is 30px of a
   height the swapped stage never renders. */
.xs-on .xs-stage.xs-measure{display:block;grid-template-rows:none;transition:none}
.xs-on .xs-stage.xs-measure .xs-pane{position:static;opacity:0;visibility:hidden}
/* the incoming draft is printed down left to right, the page's own motion law,
   rather than crossfaded — the outgoing one is simply underneath it */
.xs-on .xs-pane.is-in{z-index:2;animation:printPane 620ms cubic-bezier(.4,0,.15,1) both}
@keyframes printPane{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
.xs-blade{position:absolute;top:0;bottom:0;left:0;width:2px;z-index:3;opacity:0;
  background:var(--mint);box-shadow:0 0 16px 3px rgba(138,240,168,.45);pointer-events:none}
.xs-blade.run{animation:bladeRun 620ms cubic-bezier(.4,0,.15,1) both}
@keyframes bladeRun{0%{opacity:.9;transform:translateX(0)}
  92%{opacity:.9}100%{opacity:0;transform:translateX(var(--bw,100%))}}
/* the counts: the magnitude of the difference, in the same row on both sides */
.xs-meter{display:flex;flex-wrap:wrap;gap:8px clamp(18px,3vw,34px);
  margin:0 clamp(16px,2.4vw,28px);padding:14px 0 16px;border-top:1px solid var(--line);
  font-family:var(--mono);font-size:var(--t-label);letter-spacing:.16em;
  text-transform:uppercase;color:var(--on-ink-3)}
.xs-meter b{font-family:var(--disp);font-variation-settings:'wdth' 114;font-weight:800;
  font-size:1.02rem;letter-spacing:0;color:var(--on-ink-2);margin-right:7px}
.xs-meter--v b{color:var(--mint)}
@media(prefers-reduced-motion:reduce){
  .xs-on .xs-pane.is-in{animation:none}
  .xs-blade{display:none}
  .xs-slide,.xs-on .xs-stage{transition:none}
  .xs-on .xs-stage{display:block;grid-template-rows:none}
  .xs-on .xs-pane{position:relative;visibility:visible;opacity:1;pointer-events:auto}
  .xs-on .xs-pane:not(.is-on){display:none}
}

/* ---------- the signal band (#signal) ----------
   The dithered waveform came off this page when the constellation took the
   hero, and with it went the two motions the owner liked: the left-to-right
   blade reveal and the S3 "push" (the art receding into the ink as you pass
   it). Both are back here, unchanged in kind — the reveal is the same --cut
   property animated by the same `cutrun` keyframes, only triggered when the
   band scrolls into view rather than on load, and the push reads its own
   --wp progress from the same one-listener-per-frame pattern the hero uses.
   Placed between the measurement and the three voices: the argument has just
   been proved, and this is the thing that was measured. */
@property --wp{syntax:'<number>';inherits:true;initial-value:0}
.sigband{position:relative;overflow:clip;--wp:0;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  background:var(--ink-3)}
/* was clamp(150px,19vw,290px) — at any real width that cropped the waveform's
   tall centre peaks off top and bottom. Taller, and the object-position holds
   the trace's own centre line rather than the frame's. */
.sigband img{width:100%;height:clamp(240px,30vw,440px);object-fit:cover;
  object-position:center 46%;display:block}
/* The band is whole by default and only hidden once the script has committed
   to revealing it (`.armed`, set inside signalBand() next to a failsafe that
   forces the finished state). Keying this off `.js` instead would leave the
   band invisible for good if the observer never fired — which is exactly the
   failure this file's motion notes say never to ship. */
.sigband.armed{--cut:0%}
.sigband img{clip-path:inset(0 calc(100% - var(--cut)) 0 0)}
.sigband.run{animation:cutrun 1150ms linear forwards}
/* declared, not merely filled: a band that never animates must still be whole */
.sigband.run-done{animation:none;--cut:100%}
.sigband .art{will-change:transform;transform-origin:50% 100%;
  transform:scale(calc(1 + var(--wp) * .12))}
.sigband::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:2;
  background:
    linear-gradient(180deg,var(--ink-3) 0%,rgba(4,25,22,.3) 26%,transparent 52%),
    linear-gradient(0deg,var(--ink-3) 0%,rgba(4,25,22,.3) 22%,transparent 46%),
    linear-gradient(180deg,transparent,rgba(4,25,22,calc(var(--wp) * .75)))}
/* the blade itself, riding the cut — it prints the wave down and then leaves */
.sigband .blade{position:absolute;top:0;bottom:0;left:var(--cut);width:2px;z-index:3;
  margin-left:-1px;background:var(--mint);box-shadow:0 0 20px 4px rgba(138,240,168,.5);
  opacity:0;pointer-events:none}
.sigband.run .blade{opacity:.9}
.sigband.run-done .blade{opacity:0;transition:opacity .3s}
/* the label sits in the top-left, on the band's own top fade rather than over
   the busiest part of the wave, where it was unreadable */
.sig-cap{position:absolute;left:0;right:0;top:14px;z-index:4;
  font-family:var(--mono);font-size:var(--t-label);letter-spacing:.28em;
  text-transform:uppercase;color:var(--on-ink-3);
  max-width:1200px;margin:0 auto;padding:0 clamp(20px,4vw,50px)}
@media(prefers-reduced-motion:reduce){
  /* signalBand() returns before arming under reduced motion, so --cut is
     already the initial 100%; this only stops the push and the blade */
  .sigband.armed{--cut:100%}
  .sigband .art{transform:none}
  .sigband .blade{display:none}
}

/* --- supporting: ledger rows print in, left to right --- */
.rv{clip-path:inset(0 0 0 0);opacity:1}
.js .rv{clip-path:inset(0 100% 0 0);opacity:.001}
/* the final state is declared, not merely the animation's fill, so a row is
   readable even if the animation never runs */
.rv.in{clip-path:inset(0 0 0 0);opacity:1;animation:printRow 520ms var(--e-out) both}
@keyframes printRow{from{clip-path:inset(0 100% 0 0);opacity:1}
  to{clip-path:inset(0 0 0 0);opacity:1}}

@media(prefers-reduced-motion:reduce){
  .js .rv{clip-path:none;opacity:1}
  .js .hero-bar{opacity:1;transform:none}
  .cut .hero-bar,.rv.in,.sigband.run{animation:none}
  .plotter{display:none}
  /* animations were suppressed here but 86 elements kept their transitions.
     All are hover-triggered at 150-300ms, so nothing autoplayed — this closes
     the gap rather than fixing a violation. */
  *,*::before,*::after{transition-duration:.01ms !important;
    animation-duration:.01ms !important;animation-iteration-count:1 !important;
    scroll-behavior:auto !important}
}

/* one section-head role, was declared inline three times at the same value */
.h-sec{font-size:var(--t-display-m)}

/* ---------- the voiceprint plot: OWNER OVERRIDE, 2026-08-07 ----------
   This system's stated law is "nothing spins, everything is cut or printed left
   to right, one clock (--cut) drives it." The constellation was first built to
   obey that: opacity-only, keyed to --cut, no independent timer. Owner saw it
   next to the standalone mockup and asked for the mockup's actual animation —
   the plate settling out of a spin, marks popping with overshoot, lines actually
   drawing. Explicit instruction: override the law for this element. It runs its
   own clock (`.play`), independent of --cut and the blade.

   SCOPE WIDENED 2026-08-09, and deliberately: the gate moved from `.band-plot`
   to `.plot`, because a second plot now exists — the small one beside the
   Exhibit A/B toggle, which arrives when B is chosen. Same override, two
   elements. It is also the source of the detector's one expected finding
   (`bounce-easing` on `pop-in`). Leave it.

   Geometry is NEVER scaled between the two — both plot the same 300x300
   coordinate space. Only dot radii and stroke widths take Fusion.voiceprint()'s
   `scale`, because at 132px an unscaled 0.7px spoke is not there at all. */
.rig{transform-box:view-box;transform-origin:150px 150px}
.draw-line{stroke-dashoffset:0}
.pop-in{opacity:var(--o,1);transform:scale(1);transform-box:fill-box;transform-origin:center}
@keyframes rig-spin{0%{transform:rotate(-190deg) scale(.8);opacity:0}38%{opacity:1}100%{transform:rotate(0deg) scale(1);opacity:1}}
@keyframes draw-line{from{stroke-dashoffset:var(--len)}to{stroke-dashoffset:0}}
@keyframes pop-in{0%{opacity:0;transform:scale(0)}62%{transform:scale(1.22)}100%{opacity:var(--o,1);transform:scale(1)}}
@media(prefers-reduced-motion:no-preference){
  .plot.play .rig{animation:rig-spin 1.3s cubic-bezier(.16,1,.3,1) both}
  .plot.play .draw-line{stroke-dasharray:var(--len);animation:draw-line .5s var(--delay,0s) cubic-bezier(.4,0,.2,1) both}
  .plot.play .pop-in{animation:pop-in .55s var(--delay,0s) cubic-bezier(.34,1.56,.64,1) both}
}

/* ============================================================================
   PROMOTED FROM index.html, 2026-08-09, when free.html turned out to need the
   same components. The rule this file is written around: the SECOND page that
   needs a block is the moment it moves here, not the moment it gets copied.
   ============================================================================ */

/* ---------- hero poster ---------- */
.poster{position:relative;overflow:clip;border-bottom:1px solid var(--line)}
.poster .wrap{position:relative;z-index:2;padding-top:clamp(46px,8vh,92px);text-align:center;
  display:flex;flex-direction:column;align-items:center}
.poster h1{font-size:clamp(2.3rem,6.4vw,4.9rem);letter-spacing:-.01em;margin-top:22px;
  max-width:min(980px,100%);text-wrap:balance;font-variation-settings:'wdth' 118;font-weight:600}
.poster .subrow{display:flex;justify-content:center;gap:2.4em;flex-wrap:wrap;font-family:var(--mono);
  font-size:var(--t-label-lg);letter-spacing:.32em;text-transform:uppercase;color:var(--on-ink-2);margin-top:26px}
/* Every word was its own flex item, so on a phone the separator wrapped to the
   end of line one and orphaned. Grouped, and the separator drops when it wraps. */
.poster .subrow .grp{display:flex;gap:2.4em;flex-wrap:wrap;justify-content:center}
@media(max-width:560px){.poster .subrow{gap:1.2em}.poster .subrow .grp{gap:1.2em}
  .poster .subrow .sep{display:none}}
.poster .lede{margin:28px auto 0;max-width:530px}
.cta-row{display:flex;gap:16px;justify-content:center;margin-top:36px;flex-wrap:wrap}

/* ---------- hero grid: copy left, constellation right ----------
   The old order was kick/h1/lede/hero-bar THEN a full-width art band below —
   on a real viewport the art landed well past the fold. The constellation is
   real, not decorative, so it earns first-screen placement beside the H1
   rather than a scroll away. hero-bar (acts/stats/seal) stays full-width and
   BELOW the grid — it's already a tuned three-up row and doesn't fit a
   narrowed column without its own redesign. */
/* 2026-08-08: constellation up ~30%. It could not simply be scaled — .band-plot
   was height-driven with aspect-ratio 1/1, so any height past the column's own
   width overflowed into a clip. The plot is now WIDTH-driven and the column
   itself carries the growth: the ratio flips toward the art, the gap tightens,
   and .hero-art borrows the wrap's right gutter. Measured 454px → 584px, then
   pulled back 15% to ~496px on the owner's second pass — the copy column takes
   that width back and the H1 goes up a step with it. */
.poster-grid{width:100%;display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,.74fr);
  gap:clamp(24px,3.4vw,52px);align-items:center;text-align:left}
.poster-copy{display:flex;flex-direction:column;align-items:flex-start;min-width:0}
/* the copy column gives up ~60px to the art, so the display size comes down a
   step with it — at the old 4.9rem ceiling the H1 broke to six lines and the
   hero stopped fitting a screen. Measured, not guessed: four lines at 1440. */
.poster-grid h1{margin-top:18px;max-width:12.5ch;text-align:left;
  font-size:clamp(2.2rem,4.8vw,4.15rem)}
.poster-grid .subrow{justify-content:flex-start;margin-left:0;gap:1.5em;
  letter-spacing:.22em;margin-top:22px}
.poster-grid .subrow .grp{justify-content:flex-start;gap:1.5em}
.poster-grid .lede{margin:24px 0 0;text-align:left;max-width:46ch}
@media(max-width:900px){
  .poster-grid{grid-template-columns:1fr;text-align:center}
  .poster-copy{align-items:center}
  .poster-grid h1{max-width:min(980px,100%);text-align:center}
  .poster-grid .subrow{justify-content:center}
  .poster-grid .subrow .grp{justify-content:center}
  .poster-grid .lede{margin:24px auto 0;text-align:center}
}

/* ---------- hero bar: actions, readout and seal on one rule ----------
   The seal deliberately overhangs the bar top and bottom, the way a stamp
   sits proud of the form it was pressed onto. */
.hero-bar{margin-top:clamp(34px,5vh,54px);width:100%;border-top:1px solid var(--line);
  border-bottom:1px solid var(--line);display:grid;
  grid-template-columns:auto 1fr auto;align-items:center;gap:clamp(20px,4vw,52px);
  padding:0 clamp(4px,1vw,14px);min-height:104px}
.hero-bar .acts{display:flex;gap:12px;flex-wrap:wrap;padding:22px 0}
.hero-bar .acts .btn{margin-bottom:0}
.hero-bar .acts .act-terms{flex-basis:100%}
.hero-bar .stats{display:flex;gap:clamp(24px,4vw,52px);align-items:flex-end;
  font-family:var(--mono);justify-content:center}
.hero-bar .stats .v{font-family:var(--disp);font-variation-settings:'wdth' 116;font-weight:800;
  font-size:1.9rem;line-height:1;color:var(--paper)}
.hero-bar .stats .s2 .v{font-size:1.15rem;padding-bottom:4px}
.hero-bar .stats .l{font-size:var(--t-label);letter-spacing:.2em;text-transform:uppercase;
  color:var(--on-ink-3);margin-top:6px;white-space:nowrap}
.hero-bar .seal{margin:-18px 0}
@media(max-width:1080px){
  .hero-bar{grid-template-columns:1fr;justify-items:center;gap:26px;padding:30px 0;min-height:0}
  .hero-bar .acts{justify-content:center}
  .hero-bar .seal{margin:0}
}
/* Now a grid column beside the copy, not a full-width band below it — the
   margin-top and top-fade below existed to blend a full-bleed band into the
   text sitting above it, which no longer applies side-by-side. */
.hero-art{position:relative;pointer-events:none;
  /* reclaim the wrap's right gutter so the plot can run to the page edge */
  margin-right:calc(-1 * clamp(20px,4vw,50px))}
@media(max-width:900px){.hero-art{margin-top:clamp(28px,5vh,60px);margin-right:0}}
/* the constellation is real plotted data (real x/y, not art), so it keeps its
   native 1:1 coordinate space rather than being stretched to a wide band —
   a centred plot with ink either side, not a cropped fill */
.band-plot{position:relative;width:100%;max-width:620px;aspect-ratio:1/1;
  margin:0 auto;overflow:visible}
.band-plot svg{width:100%;height:100%;display:block;overflow:visible}
/* the static stand-in, for every path where the live plot never gets built.
   It is a real export of the same voiceprint, so the caption under it stays
   true. Switched on by the inline script (script ran, Fusion missing) and by
   the <noscript> block in the head (scripting off). */
.band-plot.static{background:url("assets/voiceprint-maren.svg") center/contain no-repeat}
.band-plot.static svg{display:none}


/* ---------- artifact / dossier (ink) ---------- */
.dossier{display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center}
.dossier h2{font-size:clamp(1.7rem,3.4vw,2.5rem)}
.dossier .lede{margin-top:18px}
.vfile{border:1.5px solid var(--line-2);background:var(--ink-3);border-radius:var(--r-lg);overflow:clip}
.vfile .top{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.2em;text-transform:uppercase;
  color:var(--on-ink-3);padding:11px 18px;border-bottom:1px solid var(--line);
  display:flex;justify-content:space-between;gap:12px}
.vfile .top .you{color:var(--gold)}
.vfile .rec{color:var(--mint);opacity:0;transition:opacity .3s}
.vfile.typing .rec{opacity:1;animation:recblink 1.1s steps(1,end) infinite}
@keyframes recblink{50%{opacity:.25}}
.vfile pre::after{content:"";display:none;width:.62em;height:1.05em;background:var(--mint);
  vertical-align:-.18em;margin-left:1px}
.vfile.typing pre::after{display:inline-block;animation:caret .9s steps(1,end) infinite}
@keyframes caret{50%{opacity:0}}
.vfile pre{font-family:var(--mono);font-size:.74rem;line-height:1.85;padding:20px 18px 24px;
  color:var(--on-ink-2);overflow-x:auto}
.vfile .h{color:var(--mint)}
.vfile .g{color:var(--gold)}
.vfile .k{color:var(--on-ink-3)}
.dossier .plate{margin-top:30px;max-width:340px;border:1px solid var(--line);border-radius:var(--r-lg)}
/* height:auto is load-bearing: the intrinsic height attribute (added to stop
   layout shift) otherwise pins the box and squashes this square render */
.dossier .plate img{width:100%;height:auto;
  -webkit-mask-image:radial-gradient(circle at 50% 48%,#000 62%,transparent 96%);
  mask-image:radial-gradient(circle at 50% 48%,#000 62%,transparent 96%)}
.dossier .plate .cap{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.2em;
  text-transform:uppercase;color:var(--on-ink-3);padding:10px 14px;border-top:1px solid var(--line)}
@media(max-width:900px){.dossier{grid-template-columns:1fr;gap:38px}}


/* ---------- hero scroll behaviour ----------
   Reads a single --sp (0 -> 1, hero scroll progress) written once per frame by
   Fusion.scrollProgress(). Default state is the static hero; if --sp never
   updates, nothing moves and nothing breaks.
   The mockup carried three switchable options behind a `data-scroll` fork panel
   (S1 drift / S2 playhead / S3 push). The fork went with the rest of the mockup
   chrome; PUSH is the one that shipped — the hero recedes into the ink rather
   than sliding.
   The fork also carried a `.hero-art::before` gradient. It is NOT reproduced
   here: that pseudo-element never had a `content`, so it rendered nothing in
   the mockup either. It dated from the full-bleed band the constellation
   replaced, and copying dead rules forward is how a stylesheet stops being
   readable. */
@property --sp{syntax:'<number>';inherits:true;initial-value:0}
.poster{--sp:0}
.hero-art{will-change:transform;transform-origin:50% 100%;
  transform:scale(calc(1 + var(--sp) * .12))}
@media(prefers-reduced-motion:reduce){
  .hero-art,.poster > .wrap{transform:none;opacity:1}
}

/* caption under the plotted constellation. It named the person whose answer
   set is plotted; the owner's call is that the reader only needs to know it is
   one real, working voiceprint. The old line also ended "plotted, not drawn",
   which put a bad word on drawing to make a point about data — the claim is
   made positively now, and the drawing keeps its own caption in the dossier. */
.band-cap{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.16em;text-transform:uppercase;
  color:var(--on-ink-3);text-align:center;margin:14px auto 0;padding:0 20px;max-width:62ch}


/* ---------- STEP 05 · floodlight ---------- */
.flood{background:var(--ink-3);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.flood .h-disp{font-size:var(--t-display-m);max-width:19ch}
.flood .lede{margin-top:20px;max-width:66ch}
.exhibit{margin-top:44px;border:1px solid var(--line);background:var(--ink);max-width:760px}
.ex-head{display:flex;align-items:baseline;gap:16px;flex-wrap:wrap;padding:13px 20px;
  border-bottom:1px solid var(--line);font-family:var(--mono);font-size:var(--t-label);
  letter-spacing:.2em;text-transform:uppercase}
.ex-n{color:var(--on-ink)}
.ex-what{color:var(--on-ink-3)}
.ex-body{padding:26px 28px 8px}
/* 28px inside a 20px gutter took the exhibit to 35 characters a line on a phone */
@media(max-width:560px){.ex-body{padding:22px 16px 6px}
  .ex-head,.ex-src{padding-left:16px;padding-right:16px}}
/* in-fiction exhibit: this is product output, so DESIGN.md's voice rules do not
   apply to the words inside it, and its em-dashes are the machine's own. */
.ex-body p{color:var(--on-ink-2);font-size:var(--t-body);max-width:64ch;margin-bottom:16px;
  line-height:var(--lh-prose);letter-spacing:var(--track-on-ink)}
/* FLAGGED, NOT STRUCK — reworked 2026-08-09.
   These were line-through and dimmed to --on-ink-3, which had two costs: the
   rule cut through the letterforms, and dimming told the reader to skip them.
   So people tried to read the paragraph WITHOUT the flagged phrases, which is
   not a sentence. The point of the exhibit is that the draft reads fine and is
   still nobody's — and that only lands if it stays readable.
   The phrases are now marked the way a proof-reader marks a page: a wash behind
   them and a rule under them, with the text itself brought UP to full brightness
   rather than pushed down.
   The colour was mint for one day and that was wrong — see the --flag note in
   :root. Green reads as approval, and these are the villain of the section. */
.tl{color:var(--paper);padding:.06em .16em;margin:0 -.06em;border-radius:2px;
  background-color:var(--flag-wash);
  text-decoration:underline solid var(--flag);
  text-decoration-thickness:2px;text-underline-offset:.22em;
  transition:background-color .2s}
/* tells 3 and 4 are adjacent in the source text with no character between them,
   so eight marks would render as one continuous run */
.tl + .tl{margin-left:3px}
/* hovering the exhibit lifts every flag at once, so the eight can be counted */
.exhibit:hover .tl{background-color:var(--flag-wash-hi)}
/* ---------- the key ----------
   Sits between the draft and its counts, on its own ruled ground so it reads as
   apparatus rather than more prose. Two columns at desktop, one on a phone. */
.ex-key{margin:0 clamp(16px,2.4vw,28px);padding:16px 0 4px;border-top:1px solid var(--line)}
.ex-key-lead{color:var(--on-ink-2);font-size:var(--t-body-s);line-height:1.65;
  letter-spacing:var(--track-on-ink);max-width:68ch}
.ex-key-lead b{color:var(--paper);font-weight:700}
.ex-key-sw{display:inline-block;width:.95em;height:.62em;margin-right:.55em;
  vertical-align:baseline;border-radius:2px;background:var(--flag-wash);
  box-shadow:inset 0 -2px 0 var(--flag)}
.ex-key-list{list-style:none;margin:16px 0 0;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 clamp(20px,3vw,40px)}
.ex-key-list li{display:flex;gap:10px;align-items:baseline;padding:7px 0;
  border-bottom:1px solid var(--line);font-family:var(--mono);font-size:var(--t-label);
  line-height:1.6;letter-spacing:.03em}
.ex-key-list .q{color:var(--on-ink);flex:1 1 auto;min-width:0}
.ex-key-list .w{color:var(--on-ink-3);flex:0 1 auto;text-align:right;
  text-transform:uppercase;letter-spacing:.12em;font-size:.94em}
@media(max-width:820px){
  .ex-key-list{grid-template-columns:1fr}
  .ex-key-list li{flex-wrap:wrap;gap:2px 10px}
  .ex-key-list .w{text-align:left;flex-basis:100%}
}
.ex-src{padding:0 28px 22px;font-family:var(--mono);font-size:var(--t-label);line-height:1.7;
  letter-spacing:.04em;color:var(--on-ink-3);max-width:70ch}
.ex-src .nw{white-space:nowrap}

/* ---------- the B-side voiceprint plot (added 2026-08-09, owner request) ----
   The point it makes: on the B side there is an actual voiceprint in play, and
   here it is. So it is not an emblem — Fusion.voiceprint() plots the real `dev`
   answer set from js/voiceprints.js, which is the set that produced
   _marketing/voices/dev-voice.md, which is the file the B draft was generated
   from. Anything less would be decoration wearing the evidence's label, which
   is this page's most dangerous defect class.

   It must not make this row taller for its own sake. A two-column grid does not
   achieve that — measured, the plot at 112px pushed a 124px row to 143px,
   because the row's content box is only 94px and the grid sizes to its tallest
   item. So the plot is taken OUT of flow: the control reserves a gutter for it
   with padding, and the plot is absolutely positioned in that gutter. It cannot
   contribute to the row's height at any width, by construction rather than by a
   number that happens to fit today.
   The size is capped below the row's own minimum height (~105px, one-line hint)
   so it can never spill downward into the draft either. Both measured at 1400px
   and at a true 390px, not eyeballed. */
.xs-ctl{position:relative;--vp-size:clamp(62px,8vw,96px);--vp-gut:clamp(14px,2vw,22px);
  padding-right:calc(var(--vp-size) + var(--vp-gut) + clamp(16px,2.4vw,22px))}
.xs-ctl-main{min-width:0}
.xs-vp{position:absolute;top:50%;right:var(--vp-gut);transform:translateY(-50%);
  width:var(--vp-size);height:var(--vp-size);
  /* hidden until B is chosen, and NOT by opacity alone: the plot's own arrival
     (rig-spin/draw-line/pop-in) starts from zero, so revealing the box at the
     moment .play lands makes the two read as one movement. */
  opacity:0;visibility:hidden}
.xs-vp.is-lit{opacity:1;visibility:visible}
.xs-vp svg{width:100%;height:100%;display:block;overflow:visible}
@media(prefers-reduced-motion:reduce){
  /* no arrival to ride, so it simply is there */
  .xs-vp.is-lit{opacity:1}
}
/* Below 560px it comes off. Measured: the 62px gutter squeezed the hint into
   one more line and put 17.7px back on the row — the one thing this element was
   told not to do. Owner's call, 2026-08-09. The gutter goes with it, so the hint
   gets its full measure back. */
@media(max-width:560px){
  .xs-ctl{padding-right:clamp(16px,2.4vw,22px)}
  .xs-vp{display:none}
}

.flood-close{margin-top:34px;max-width:62ch;color:var(--on-ink);font-size:var(--t-lede);
  line-height:1.6;letter-spacing:var(--track-on-ink)}


/* ---------- STEP 15 · the guarantee (light band) ---------- */
.guar{margin-top:40px;display:flex;gap:22px;align-items:flex-start;padding:24px 28px;
  border:1px solid var(--rule-2);background:var(--cream);max-width:74ch}
.guar-mk{font-family:var(--serif);font-size:2.6rem;font-weight:600;line-height:.9;
  color:var(--gold-deep);flex:none}
.guar h3{font-family:var(--serif);font-size:var(--t-serif-s);font-weight:600;color:var(--t1)}
.guar p{color:var(--t2);font-size:var(--t-body);line-height:1.7;margin-top:7px}
.guar a{color:var(--t1);border-bottom:1px solid var(--rule-2)}
/* the 2.6rem numeral plus 28px of padding left this at 26 characters a line on
   a phone — the narrowest column on the page, carrying the risk reversal */
@media(max-width:560px){.guar{flex-direction:column;gap:10px;padding:22px 18px}
  .guar-mk{font-size:2rem}}

/* ---------- STEP 17 · the P.S., set as a press note ---------- */
.ps{max-width:66ch;margin:54px auto 0;text-align:left;padding-top:26px;
  border-top:1px solid var(--line)}
.ps-k{font-family:var(--mono);font-size:var(--t-label);letter-spacing:.28em;text-transform:uppercase;
  color:var(--on-ink-3);display:block;margin-bottom:14px}
.ps p{color:var(--on-ink-2);font-size:var(--t-body);line-height:var(--lh-prose);
  letter-spacing:var(--track-on-ink);margin-bottom:14px}
/* the five paid additions, set as a numbered ledger rather than buried in the
   middle of a paragraph — this is the only place on the page that states the
   whole free-vs-paid difference in one run */
.ps p.ps-lead{color:var(--on-ink);font-weight:600}
.ps-list{list-style:none;counter-reset:psn;margin:16px 0 20px;border-top:1px solid var(--line)}
.ps-list li{counter-increment:psn;position:relative;padding:13px 0 13px 40px;
  border-bottom:1px solid var(--line);color:var(--on-ink-2);font-size:var(--t-body-s);
  line-height:1.62;letter-spacing:var(--track-on-ink)}
.ps-list li::before{content:counter(psn,decimal-leading-zero);position:absolute;left:0;top:15px;
  font-family:var(--mono);font-size:var(--t-label);letter-spacing:.14em;color:var(--on-ink-3)}
.ps-list b{color:var(--paper);font-weight:700}
.ps p.ps-note{font-size:var(--t-body-s);color:var(--on-ink-3)}
.ps-act{font-family:var(--mono);font-size:var(--t-label-lg);letter-spacing:.1em;color:var(--on-ink-3)}
.ps-act a{display:inline-block;color:var(--mint);border-bottom:1px solid var(--mint);padding:13px 0 11px}

/* the closing beat's headline. Systemic: every page that ends on a CTA ends on
   this size. The gold bloom on one word of it is index.html's alone (an owner
   override of the gold-as-fill rule) and stays inline there. */
.close h2{font-size:clamp(2rem,5.2vw,3.7rem)}

/* ============================================================================
   THE TEXT PAGES — terms · privacy · contact · unsubscribe · 404.
   Added 2026-08-09. Five pages, one shell: a cream reading band, a measure that
   holds, and nothing else. FUSION-SYSTEM §5 is explicit that these get "the
   band, the type scale and the nav, and nothing else" — so this block is
   deliberately small, and anything a legal page seems to need beyond it is
   almost certainly the page arguing when it should be informing.
   ============================================================================ */

/* A cream page rather than an ink one. These are documents: someone reads a
   refund clause the way they read a form, and 900 words of small print on ink
   is a worse experience than an on-brand one. The band's own paper gradient and
   halftone come from .band-light, which this reuses rather than re-declares. */
.legal{max-width:74ch;padding:clamp(46px,7vw,84px) 0 clamp(56px,8vw,96px)}
.legal h1{font-family:var(--disp);font-variation-settings:'wdth' 118;font-weight:800;
  text-transform:uppercase;line-height:1.06;letter-spacing:-.01em;color:var(--t1);
  font-size:clamp(2rem,4.6vw,3.1rem)}
.legal .updated{display:block;margin-top:14px;font-family:var(--mono);
  font-size:var(--t-label);letter-spacing:.24em;text-transform:uppercase;color:var(--t3)}
.legal .lede{margin-top:26px;max-width:62ch;color:var(--t2);font-size:var(--t-lede);
  line-height:1.62;letter-spacing:normal}
/* Fraunces for the section heads, the same voice the light bands use elsewhere,
   with a ruled top so a long document has visible joints rather than one slab */
.legal h2{font-family:var(--serif);font-weight:600;font-size:1.28rem;letter-spacing:-.008em;
  color:var(--t1);margin-top:42px;padding-top:22px;border-top:1px solid var(--rule)}
.legal h2 + p,.legal h2 + ul{margin-top:14px}
.legal p{color:var(--t2);font-size:var(--t-body);line-height:1.72;max-width:68ch}
.legal p + p{margin-top:15px}
.legal ul{list-style:none;margin-top:14px;display:grid;gap:11px}
.legal li{position:relative;padding-left:20px;color:var(--t2);font-size:var(--t-body);
  line-height:1.7;max-width:68ch}
.legal li::before{content:"\25B6";position:absolute;left:0;top:.34em;font-size:.5rem;
  color:var(--mint-deep)}
.legal strong{color:var(--t1);font-weight:600}
.legal a{color:var(--t1);border-bottom:1px solid var(--rule-2);
  transition:border-color .15s,color .15s}
.legal a:hover{color:var(--mint-deep);border-color:var(--mint-deep)}
/* a.btn only: this cancels the .legal a underline-border doubling up with
   .btn's own border (404.html's "Build your voice" link). Was `.legal .btn`,
   tag-agnostic, so it also stripped the bottom border off contact.html's
   real <button class="btn"> submit — the one element `.legal a` (an anchor
   selector) could never have conflicted with in the first place. */
.legal a.btn{border-bottom:0}

/* ---------- the contact form ----------
   Hard-edged and ruled, like the ledgers everywhere else on the site. The
   honeypot stays off-screen rather than display:none — a hidden input some bots
   will still fill, which is the entire point of it. */
.cform{margin-top:34px;display:grid;gap:20px;max-width:56ch}
.cform label{display:block;font-family:var(--mono);font-size:var(--t-label);
  letter-spacing:.2em;text-transform:uppercase;color:var(--t2);margin-bottom:9px}
.cform label .opt{letter-spacing:.1em;color:var(--t3)}
.cform input,.cform textarea{width:100%;font:inherit;font-size:var(--t-body);
  color:var(--t1);background:#fdfbf5;border:1.5px solid var(--rule-2);
  border-radius:var(--r);padding:13px 15px;transition:border-color .15s,box-shadow .15s}
.cform textarea{min-height:170px;resize:vertical;line-height:1.65}
/* :focus-visible, not :focus — same fix already made for .onboard-input/.ta:
   a plain mouse click shouldn't light the field like an error state. Restoring
   the outline (was suppressed) puts the system's real 2px ring back on the
   one form on the site; the border/shadow layer on top, doesn't replace it. */
.cform input:focus-visible,.cform textarea:focus-visible{border-color:var(--mint-deep);
  box-shadow:2px 2px 0 rgba(18,96,63,.18)}
.cform [aria-invalid="true"]{border-color:var(--flag);
  box-shadow:2px 2px 0 rgba(233,112,90,.22)}
.cform .hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
/* the 5000-char maxlength truncated silently with nothing on screen to say
   so; someone pasting a long report lost the tail with no signal */
.char-count{margin-top:7px;font-size:var(--t-body-s);color:var(--t3);text-align:right}
.char-count.low{color:var(--flag)}
.cform-actions{display:flex;flex-wrap:wrap;gap:14px 22px;align-items:center}
.cform-actions .note{font-size:var(--t-body-s);color:var(--t3)}
.form-msg{margin-top:4px;font-size:var(--t-body-s);line-height:1.6;display:none}
.form-msg.show{display:block}
.form-msg.ok{color:var(--mint-deep)}
/* the machine's tells get --flag; so does a form error. Both are "this is wrong",
   marked in the colour an editor's pen has used for a century. */
.form-msg.err{color:#b6412c}
