/* ══════════════════════════════════════════════════════════════════════════
   SKELETON — structure · type · motion. ZERO HEX CODES, on purpose.
   Every colour is a var() defined in world-a|b|c.css. If you find yourself
   typing a # in this file, the three-variant claim has just become a lie.
   ══════════════════════════════════════════════════════════════════════════ */

/* Both variable fonts — one file covers the whole weight range, so two
   @font-face rules replace what used to take three.

   font-display:BLOCK, restored 2026-09-14. This file shipped `block` for all
   three of its old faces; the two-face rewrite earlier the same day changed it
   to `swap` on the theory that blocking on a 132KB serif was a stall worth
   avoiding. That theory was wrong in the way that matters: `swap` paints the
   page in Georgia and Arial first and then re-paints it in Newsreader and
   Inter, so the founder — who reloads this page many times an hour — saw the
   type change between opens. His words: "if I open it multiple times, the
   fonts could switch up."
   `block` hides the text for up to 3s instead of showing it in the wrong face,
   so there is never a switch to see. The cost of blocking is paid down by the
   two <link rel="preload"> tags now in every page's <head>: the browser starts
   both font downloads at parse time rather than waiting to discover them
   through this stylesheet, and the fonts directory is cached immutable for a
   year in _headers, so only the very first visit ever waits at all.
   These two facts live in two files. If display ever moves off `block`, the
   preloads stop being load-bearing — and if the preloads go, this becomes a
   real stall. Move them together. */
@font-face{font-family:Inter;src:url(../fonts/inter-var.woff2) format('woff2');
  font-weight:100 900;font-display:block}
@font-face{font-family:Newsreader;src:url(../fonts/newsreader-var.woff2) format('woff2');
  font-weight:200 800;font-display:block}

:root{
  --pad:clamp(20px,4.4vw,56px);
  --measure:34rem;
  --rig:130svh;               /* pin travel — 1.3 screens drive one pinned screen.
                                 250 -> 170 -> 130: shortened twice on 2026-09-14. */

  /* SPACING SCALE — explicit so it cannot drift. Heuristic 4 (spacing) says
     one scale, and a value that isn't on the scale is a bug, not a taste
     choice. If you are about to type a raw px gap, it belongs here first. */
  --s1:4px; --s2:8px; --s3:12px; --s4:20px; --s5:32px; --s6:52px; --s7:84px;

  /* Motion budget. Disclosure is capped at 180ms by contract — reveal that
     performs is reveal that annoys on the fortieth click. */
  --t-disclose:160ms;
  --ease:cubic-bezier(.22,.61,.36,1);
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
  background:var(--ground);color:var(--t1);
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-weight:400;-webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

/* ── TYPE ─────────────────────────────────────────────────────────────── */
/* CAPTION "D" — the design partner's pick off the five-up at /review/caption/,
   applied 2026-08-14. Was 10.5px / 300 / var(--t3), which measured Lc 39 on a
   quiet ground and Lc 0 with the ambient field behind it.

   The colour is NOT a new token and NOT a hex. #b9b2a5 is what option D asked
   for; var(--t3) lifted 37.5% of the way to var(--t1) lands on (185,178,165)
   in world A — exactly it — and derives the same relationship in worlds B and
   C for free. Skeleton.css still owns zero colours, which is the whole reason
   "same page, three designs" is structurally true rather than a promise.

   Honest number, stated because the founder's audit surface is honesty: in
   worlds A/B/C, at 13px/500, this measured Lc 56-57 — and APCA's smallest
   LISTED combination is 14px/400, so 13px/500 was below the table and
   "clears the bar" could not be claimed, only "56 against 39". Option E
   (16px/500/#d2ccc1) was the one that came close to a real bar, and it was
   not chosen.

   RE-MEASURED 2026-09-14 against world-sf.css, because that palette changed
   both --t1 and --t3 and the old number no longer describes this class. Same
   37.5% mix, same APCA-W3 0.1.9 method:

       light   #535C66   Lc 78.0 on --ground   81.6 on --screen
       dark    #B7BBBF   Lc 64.9 on --ground   63.9 on --screen

   Every one of those is well clear of the 56-57 this class used to run at, so
   the old caveat no longer applies in either mode. Two notes on why the weight
   moved 500 -> 600: Inter is loaded at 400/600/700 only, so 500 would have
   been synthesised; and heavier type NEEDS LESS contrast at a given size, so
   the change moves the requirement in the safe direction. It is still 13px,
   which is still below APCA's smallest listed row, so the claim here remains
   "78 against 39", not "passes". */
/* overflow-wrap:break-word on this and the three prose classes below
   (.claim, .body, .lede): a preventive add, not a fix for anything actually
   overflowing today — every slot on the page is still its bracketed
   [s1-headline — the hook] placeholder, and nothing in that placeholder
   text is long enough to break 360px. But it will be replaced with the
   founder's own words, and a long unbroken word or a URL typed into a body
   paragraph is the one kind of overflow max-width and clamp() cannot stop
   on their own — the browser will not wrap a word that has nowhere to
   break unless this is set. Costs nothing when every word already wraps
   normally, which is every case today. */
.mono{font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-weight:600;font-size:13px;letter-spacing:.02em;line-height:1.45;
  font-variant-numeric:tabular-nums;overflow-wrap:break-word;
  color:color-mix(in srgb,var(--t1) 37.5%,var(--t3))}
/* The app's signature micro-label — copied to match it exactly, not adapted. */
.label{font-family:inherit;font-size:10.5px;font-weight:600;letter-spacing:.03em;
  text-transform:uppercase;color:var(--t3)}
.claim{
  font-family:Newsreader,Georgia,'Times New Roman',serif;font-weight:400;
  font-size:clamp(2.4rem,5vw,4.25rem);line-height:.97;letter-spacing:-.021em;
  text-wrap:balance;max-width:20ch;overflow-wrap:break-word;
}
/* The emphasised word in every headline — "bullshit.", "connected", "can't".
   Was var(--t2), a grey, which meant the page carried brand colour on the
   wordmark and nowhere else: five headlines, all grey on grey. The founder
   called the result "a little bland" on 2026-09-14 and could not say why.
   This is why. Putting the accent here lands colour exactly where the eye
   already stops, five times down the scroll, and still spends it on well
   under the 5% of pixels the palette's own restraint rule allows.
   One line to revert: swap var(--accent) back to var(--t2). */
.claim em{font-style:normal;color:var(--accent)}
.claim--wide{max-width:26ch}
.body{margin-top:14px;max-width:var(--measure);font-size:16px;line-height:1.5;
  color:var(--t2);overflow-wrap:break-word}
.body strong{color:var(--t1);font-weight:600}
/* There was NO link rule on this page at all, so every in-copy link fell back
   to the browser's default blue — which on the navy slab is barely visible and
   is where the founder lost "The longer version of this list" on 2026-09-14.
   color:inherit makes a link take whatever its surface already reads as, in
   every world and both modes; the underline, not a colour, carries that it is
   a link. */
.body a{color:inherit;text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1px;text-decoration-color:var(--t3)}
.body a:hover,.body a:focus-visible{text-decoration-color:currentColor}
.body + .body{margin-top:11px}
.lede{font-size:18px;line-height:1.45;color:var(--t2);max-width:30rem;overflow-wrap:break-word}

/* ── COPY SLOTS AND THEIR BRIEFS ──────────────────────────────────────────
   Every slot the founder still has to write. Visible in dev, invisible in
   prod by flipping ONE class on <body> — so a half-written page can never
   ship silently as finished copy.

   The brief next to each slot carries the PSYCHOLOGICAL PRINCIPLE the copy
   has to satisfy, not a suggested sentence. Founder's instruction: "don't
   write the hook, write the psychological principle behind it." A drafted
   hook gets edited into a worse version of itself; a stated principle gets
   written against. */
.ph::before{
  content:"DRAFT SLOT · ";
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-size:9px;letter-spacing:.08em;color:var(--accent);opacity:.85;
}
body.final .ph::before{content:none}

/* THE BRIEF PANEL.
   ^^ THIS OPENER WAS MISSING until 2026-09-14, and it silently killed the whole
   rule below. With no comment-opener here the parser read this prose as the
   start of a selector, kept consuming to the next "{" — .brief's — and dropped
   the lot as invalid. So .brief had no border, no tint, no 10px size and no
   colour for as long as the file has existed; it inherited body type at 16px.
   Found while chasing why the founder could not read the brief panels. That
   had TWO causes, this one and the .paper token scoping further down.

   COLLAPSED BY DEFAULT, and that is not a nicety. Expanded, a brief is taller
   than the copy slot it describes, and on the two screens where text overlaps
   art the text column then rides up over the map — the headline lands on node
   text and the lower gutter stamps disappear behind it. The founder has to be
   able to judge the LAYOUT he is going to ship while the guidance is still on
   the page, so the brief collapses to one line and opens on click.
   <details> rather than JS: keyboard and screen-reader behaviour for free. */
.brief{
  display:block;margin:var(--s3) 0;max-width:var(--measure);
  border-left:2px solid var(--accent);
  background:color-mix(in srgb,var(--accent) 7%,transparent);
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-size:10px;line-height:1.62;
  color:var(--t2);letter-spacing:.01em;
}
.brief > summary{
  cursor:pointer;list-style:none;padding:7px var(--s3);
  color:var(--t1);font-weight:600;font-size:9px;letter-spacing:.1em;
  text-transform:uppercase;
}
.brief > summary::-webkit-details-marker{display:none}
.brief > summary::before{content:"+ ";color:var(--accent)}
.brief[open] > summary::before{content:"– "}
.brief > summary:focus-visible{outline:2px solid var(--t1);outline-offset:1px}
.brief > *:not(summary){padding:0 var(--s3);margin-bottom:var(--s2)}
.brief > p:first-of-type{padding-top:var(--s1)}
.brief > p:last-child{padding-bottom:var(--s3)}
.brief i{font-style:normal;color:var(--t1)}
.brief u{display:block;text-decoration:none;color:var(--t3)}
body.final .brief{display:none}
/* A whole unwritten block on a sub-page (heading + brief + placeholder),
   wrapped in <div class="draft">. On a finished page the prefix alone is not
   enough: the placeholder sentence itself would still show. Added 2026-09-17. */
body.final .draft{display:none}

/* ── AMBIENT FIELD ────────────────────────────────────────────────────────
   Fixed behind everything, running on its own clock, never stops. This is the
   "connecting dots just going on in the background" reference. It persists
   across every screen, which is a large part of why the page reads as ONE
   object rather than a stack of sections. */
#ambient{
  position:fixed;inset:0;width:100%;height:100%;
  z-index:-1;pointer-events:none;display:block;
}

/* ── GRAIN ────────────────────────────────────────────────────────────────
   Layer 1 of the ambient stack: paper grain. Editorial worlds live or die on
   it — a perfectly clean dark field reads as a CSS default, and world C's
   whole risk is reading as a code editor at thumbnail size.

   Inline SVG feTurbulence, not a tiled PNG: costs no request and no bytes
   against the 60KB texture budget, and there is no binary to keep in the
   repo. Fixed and pointer-events:none so it never repaints on scroll —
   attaching grain to a scrolling container is a permanent GPU cost.

   3% opacity. Above ~5% it stops being paper and starts being noise. */
.grain{
  position:fixed;inset:0;z-index:-1;pointer-events:none;
  opacity:.03;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:140px 140px;
}
/* Never behind reading copy — texture under body text is the fastest way to
   look amateur.

   The one light screen needs no rule: `.paper` declares its own background,
   and an in-flow background paints above a z-index:-1 fixed layer. It covers
   the grain for free. (Same mechanism that lets #ambient show through the
   dark screens but not through the paper one.)

   The prose routes DO need a rule, because they sit on --ground like every
   dark screen. They set <body class="reading"> and opt out. */
body.reading .grain{display:none}
@media (prefers-reduced-motion:reduce){ .grain{opacity:.02} }

/* ── NAV ──────────────────────────────────────────────────────────────────
   Deliberately not a pill bar. Sub-pages live at their own links; this is a
   directory line, not navigation chrome. */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:40;
  display:flex;align-items:baseline;gap:22px;
  padding:16px var(--pad);pointer-events:none;
}
/* NOT mix-blend-mode:difference. It garbles the wordmark wherever the nav
   crosses body text, which on a page of pinned full-screen scenes is most of
   the scroll. A soft top scrim keeps the nav legible over anything without
   drawing a bar. */
.nav::before{
  content:"";position:absolute;inset:0 0 auto;height:260%;z-index:-1;
  background:linear-gradient(to bottom,
    color-mix(in srgb,var(--ground) 95%,transparent) 0%,
    color-mix(in srgb,var(--ground) 78%,transparent) 34%,
    color-mix(in srgb,var(--ground) 40%,transparent) 66%,
    transparent 100%);
}
/* ── THE BREAKPOINT RULE — 2026-09-20, the founder's ruling. Do not re-mix. ──
   VIEWPORT SIZE decides LAYOUT. POINTER decides TOUCH TARGETS.

   Every layout breakpoint tests width and height only — the handset layout is
   `(max-width:820px), (max-height:500px)`, and its desktop complement is
   `(min-width:821px) and (min-height:501px)`. Only the nav's ergonomics — the
   hamburger, the 44px targets, the scrim that clears it — may also test
   `(hover:none) and (pointer:coarse)`.

   The `max-height:500px` half is not decoration: a phone held LANDSCAPE is
   844x390 — wider than 820 — so width alone would hand a phone the desktop
   layout. No tablet is under 500px tall in either orientation, so the two
   cases stay separated without asking about the pointer.

   Why: on 2026-09-19 fourteen breakpoints had `, (hover:none) and
   (pointer:coarse)` appended so an Android phone in Chrome's "Desktop Site"
   mode (which lies about its width) would still get the phone layout. An iPad
   is also a coarse, hoverless pointer, so every iPad silently became a giant
   phone — stacked columns, collapsed section heights, 96px Genie — and the
   desktop right-column rule below stopped applying to it. That trade is now
   made the other way round on purpose: a phone in Desktop Site mode gets a
   squeezed desktop layout with finger-sized buttons, which is what it asked
   for, and every iPad gets its real layout back.

   `motion.js` HANDSET_QUERY and `theme.js` must agree with this file or the
   same bug returns. theme.js no longer guesses — it reads whether the
   hamburger is visible. */
/* Phone needs more scrim, not less. The nav is short there but the display
   serif behind it is nearly as tall as the nav itself, so a 190% scrim ended
   below the headline's ascenders and the wordmark sat directly on top of live
   letterforms. Both were unreadable at the exact moment the page is being
   judged from a forwarded screenshot. */
@media (max-width:700px), (hover:none) and (pointer:coarse){ .nav::before{height:340%} }
/* The page has exactly one light screen, and a fixed nav has to survive
   crossing it. Toggled from motion.js when the paper section is behind the
   nav band — colours flip, scrim flips, nothing else moves. */
.nav--onpaper .wordmark{color:var(--ink)}
.nav--onpaper .links a{color:color-mix(in srgb,var(--ink) 68%,transparent)}
/* 2026-09-17: the two rules above never won. `.nav .wordmark` and
   `.nav .links a` below have the same specificity and come later, so over
   the navy slab the wordmark, links and theme toggle stayed dark-on-navy
   (seen in headless screenshots at 1440 and 390). Same fix as .paper: remap
   the TOKENS those rules read, so every child flips without a per-class
   override. */
.nav.nav--onpaper{
  --t1:var(--ink);
  --t2:color-mix(in srgb,var(--ink) 76%,var(--paper));
  --hairline:color-mix(in srgb,var(--ink) 26%,var(--paper));
}
.nav--onpaper::before{
  background:linear-gradient(to bottom,
    color-mix(in srgb,var(--paper) 95%,transparent) 0%,
    color-mix(in srgb,var(--paper) 78%,transparent) 34%,
    color-mix(in srgb,var(--paper) 40%,transparent) 66%,
    transparent 100%);
}
.nav a{pointer-events:auto;color:inherit;text-decoration:none}
/* Gate A #8: a text-only logo is a slop tell. The SF mark already exists as
   favicon.png; the wordmark leads with it rather than being a bare <span>. */
.nav .wordmark{font-family:Newsreader,Georgia,'Times New Roman',serif;font-weight:600;font-size:15px;
  letter-spacing:-.01em;margin-right:auto;color:var(--t1);
  display:inline-flex;align-items:center;gap:var(--s2)}
/* The mark is MASKED, not placed.
   favicon.png is the SF mark of record and it is correctly transparent, but
   its glyph is #5EEAD4 — bright mint, and a Tailwind default at that. It
   appears in none of the three worlds, whose only chromatic notes are
   oxblood --accent and the semantic --fault teal. Dropping it in raw would
   add a fourth brand colour and break the one-accent lock in exchange for
   fixing a text-only-logo tell: a bad trade.
   Used as a mask it renders in currentColor, so it is monochrome, it costs
   no new asset, and it flips on the paper screen for free. */
.nav .wordmark::before{
  content:"";width:16px;height:16px;flex:0 0 auto;
  background:currentColor;
  -webkit-mask:url(../favicon.png) center/contain no-repeat;
          mask:url(../favicon.png) center/contain no-repeat;
}
.nav .links{display:flex;gap:18px}
.nav .links a{font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-size:10px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--t2);padding-bottom:2px;
  border-bottom:1px solid transparent}
.nav .links a:hover,.nav .links a:focus-visible{border-bottom-color:currentColor}
.nav-menu{display:flex;margin-left:auto}
.nav-menu > summary{display:none}
.nav-menu__panel{display:flex;align-items:baseline;gap:22px}
/* Method / FAQ / What it can't do / Waitlist. This used to be
   `display:none` below 700px, which took all four destinations off the page
   for every phone visitor — not a style choice, a navigation failure (the
   Waitlist link is how a phone visitor reaches the one form on the page
   without scrolling the whole way down). Fixed here in CSS only, because
   the fix cannot touch markup: the strip stays on the same row as the
   wordmark and the theme toggle, given the flexible width between them, and
   scrolls horizontally inside that width rather than wrapping to a second
   line (a second nav row would need the scrim above — .nav::before — resized
   to match, and would eat into the pinned canvas immediately below it).
   -webkit-overflow-scrolling:touch is Safari-only and harmless dead weight
   everywhere else; the two scrollbar-hiding lines are the standard pair
   (Firefox/IE property, WebKit pseudo-element) for a strip that is meant to
   read as nav text, not as a visible scroll widget. */
@media (max-width:700px), (hover:none) and (pointer:coarse){
  .nav .wordmark{margin-right:0}     /* was auto — with .links now visible and
    flexible, an auto margin here and flex-grow on .links would both be
    claiming the same free space; fixing the wordmark's margin at 0 and
    letting .links alone absorb the space between it and the toggle keeps
    the split predictable instead of fought over by two rules at once. */
  .nav .links{
    display:flex;flex:1 1 0;min-width:0;gap:16px;
    overflow-x:auto;-webkit-overflow-scrolling:touch;
    scrollbar-width:none;pointer-events:auto;
  }
  .nav .links::-webkit-scrollbar{display:none}
  .nav .links a{
    flex:0 0 auto;white-space:nowrap;
    /* padding, not font-size, for the tap target (brief's own instruction) —
       a link this short and this close to the fixed nav's own edge needs
       real vertical room around the visible 10px label, not a bigger label. */
    padding:13px 2px;
  }
  /* TWO ROWS ON PHONES, 2026-09-17. The one-row strip above left about 110px
     for the four links at 390px wide, so "What it can't do" and "Waitlist"
     sat off the edge of the strip and a visitor had to guess to swipe it.
     Measured: the four links need ~290px, which fits a full-width row at
     360px. So the nav wraps: wordmark left and theme toggle right on row 1,
     links on their own full row underneath. overflow-x:auto above stays as a
     safety net for very narrow screens. The scrim is a % of the nav's height,
     so it is cut from 340% to keep roughly the same painted depth now that
     the nav is taller. Desktop (>700px) is untouched. */
  .nav{flex-wrap:wrap;align-items:center;column-gap:12px;row-gap:0;padding-bottom:6px}
  .nav .wordmark{margin-right:auto}
  .nav .links{order:3;flex:1 1 100%;padding-left:0}
  .nav .links a{padding:10px 2px}
  .nav::before{height:240%}
}

/* ── THEME TOGGLE ─────────────────────────────────────────────────────────
   Auto/Light/Dark, three buttons in one hairline-bordered pill. Quiet by
   design — no fill until pressed — so it sits in the nav without competing
   with the wordmark for attention. */
.theme{
  display:flex;pointer-events:auto;flex:0 0 auto;
  border:1px solid var(--hairline);border-radius:3px;overflow:hidden;
}
.theme__btn{
  background:none;border:none;cursor:pointer;pointer-events:auto;
  padding:5px 9px;font-family:inherit;font-size:10.5px;letter-spacing:.03em;
  color:var(--t2);white-space:nowrap;
}
.theme__btn + .theme__btn{border-left:1px solid var(--hairline)}
.theme__btn:hover{color:var(--t1)}
.theme__btn[aria-pressed="true"]{
  background:color-mix(in srgb,var(--t1) 14%,transparent);
  color:var(--t1);font-weight:600;
}
.theme__btn:focus-visible{outline:2px solid var(--t1);outline-offset:-2px}
/* Touch range: the desktop 5px 9px pad is a mouse-cursor size, not a
   fingertip one. Padding grows here, never the font — a bigger label would
   fight the wordmark for room (see .nav .links above); a bigger tap zone
   around the same label does not. This does not reach the ~44px target
   (it lands near 37px tall) without either wrapping the labels or pushing
   the whole nav row taller than the wordmark next to it — the honest
   number is "closer", not "there"; a true 44px fingertip pill is a markup
   change (three real buttons instead of one text-sized one each), flagged
   in the report rather than attempted here. */
@media (max-width:700px), (hover:none) and (pointer:coarse){
  /* min-height, NOT more padding, to reach a real tap target. Padding maths
     tops out around 43px here (12+12 plus a 10.5px label), and the honest
     answer is to state the 44px Apple asks for and let the box centre its
     own label. WCAG 2.2 SC 2.5.8 only requires 24x24 and the old 37px already
     cleared that, so this is the guideline, not a failure being fixed — the
     founder asked for "perfectly optimized according to what should be
     optimized", and a thumb on a moving bus is what should be optimized.
     inline-flex so the label sits centred in the taller box instead of
     riding its top edge. */
  .theme__btn{
    padding:12px 9px;min-height:44px;
    display:inline-flex;align-items:center;justify-content:center;
  }
}
/* Below 700px the directory links now SHARE the row with the toggle
   (above) instead of disappearing, so the toggle has less width to work
   with than it used to — shrink the label and the horizontal padding here,
   never the vertical padding that carries the tap target. */
@media (max-width:400px){
  /* horizontal padding and label shrink to fit three buttons next to the
     wordmark; the 44px min-height from the block above is inherited and is
     deliberately NOT reduced — the row gets narrower, never shorter. */
  .theme__btn{padding:12px 6px;font-size:9.5px}
}

/* Phone navigation is one calm row. The full directory and colour control
   move into a native disclosure, so a 320px screen never has to compress four
   destinations into unreadable labels. Desktop and tablet keep the directory
   line above unchanged. */
@media (max-width:700px), (hover:none) and (pointer:coarse){
  .nav{flex-wrap:nowrap;min-height:64px;padding:10px var(--pad);gap:0}
  .nav::before{height:220%}
  .nav-menu{display:block;position:relative;margin-left:auto;pointer-events:auto}
  .nav-menu > summary{
    display:flex;align-items:center;justify-content:center;min-width:58px;min-height:44px;
    list-style:none;border:1px solid var(--hairline);border-radius:3px;
    color:var(--t1);font-size:13px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;
    cursor:pointer;
  }
  .nav-menu > summary::-webkit-details-marker{display:none}
  .nav-menu > summary:focus-visible{outline:2px solid var(--t1);outline-offset:2px}
  .nav-menu__panel{display:none}
  .nav-menu[open] .nav-menu__panel{
    display:flex;flex-direction:column;gap:8px;position:absolute;top:calc(100% + 8px);right:0;
    width:min(280px,calc(100vw - 2 * var(--pad)));padding:10px;
    background:var(--ground);border:1px solid var(--hairline);border-radius:4px;
    box-shadow:0 10px 22px color-mix(in srgb,var(--t1) 14%,transparent);
  }
  .nav .links{display:grid;order:initial;flex:none;width:100%;min-width:0;overflow:visible;gap:2px}
  .nav .links a{
    display:flex;align-items:center;min-height:44px;padding:10px;
    white-space:normal;font-size:14px;letter-spacing:.01em;
  }
  .nav-menu .theme{align-self:stretch;margin-top:2px}
  .nav-menu .theme__btn{flex:1}
}

/* ── PIN RIG (the two motion screens) ─────────────────────────────────────
   Contract, do not vary — DESKTOP/TABLET ONLY now (see the phone break
   below): 130svh parent, 100svh sticky child. Progress is -top/(height-vh);
   the driver lerps toward it at 0.11 so the scrub reads as a camera rather
   than a slider. (This comment said 170svh; --rig moved to 130svh on
   2026-09-14 and the comment was never updated — fixed while touching this
   section for the phone break.) */
.rig{height:var(--rig);position:relative;pointer-events:none}
.pin{position:sticky;top:0;height:100svh;overflow:hidden}
.pin canvas{position:absolute;inset:0;width:100%;height:100%;display:block}
/* A viewport at most 700px wide occupies one live visible viewport. Its scroll
   scrub is held in motion.js while the scene is active, so there is no extra
   physical page height to make a short phone feel taller. A wide coarse handset
   still receives the compact handset layout elsewhere, but retains this base
   130% physical pinned composition and native scrolling. */
@media (max-width:700px){
  .rig{height:100dvh}
  .pin{height:100dvh}
}

.genie{position:fixed;left:0;top:0;z-index:8;width:140px;height:140px;
  object-fit:contain;pointer-events:none;opacity:1;visibility:hidden;transform:translate3d(-60px,40svh,0);
  transform-origin:center center;will-change:transform,opacity;user-select:none;
  background:transparent;border-radius:50%;padding:0;
  transition:background-color .22s ease,padding .22s ease,border-radius .22s ease}
.genie--paper{background:var(--ground);border-radius:22px;padding:5px}
/* Only the departure crosses in front of the portal. The later Genie keeps
   the landing page's established layer below the fixed navigation. */
.genie--launch{z-index:42}
/* The opening portrait is a whole little scene: a chunky rounded portal, the
   Genie leaning down through it, and both palms planted on the sill. It is
   visually above the nav but pointer-inert, so every nav control stays live. */
.gp{position:fixed;left:0;top:0;width:0;height:0;z-index:41;pointer-events:none;user-select:none}
.gp__window{position:absolute;z-index:0;left:0;top:0;overflow:hidden;
  border-radius:28px 28px 18px 18px;background:color-mix(in srgb,var(--ground) 91%,var(--accent));
  box-shadow:0 10px 18px color-mix(in srgb,var(--t1) 18%,transparent);will-change:transform,opacity;transform-origin:50% 100%}
/* The trim is deliberately a separate layer. It closes the arch over the
   portrait, so this reads as a real small portal instead of a thin box. */
.gp__frame{position:absolute;z-index:3;left:0;top:0;border:5px solid var(--t1);
  border-radius:28px 28px 18px 18px;box-shadow:inset 0 0 0 3px color-mix(in srgb,var(--accent) 42%,transparent),0 7px 0 -2px var(--accent);
  pointer-events:none;will-change:transform,opacity;transform-origin:50% 100%}
.gp__frame::after{content:"";position:absolute;left:-8px;right:-8px;bottom:-10px;height:13px;
  border-radius:9px;background:var(--t1);box-shadow:0 3px 0 var(--accent)}
.gp__aperture{position:absolute;left:0;top:0;z-index:1;overflow:hidden;border-radius:23px 23px 13px 13px;
  will-change:transform;transform-origin:50% 100%}
.gp__head{position:absolute;left:0;top:0;width:100%;height:100%;
  transform-origin:50% 18%;will-change:transform}
.gp__head img{display:block;width:100%;height:auto}
.gp__lids{position:absolute;left:0;top:0;width:100%;height:auto;aspect-ratio:1;overflow:visible}
.gp__hand{display:block;position:absolute;top:0;left:0;z-index:4;overflow:visible;will-change:transform;transform-origin:50% 0}
.gp__f{transition:none}
@media (max-width:820px), (max-height:500px){.genie{width:96px;height:96px;transform:translate3d(-45px,40svh,0)}}
@media (prefers-reduced-motion:reduce){.genie{will-change:auto;transition:none}}

/* copy revealed BY the scrub, never by a timer */
.over{position:absolute;left:0;right:0;bottom:0;padding:0 var(--pad) 46px;
  opacity:0;transform:translateY(14px);will-change:opacity,transform}
.reg{position:absolute;right:var(--pad);bottom:46px;text-align:right;
  display:flex;flex-direction:column;gap:5px;opacity:0}

/* ── PHONE: THE PIN STAYS ─────────────────────────────────────────────────
   Reversed 2026-09-14, the same day it was added. An earlier instruction, "I
   want the animations to stop the scroll on mobile", was read as "stop them
   from hijacking the scroll", and both rigs were unpinned below 820px. He
   meant the opposite reading: "i want the animation to stop the scroll dude
   to make sure ppl see it". The pin IS the point — it is the only thing that
   makes the brain assembly and the iPad write impossible to swipe past.
   So there is deliberately no unpin rule here, and there should not be one.
   If `.rig{height:auto}` or `.pin{position:static}` is ever re-added at a
   phone width, motion.js's rigProgress() divides by the actual difference
   between rig and pin heights, which goes negative, clamps to 0 and holds
   there forever — a permanently
   unassembled brain and an iPad frozen at its smallest pose, on every phone.
   The two files move together or not at all.
   The REST of that pass was NOT reverted and has nothing to do with the pin:
   nav links restored below 700px, .ask input at 16px, 44px theme targets,
   .screen moved to svh. Leave those alone.
   If the pinned scroll ever feels too LONG on a phone, the number to change
   is --rig, not this. */

/* SCREEN 2 · the copy sits on the RIGHT, against the brain on the left.
   Founder directive 2026-08-07: "acc the text on the right".
   The block is pushed right; the text inside stays LEFT-aligned, because
   right-aligned ragged-left prose is harder to read and this is the one screen
   whose copy is a diagnosis the reader has to follow. Desktop only — under
   820px there is one column and the side stops meaning anything. */
@media (min-width:821px) and (min-height:501px){
  #ov2 > *{max-width:min(38rem,52%);margin-left:auto}
  #ov2 .claim{text-align:left}
}

@media (max-width:820px), (max-height:500px){
  .reg{right:auto;left:var(--pad);bottom:auto;top:76px;text-align:left}
  .over{padding-bottom:34px}
  /* The opening argument is present from the first phone frame. Its art owns
     the upper half; the existing headline owns a distinct lower reading area. */
  #ov1{top:clamp(400px,57svh,480px);bottom:auto;padding-bottom:0}
  /* The second body is three lines at 320px. Its own slightly higher slot
     keeps the complete argument inside a 600px short viewport. */
  #ov2{top:clamp(388px,55svh,468px);bottom:auto;padding-bottom:0}
  #ov1 .claim,#ov2 .claim{line-height:1.03}
}
/* The compact one-row phone nav leaves the source register clear below it. */
@media (max-width:700px), (hover:none) and (pointer:coarse){ .reg{top:76px} }

/* ── STATIC SCREENS ───────────────────────────────────────────────────── */
/* svh, not vh: vh on a phone is defined against a viewport that can change
   size as the browser's own address bar shows and hides, so plain vh here
   would make this padding visibly jump by a few px on every scroll-triggered
   chrome toggle. svh is fixed to the SMALLEST that viewport ever gets, so it
   never jumps — same reason --rig and every other height in this file
   already uses svh; this was the one place still on the unstable unit. */
.screen{position:relative;padding:clamp(80px,13svh,150px) var(--pad)}
.screen--tall{min-height:100svh;display:flex;flex-direction:column;justify-content:center}

/* Phone sections keep the page's large editorial breaks, but stop spending a
   second empty viewport above and below short content. The opening rigs remain
   full-screen and pinned; this only changes the static reading sections. */
@media (max-width:820px), (max-height:500px){
  .screen{padding:clamp(52px,8svh,72px) var(--pad)}
  .screen--tall{min-height:0;justify-content:flex-start}
  /* The only in-page nav target is the access form. Leave its compact section
     alone, then offset hash navigation below the two-row fixed nav. */
  #ask{scroll-margin-top:112px}
}

/* ── OVERLAP ──────────────────────────────────────────────────────────────
   Founder directive 2026-08-05: "I want the text to overlap on the images."
   Text and art share ONE grid cell and are stacked with z-index, so the words
   sit on the picture rather than in a column beside it. The scrim is a local
   wash under the text only — never a full-width band, which would rebuild the
   stack we just tore down. */
.lay{display:grid;grid-template-columns:1fr;align-items:end}
.lay > *{grid-area:1/1}
.lay__art{position:relative;width:100%}
.lay__art canvas,.lay__art .plate{display:block;width:100%}
.lay__txt{position:relative;z-index:2;padding:0 0 clamp(14px,3vw,40px);
  max-width:min(46rem,86%);isolation:isolate}
/* The scrim has to hug the text. An earlier, wider version reached ~86% of the
   viewport and faded out only at 78%, which meant it occluded the ambient field
   across most of the screen — and because the ambient is the only thing moving
   back there, its absence read as a lighter RECTANGLE sitting behind the copy.
   A scrim that announces itself is worse than no scrim. Tighter extent, earlier
   falloff, anchored under the text's own baseline. */
.lay__txt::before{
  content:"";position:absolute;inset:-20px -32px -14px -28px;z-index:-1;
  background:radial-gradient(72% 88% at 12% 88%,
    color-mix(in srgb,var(--ground) 88%,transparent) 0%,
    color-mix(in srgb,var(--ground) 58%,transparent) 38%,
    transparent 68%);
}
.lay--top{align-items:start}
.lay--top .lay__txt{padding:clamp(14px,3vw,40px) 0 0}

/* ── THE SCANNED SPECIMEN (screen 4) ──────────────────────────────────────
   A MASK over var(--t3), not a coloured image — the same mechanism as the SF
   wordmark below. One greyscale asset serves all three worlds, it can never be
   brighter than --t3 because it has no colour of its own, and no fourth hue
   enters the palette. Swapping the specimen is swapping one PNG.

   It is pushed to the right half on desktop because the headline owns the left.
   The overlap the founder asked for is kept — the two still share one grid cell
   and the art passes under the type — but what it now passes under is a lit
   surface, not a sentence. Text over text was the actual defect. */
.specimen{
  width:min(64%,620px); margin:-2% 5% 0 auto; aspect-ratio:1;
  background:var(--t3);
  -webkit-mask:url(specimen-heart.png) center/contain no-repeat;
          mask:url(specimen-heart.png) center/contain no-repeat;
}
/* The receipt. Real selectable text and a real link, never baked into the
   pixels — an image of a citation is exactly the thing this screen argues
   against, and CC BY requires the attribution be actually readable. */
.specimen__src{
  width:min(74%,700px); margin:10px 5% 0 auto; text-align:right;
  font-size:9.5px; font-weight:400; letter-spacing:.04em; color:var(--t3); line-height:1.5}
/* Pinned to the OLD mono weight and tone on purpose. The partner's ruling was
   about the caption over the moving field; this receipt sits on ground under
   the specimen, where it measures Lc 43, not Lc 0. Left unchanged rather than
   swept along with a rule it was not part of — flagged as an open call. */
/* nowrap on the citation itself: left to wrap freely it broke at the hyphen
   inside the accession number and printed "3DPX-" on one line and "020991" on
   the next, which on the one screen about citing things reads as a broken id */
.specimen__src a{color:inherit;text-decoration:underline;text-underline-offset:3px;
  white-space:nowrap}

@media (max-width:820px), (max-height:500px){
  /* phone: the art is its own row under the copy, so it gets the full width
     and the caption goes back under it, left-aligned like every other stamp */
  .specimen{width:min(86%,360px);margin:0 auto}
  .specimen__src{width:100%;margin-inline:0;text-align:left}
}

/* PHONE: the overlap is kept but relaxed. At 390px a five-line headline is
   taller than the whole plate, so stacking them in one cell puts type on type
   and neither survives. Text leads, art follows and rides up under it — the
   layers still touch, but nothing is printed over a fact. */
@media (max-width:820px), (max-height:500px){
  .lay{grid-template-rows:auto auto}
  .lay__txt{grid-area:1/1;max-width:none;padding-bottom:0}
  .lay__art{grid-area:2/1;margin-top:-26px}
  .lay__txt::before{inset:-18px -20px -10px -20px}
  .lay--top .lay__txt{padding-top:0}
}

/* ── PAPER ────────────────────────────────────────────────────────────────
   Used EXACTLY ONCE on the page. One inversion is punctuation; alternating
   grounds is the band stack that got rejected five times. Ink = the artifact,
   paper = the argument — so the honest-limits screen is the one that earns it. */
/* FIXED 2026-09-14. Every rule inside this section reads --t1/--t2/--t3/
   --accent/--hairline, and all five of those are defined AGAINST --ground.
   On an inverted slab they land ink-on-ink or paper-on-paper and disappear.
   Only .claim, .body and .mono were ever remapped, so the brief panels, the
   DRAFT SLOT markers and the "longer version of this list" link were all
   invisible here — the founder hit exactly those three on 2026-09-14.

   The fix is to REDEFINE THE TOKENS for this subtree rather than write a
   .paper override per class. Every child rule then keeps working untouched,
   and the next class added inside .paper is correct without anyone
   remembering this. Derived with color-mix from --ink and --paper, so the
   zero-hex contract holds and all four worlds get it for free.

   NOTE: this was broken in worlds A/B/C too, where --paper was the LIGHT
   surface and --t2 was a light-on-dark grey. The light-mode inversion did
   not cause it; it made it impossible to miss. */
.paper{
  background:var(--paper);color:var(--ink);
  padding-left:clamp(230px,21vw,320px);
  --t1:var(--ink);
  --t2:color-mix(in srgb,var(--ink) 76%,var(--paper));
  --t3:color-mix(in srgb,var(--ink) 62%,var(--paper));   /* 62, not 56: at 56 the
         dark slab put .brief u at 4.11:1, under the 4.5 small text needs. 62
         clears it in both modes (5.8:1 light, 5.0:1 dark). */
  --hairline:color-mix(in srgb,var(--ink) 26%,var(--paper));
  --accent:color-mix(in srgb,var(--ink) 70%,var(--paper));
}
@media (max-width:820px), (max-height:500px){
  /* The compact paper Genie docks above and to the left of the title. This
     keeps 184–200px clear for him instead of reserving 320px on every phone,
     while leaving the section's normal lower breathing room. */
  .paper{padding-left:var(--pad);padding-top:clamp(184px,23svh,200px)}
  /* The small kicker enters beside the Genie; the title below keeps its
     standard reading edge. This clears the visible pose without adding height. */
  .paper [data-copy="s4-eyebrow"]{margin-left:40px}
}
.paper .claim{color:var(--ink)}
.paper .claim em{color:var(--ink);opacity:.62}
.paper .body{color:var(--ink);opacity:.82}
.paper .mono{color:var(--ink);opacity:.55}

/* ── LISTS THAT CARRY DATA ────────────────────────────────────────────── */
/* 5 surfaces must fit on one row at desktop — 4+1 reads as an accident.
   minmax floor and max-width are tuned together: 72rem / 5 ≈ 230px per column. */
.cols{display:grid;gap:clamp(22px,2.6vw,38px);margin-top:34px;
  grid-template-columns:repeat(auto-fit,minmax(192px,1fr));max-width:74rem}
.cols h3{font-family:Newsreader,Georgia,'Times New Roman',serif;font-weight:400;font-size:20px;
  letter-spacing:-.01em;margin-bottom:6px}
.cols p{font-size:13.5px;line-height:1.5;color:var(--t2)}
.cols .mono{display:block;margin-bottom:9px}

.ledger{margin-top:30px;max-width:52rem;border-top:1px solid currentColor}
.ledger div{display:flex;gap:16px;padding:11px 0;
  border-bottom:1px solid color-mix(in srgb,currentColor 22%,transparent)}
.ledger dt{flex:0 0 8.5rem;
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-size:10px;letter-spacing:.06em;text-transform:uppercase;padding-top:3px;opacity:.7}
.ledger dd{font-size:14.5px;line-height:1.5}

/* ── THE ASK ──────────────────────────────────────────────────────────────
   Swappable. Founder: "Make this editable because I'm gonna change this later."
   Everything about the ask is inside <section id="ask"> and this block. */
.ask{display:flex;flex-wrap:wrap;gap:0;margin-top:26px;max-width:30rem}
.ask input{
  flex:1 1 13rem;min-width:0;background:transparent;color:var(--t1);
  border:1px solid var(--t3);border-right:none;
  border-radius:2px 0 0 2px;padding:13px 15px;
  /* 16px, not 14.5px. Below 16px, iOS Safari treats a focused input as "the
     page needs to zoom in to let you read what you're typing" and zooms the
     WHOLE PAGE, not just the field — on the one form this page exists to
     get filled out. This was the classic version of that bug, present at
     every width, not just narrow ones: font-size, not a media query, is
     the fix. */
  font-family:inherit;font-size:16px;
}
.ask input::placeholder{color:var(--t3)}
.ask input:focus-visible{outline:2px solid var(--t1);outline-offset:1px}
.ask button{
  flex:0 0 auto;background:var(--accent);color:var(--accent-ink);
  border:1px solid var(--accent);border-radius:0 2px 2px 0;
  /* 15px vertical, not 13px: with the input's own font now 16px its box is
     a couple px taller than before, and the button sat in the same joined
     pill — matching its padding keeps the two edges level again, and pushes
     the button itself a little closer to a real tap target as a side effect. */
  padding:15px 20px;cursor:pointer;
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-size:11px;letter-spacing:.06em;
  text-transform:uppercase;
}
.ask button:hover{filter:brightness(1.14)}
.ask button:focus-visible{outline:2px solid var(--t1);outline-offset:2px}
/* The bot check gets its own row under the joined input+button pill. Without a
   full-width basis it competes for the row, and the two controls that have to
   touch stop touching. min-height reserves the space the widget will occupy so
   the copy underneath does not jump when it finishes loading. */
.ask .cf-turnstile{flex:1 0 100%;margin-top:14px;min-height:65px}
.ask__note{margin-top:14px;font-weight:400;font-size:13px;letter-spacing:0;opacity:.75}
.ask__note a{color:inherit;text-decoration:underline;text-underline-offset:3px;
  text-decoration-color:color-mix(in srgb,currentColor 45%,transparent)}
.ask__note a:hover,.ask__note a:focus-visible{text-decoration-color:currentColor}
/* the opening rig is pointer-events:none so the pinned scroll never catches a click; its links opt back in */
.reg a{pointer-events:auto;color:inherit;text-decoration:underline;text-underline-offset:3px;
  text-decoration-color:color-mix(in srgb,currentColor 35%,transparent)}
.reg a:hover,.reg a:focus-visible{text-decoration-color:currentColor}
/* s4: the "why NOT to sign up" list, stacked for reading */
.nots{list-style:none;margin-top:22px;max-width:44rem;border-top:1px solid color-mix(in srgb,currentColor 22%,transparent)}
.nots li{display:flex;justify-content:space-between;gap:16px;padding:13px 0;font-size:18px;line-height:1.4;
  border-bottom:1px solid color-mix(in srgb,currentColor 22%,transparent)}
.nots b{font-weight:700;text-align:right}
.nots small{display:block;margin-top:3px;font-size:15px;opacity:.7}
[data-copy="s4-eyebrow"]{display:block;margin-bottom:14px}
/* phone: when the button can't fit beside the email box, both go full width and stack as one block */
@media (max-width:520px){
  .ask input{flex-basis:100%;border-right:1px solid var(--t3);border-radius:2px 2px 0 0}
  .ask button{flex-basis:100%;border-radius:0 0 2px 2px}
}
.ask__said{margin-top:10px;color:var(--t1)}
.ask__said[data-tone="bad"]{color:var(--t2)}

/* HONEYPOT. Not display:none — a competent bot skips fields that are display
   none or visibility hidden, and so does anything reading the accessibility
   tree. This takes it out of flow and out of view while leaving it a real,
   fillable input for anything that just walks the DOM and types into inputs. */
.hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}

.ask button[aria-busy="true"]{opacity:.6;cursor:progress}

/* ── FOOT ─────────────────────────────────────────────────────────────── */
.foot{padding:44px var(--pad) 40px;border-top:1px solid var(--hairline);
  display:flex;flex-wrap:wrap;gap:18px 30px;align-items:baseline}
.foot a{color:var(--t2);text-decoration:none;border-bottom:1px solid transparent}
.foot a:hover,.foot a:focus-visible{border-bottom-color:currentColor}
.foot .spacer{margin-left:auto}

/* Mobile-only cadence: preserve the larger section breaks above, then close
   the repeated internal gaps that separated a heading from its own evidence. */
@media (max-width:820px), (max-height:500px){
  .cols{margin-top:26px}
  .nots{margin-top:18px}
  .ledger{margin-top:24px}
  .ask{margin-top:20px}
  .foot{padding-top:30px;padding-bottom:28px;gap:14px 24px}
}

/* ── DISCLOSURE ───────────────────────────────────────────────────────────
   The one primitive, used here for the specimen widget and reusable for app
   onboarding and the flowchart. Do not build a second disclosure component.

   Level 0 must stand alone: a visitor who never clicks must not be misled.
   That is the honesty invariant expressed as interaction design, and it is
   why the resting state shows a real node with its real slide number rather
   than a teaser. */
.spec{
  border:1px solid var(--hairline);
  padding:var(--s5) var(--s5) var(--s4);
  max-width:44rem;margin-top:var(--s5);
  background:color-mix(in srgb,var(--ground) 72%,transparent);
}
.spec__lab{display:block;margin-bottom:var(--s3)}
.spec__node{
  border:1px solid var(--hairline);padding:var(--s4);
  font-size:15px;line-height:1.45;color:var(--t1);
}
.spec__node .p{display:block;margin-top:var(--s3);
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-size:9.5px;letter-spacing:.06em;color:var(--t3)}
.spec [data-level]{
  animation:sfIn var(--t-disclose) var(--ease) both;
}
.spec [data-level="1"],.spec [data-level="2"]{margin-top:var(--s4)}
.spec__q{
  border-left:2px solid var(--fault);padding-left:var(--s4);
  font-size:14.5px;line-height:1.5;color:var(--t2);
}
.spec__q i{font-style:normal;color:var(--t1)}
.spec__slide{
  border:1px solid var(--hairline);padding:var(--s4);
  font-size:13.5px;line-height:1.55;color:var(--t2);
  background:color-mix(in srgb,var(--t1) 4%,transparent);
}
.spec__slide b{display:block;color:var(--t1);font-weight:600;margin-bottom:var(--s2)}
@keyframes sfIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

/* The affordance NAMES what is behind it. Never "Learn more" — you should
   know what a click costs before you spend it. */
.spec__more{
  margin-top:var(--s4);background:none;cursor:pointer;
  border:1px solid var(--t3);border-radius:2px;
  padding:9px var(--s4);color:var(--t1);
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-size:10px;letter-spacing:.06em;
  text-transform:uppercase;
  transition:border-color 140ms var(--ease),color 140ms var(--ease);
}
.spec__more:hover{border-color:var(--t1)}
.spec__more:focus-visible{outline:2px solid var(--t1);outline-offset:2px}
.spec__reset{
  margin-top:var(--s3);background:none;border:none;cursor:pointer;padding:0;
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-size:9.5px;letter-spacing:.06em;
  color:var(--t3);text-decoration:underline;text-underline-offset:3px;
}
.spec__reset:focus-visible{outline:2px solid var(--t1);outline-offset:2px}
@media (prefers-reduced-motion:reduce){ .spec [data-level]{animation:none} }

/* ── PROVENANCE GUTTER ────────────────────────────────────────────────────
   Each stamp is placed at the vertical centre of the box it belongs to, by
   the same code that draws the boxes. It tracks the map or it is decoration,
   and decoration is what this page is trying to get rid of. */
/* A real two-column grid, NOT an absolutely-positioned overlay with canvas
   padding. Padding on a <canvas> shrinks and offsets the drawing surface
   relative to its bitmap, so every stamp would sit at a coordinate the map is
   no longer drawn at. As grid columns, the gutter and the canvas share a top
   edge and canvas-local y maps straight onto gutter-local y. */
.plate{display:grid;grid-template-columns:var(--gut,74px) minmax(0,1fr)}
.gutter{
  position:relative;
  border-right:1px solid var(--hairline);pointer-events:none;
}
.gutter[hidden]{display:none}
.plate:has(.gutter[hidden]){grid-template-columns:minmax(0,1fr)}
.gutter .s{
  position:absolute;left:var(--s4);line-height:1;transform:translateY(-50%);
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-weight:600;font-size:12px;letter-spacing:.02em;
  color:color-mix(in srgb,var(--t1) 37.5%,var(--t3));
  opacity:0;transition:opacity .3s var(--ease);
}
.gutter .s.in{opacity:1}
@media (max-width:820px), (max-height:500px){
  :root{--gut:46px}
  /* stays at D's 12px on phone too — the p.NN gutter is exactly the text that
     measured Lc 0, and the phone is the only screen he judges from. "p.17" at
     12px Inter is a similar width inside a 36px column — unverified after the
     mono-to-Inter font swap; flagged rather than re-asserted as measured fact. */
  .gutter .s{left:10px;font-size:12px}
}

/* ── PROSE ROUTES (/method/ /faq/ /limits/ /privacy/ /terms/ /404) ────────
   Depth goes sideways, not downward. These exist so the site is a site and
   not one page pretending to be one — and so the home page's composition
   never has to absorb another band to prove it has substance. */
.doc{padding:calc(var(--s7) + 34px) var(--pad) var(--s7);max-width:52rem}
.doc h1{font-family:Newsreader,Georgia,'Times New Roman',serif;font-weight:400;
  font-size:clamp(2rem,4.4vw,3rem);line-height:1.02;letter-spacing:-.02em;
  margin-bottom:var(--s4)}
.doc h2{font-family:Newsreader,Georgia,'Times New Roman',serif;font-weight:400;font-size:22px;
  letter-spacing:-.01em;margin:var(--s6) 0 var(--s3);color:var(--t1)}
.doc h3{font-size:14px;font-weight:600;margin:var(--s5) 0 var(--s2);color:var(--t1)}
.doc p,.doc li{font-size:15px;line-height:1.62;color:var(--t2);max-width:38rem}
.doc p+p{margin-top:var(--s3)}
.doc ul,.doc ol{margin:var(--s3) 0 0 var(--s4)}
.doc li{margin-bottom:var(--s2)}
.doc strong{color:var(--t1);font-weight:600}
.doc a{color:var(--t1);text-decoration:underline;text-underline-offset:3px;
  text-decoration-color:var(--t3)}
.doc a:hover{text-decoration-color:var(--t1)}
.doc .lede{margin-bottom:var(--s5)}
.doc__meta{margin-bottom:var(--s5)}
.doc dl{margin-top:var(--s4)}
.doc dt{font-size:15px;font-weight:600;color:var(--t1);margin-top:var(--s5)}
.doc dd{font-size:15px;line-height:1.62;color:var(--t2);margin-top:var(--s2);
  max-width:38rem}

/* A draft banner that cannot be mistaken for a reviewed legal document. */
.draftbar{
  border:1px solid var(--accent);
  background:color-mix(in srgb,var(--accent) 10%,transparent);
  padding:var(--s3) var(--s4);margin-bottom:var(--s5);max-width:38rem;
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-size:10.5px;line-height:1.6;color:var(--t1);
}

/* ── REVEAL (static screens) ──────────────────────────────────────────── */
.rv{opacity:0;transform:translateY(15px);
  transition:opacity .62s cubic-bezier(.22,.61,.36,1) var(--d,0ms),
             transform .62s cubic-bezier(.22,.61,.36,1) var(--d,0ms)}
.rv.in{opacity:1;transform:none}

.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}

/* Keyboard users hit a fixed nav and two pinned full-screen scenes before any
   content. Without this they tab through the whole rig to reach a paragraph. */
.skip{
  position:absolute;left:var(--s4);top:-60px;z-index:60;
  background:var(--ground);color:var(--t1);
  border:1px solid var(--t1);border-radius:2px;
  padding:10px var(--s4);text-decoration:none;
  font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;
  font-size:11px;letter-spacing:.06em;
  transition:top 140ms var(--ease);
}
.skip:focus{top:var(--s3)}

/* ── REDUCED MOTION ───────────────────────────────────────────────────────
   Settled state with ALL information present. Never removes content — the pin
   travel collapses to one screen and everything is simply already revealed. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .rig{height:100svh}
  .over,.reg{opacity:1!important;transform:none!important}
  .rv{opacity:1!important;transform:none!important;transition:none}
}
@media (prefers-reduced-motion:reduce) and (max-width:700px){.rig{height:100dvh}}
