/* The Long Way Home — Phase 1d (B4 motion spec v3, D022, D023; 2026-09-18).
   Espresso ground, one fixed stage (a canvas: the map, and each town's city), prints fixed on the
   stage, copy in flow. The stage changes state at stations (data-st) the engine reads; nothing is scrubbed. The
   composition is decided before first paint by the boot script (html[data-comp]); with no JS there is none, and the
   page is a single column with the prints in flow. */

@font-face{font-family:"Space Grotesk";font-style:normal;font-weight:700;font-display:swap;src:url(fonts/space-grotesk-700.35f8aec5.woff2) format("woff2")}
@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(fonts/inter-400.8909904a.woff2) format("woff2")}
@font-face{font-family:Inter;font-style:italic;font-weight:400;font-display:swap;src:url(fonts/inter-400-italic.7ea9d2f1.woff2) format("woff2")}
@font-face{font-family:"JetBrains Mono";font-style:normal;font-weight:400 500;font-display:swap;src:url(fonts/jetbrains-mono.b8365810.woff2) format("woff2")}

/* Tokens (B2 §1.3), declared once; the story page does not load tokens.css. */
:root,::backdrop{
  --ground:#211913;     /* espresso — page and canvas clear colour   (tokens.css --bg-paper) */
  --panel:#322820;      /* lifted panel — cards, dialogs. His option A, 2026-09-21 (Lane 2 review): the deeper token for section separation; ink 11.3:1, brass 6.4:1, rust-card 3.1:1 on it */
  --ink:#ECE3D0;        /* cream — headings, map line-work at full   (tokens.css --ink-primary) */
  --body-ink:#E4DACB;   /* body prose on espresso (≥ 7:1) */
  --quiet:#A89C8C;      /* eyebrows, readout, credit (≥ 4.5:1 on espresso at 11 px+) */
  --sheet:#F6F0E5;      /* the cream print border: review switch ?frame=print only since D032 */
  --sheet-ink:#5C5046;  /* ink on the cream sheet (≥ 7:1) */
  --brass:#D8A45C;      /* the thread, pins, focus ring               (tokens.css --accent-blue) */
  --rust:#A85230;       /* story-turn lines only (3.2:1 on espresso, large text) */
  --rust-card:#B85A34;  /* story-turn lines on the lifted portrait card (3.1:1 on --panel, large-text floor 3.0) */
  --camel:#C29B63;      /* visited legs, visits */
  --line:#4A3D31;       /* hairlines on espresso */
  /* D027: the warm-autumn supporting palette (webflow-rebrand/brand-foundation.md), one colour per town (--rust above is Oakland's) */
  --pine:#2F4738;       /* Jacksonville */
  --teal:#2C5D5A;       /* Gulfport (muted teal) */
  --oxblood:#6E2E2C;    /* free since D028 (Columbia is a brief move, not a chapter); kept so the data can pick it */
  --olive:#6F6A3A;      /* Hammond (olive drab) */
  --ochre:#C68A2E;      /* Wilmington */
  --display:"Space Grotesk",Inter,-apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif;
  --body:Inter,-apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,"SF Mono",Menlo,Consolas,monospace;
}

html{
  --measure:34rem;
  --pad-x:max(clamp(1.2rem,5vw,5.8rem),env(safe-area-inset-left));
  --pad-r:max(clamp(1.2rem,5vw,5.8rem),env(safe-area-inset-right));
  --stage-w:46vw;
  --band:38svh;          /* the phone's stage band: ONE height, never animated (D068) */
  --band-rest:38svh;     /* layout reads the same number, so the copy never shifts */
  --sec-top:clamp(8rem,18vh,12rem);
  --copy-r:50vw;         /* the copy column's right edge, px (set by the engine at measure) */
}
html[data-comp="tablet"]{--stage-w:42vw;--measure:31rem}
html[data-comp="portrait"]{--measure:31rem}

*{box-sizing:border-box}
html{scroll-behavior:smooth;background-color:var(--ground)}
body{margin:0;color:var(--body-ink);font-family:var(--body);font-size:clamp(1rem,.3vw + .95rem,1.12rem);line-height:1.62;-webkit-font-smoothing:antialiased;overflow-x:hidden;background-color:var(--ground)}
button,a{font:inherit}
a{color:inherit}
:focus-visible{outline:2px solid var(--brass);outline-offset:4px;box-shadow:0 0 0 2px var(--ground)}

.skip{position:fixed;left:-999px;top:1rem;z-index:6;display:inline-flex;align-items:center;min-height:44px;padding:.75rem 1rem;border-radius:99px;background:var(--ink);color:var(--ground);font:500 .75rem var(--mono);text-decoration:none}
.skip:focus{left:1rem}

/* ---------- controls ---------- */
.controls{position:fixed;z-index:5;left:var(--pad-x);right:var(--pad-r);top:max(1rem,env(safe-area-inset-top));display:grid;grid-template-columns:1fr auto auto;align-items:center;gap:clamp(.8rem,2vw,1.6rem);pointer-events:none}
.controls>*{pointer-events:auto}
/* H16 (2026-09-19): on a computer the header floats over the copy column, and the copy ran on under the pills, cut
   mid-word. The column now fades into the ground above the reading area, under the pills, so a line leaves before it
   reaches them. The phone's band already sits above its copy. */
html:is([data-comp="desktop"],[data-comp="tablet"]) .controls::before{content:"";position:fixed;z-index:-1;left:0;top:0;width:calc(var(--copy-r,calc(var(--pad-x) + var(--measure))) + 1.6rem);height:calc(max(1rem,env(safe-area-inset-top)) + 3.4rem + 1.9rem);background:linear-gradient(var(--ground) 0,var(--ground) calc(max(1rem,env(safe-area-inset-top)) + 3.4rem),transparent);pointer-events:none}
.prototype-mark{display:inline-flex;align-items:center;gap:.65rem;width:max-content;min-height:44px;padding:.5rem 1rem;border:1px solid var(--line);border-radius:99px;background:var(--panel);font-family:var(--mono);text-transform:uppercase;letter-spacing:.12em;text-decoration:none}
.prototype-mark span{font-size:.7rem;color:var(--quiet)}
.prototype-mark strong{font-size:.72rem;font-weight:500;color:var(--ink)}
.controls nav{display:flex;gap:.1rem;padding:.25rem;border:1px solid var(--line);border-radius:99px;background:var(--panel)}
.controls nav a,#motion-toggle{min-height:44px;display:inline-flex;align-items:center;justify-content:center;padding:.55rem .75rem;border:0;border-radius:99px;background:transparent;color:var(--ink);font:500 .7rem var(--mono);letter-spacing:.06em;text-decoration:none;text-transform:uppercase;cursor:pointer}
.controls nav a:hover{text-decoration:underline;text-underline-offset:.3em}
.controls nav a[aria-current="true"]{color:var(--brass)}
#motion-toggle{min-width:13em;border:1px solid var(--line);background:var(--panel);white-space:nowrap}
#motion-toggle:hover{border-color:var(--brass)}
@media (min-aspect-ratio:1/1) and (max-width:1320px){.prototype-mark span{display:none}}
@media (max-width:1180px){.controls nav a{padding:.55rem .5rem;letter-spacing:.02em}}

/* ---------- the stage ---------- */
#journey{display:none;pointer-events:none}
.js #journey{display:block;position:fixed;z-index:1;top:0;right:0;width:100%;height:100svh}
.no-gl #journey{display:none}
#scrim{display:none}
#pins{display:none}
.js #pins{display:block;position:fixed;z-index:1;top:0;right:0;width:100%;height:100svh;pointer-events:none;overflow:hidden}
#pins span{position:absolute;left:0;top:0;margin:-.55rem 0 0 0;font:500 11px var(--mono);line-height:14px;letter-spacing:.08em;text-transform:uppercase;color:var(--quiet);white-space:nowrap;visibility:hidden;will-change:transform;transition:color 400ms linear}
#pins span.visit{color:var(--camel)}
#pins span.here{color:var(--ink)}
#readout{display:none}
.js #readout{display:flex;position:fixed;z-index:2;gap:.6rem;left:calc(100% - var(--stage-w) + 1rem);bottom:1rem;font:500 11px/1.5 var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--quiet);opacity:0;pointer-events:none;white-space:nowrap;transition:opacity 240ms linear}
#readout.on{opacity:1}
#readout .ro-ch{color:var(--brass)}
.credit{display:none}
.js .credit{display:block;position:fixed;z-index:2;right:1rem;bottom:1rem;margin:0;font:500 10px/1.4 var(--mono);letter-spacing:.06em;color:var(--quiet);opacity:0;pointer-events:none;transition:opacity 400ms linear}
html[data-city] .credit{opacity:.85}
.no-gl .credit{display:none}

/* ---------- story ---------- */
main{position:relative}
section{position:relative;padding:4.5rem var(--pad-r) 3rem var(--pad-x)}
.wrap{position:relative;z-index:2;width:min(100%,var(--measure))}
.eyebrow{display:flex;gap:.6rem;flex-wrap:wrap;margin:0 0 1.4rem;font:500 .72rem/1.5 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--quiet)}
h1,h2{margin:0 0 1.4rem;font-family:var(--display);font-weight:700;line-height:1.02;letter-spacing:-.025em;color:var(--ink);text-wrap:balance}
/* D104: the page was shipping two type systems. assets/story-headings.css - a review-only refinement written for the
   old site - was linked after this stylesheet and its #story selectors outrank these, so every heading size here was
   dead and the sizes he has been reading came from the other file. Its values are folded in verbatim (checked computed
   size, leading and tracking at 1440, 1280, 402 and 320: identical), the second file is no longer linked, and the page
   now has one place where its type is decided. */
h1{font-size:clamp(2.8rem,6vw,5.4rem)}
h2{font-size:clamp(2.1rem,3.6vw,3.4rem)}
#story h1{font-size:clamp(2.8rem,5vw,5.25rem)}
#story h2,#now h2{font-size:clamp(2rem,3.5vw,3.75rem);line-height:1.02;letter-spacing:-.03em}
#story .story-turn{font-size:clamp(2.25rem,4.25vw,4.25rem);line-height:1}
@media (max-aspect-ratio:1/1){
  #story h1{font-size:clamp(2.45rem,10vw,4rem)}
  #story h2,#now h2{font-size:clamp(2rem,8vw,3.35rem)}
  #story .story-turn{font-size:clamp(2.15rem,9vw,3.5rem)}
}
p{margin:0 0 1.1rem}
/* D107: measured on the built page, ten paragraphs at 402 x 874 and three at 320 x 568 ended on a last line under
   a seventh of the measure - "it.", "a lot." alone on a line. text-wrap: pretty asks the engine to avoid them; where
   it is not supported the paragraph sets exactly as it does today. Nothing else about the type changes. */
.wrap p,#now .thanks li{text-wrap:pretty}
.lede{font-size:1.14em;line-height:1.52;color:var(--ink)}
em{font-style:italic}
.story-beat{margin-block:2.2rem;padding-left:1rem;border-left:2px solid var(--brass);font-size:clamp(1.3rem,2.1vw,1.8rem);line-height:1.4;color:var(--ink)}
.story-turn{margin-block:2.2rem;font:700 clamp(2.2rem,4.6vw,4rem)/1 var(--display);letter-spacing:-.03em;color:var(--rust)}
#story .story-turn.long{font-size:clamp(1.55rem,2.5vw,2.2rem);line-height:1.12;letter-spacing:-.02em}
/* D103: a litany - the four sentences he wrote at seventeen, and the five that answer them - keeps one sentence to a
   line. Measured on the built page, it did not: the finale ran 6 visual lines for 5 sentences at 1440 x 900 and 9 at
   320 x 568, because it was set at the page's heading scale (3.4 rem) and "Settled for North Carolina." is 12.6 times
   the font size wide against a 491 px measure. These sizes are fitted to that longest sentence, so the form holds on
   every geometry; verify.js asserts one visual line per sentence, which is what catches it if the words ever move. */
#story h2.lines{font-size:min(2.2rem,calc((100vw - 56px) * .076));line-height:1.08;letter-spacing:-.02em}
.story-beat.lines{font-size:min(clamp(1.3rem,2.1vw,1.8rem),calc((100vw - 74px) * .081))}
/* The viewport is only a proxy for the measure, and on the tablet composition it is a bad one: the stage takes 42 % of
   the width, so at 820 x 960 the copy column is 379 px and the litany wrapped again. The copy card is the container the
   line actually has to fit, so ask it. 7.6 cqw is the longest sentence's own ratio with a 4 % margin; the story-beat
   also pays for its brass rule and its indent, hence the 18 px. */
@supports (container-type: inline-size){
  .wrap{container-type:inline-size}
  #story h2.lines{font-size:min(2.2rem,7.6cqw)}
  .story-beat.lines{font-size:min(clamp(1.3rem,2.1vw,1.8rem),calc((100cqw - 18px) / 12.8))}
}
/* D027: each town's colour, used lightly: its door, the copy column's rule, a mark on its eyebrows (and its leg on the map) */
section[data-tone="pine"]{--ch:var(--pine)}section[data-tone="teal"]{--ch:var(--teal)}section[data-tone="oxblood"]{--ch:var(--oxblood)}
section[data-tone="olive"]{--ch:var(--olive)}section[data-tone="ochre"]{--ch:var(--ochre)}section[data-tone="rust"]{--ch:var(--rust)}
section[data-tone]{--ch-lift:color-mix(in srgb,var(--ch) 60%,var(--ink))}
.door{display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;margin:0 0 1.8rem;padding:.95rem 1.1rem;border-radius:12px;border-left:3px solid var(--ch);background:color-mix(in srgb,var(--ch) 34%,var(--panel))}
.door-t{display:flex;flex-direction:column;gap:.3rem;margin:0}
.door-e{font:500 .72rem/1.5 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--body-ink)}
.door-town{font:700 clamp(1.2rem,1.8vw,1.45rem)/1.1 var(--display);letter-spacing:-.01em;color:var(--ink)}
/* the script's reason for each move, under the chapter's title: "Because a hurricane." */
.door-why{margin-top:.15rem;font:italic .95rem/1.4 var(--body);color:var(--body-ink)}
.door-n{flex:none;font:700 clamp(2.6rem,4.4vw,3.6rem)/.78 var(--display);letter-spacing:-.04em;color:var(--ch-lift)}
section[data-tone] .wrap>.eyebrow::before{content:"";flex:none;align-self:center;width:.55rem;height:.55rem;border-radius:2px;background:var(--ch-lift)}
/* a lesser event set as an aside (a visit; a brief move could be one too): a quiet aside in the copy (camel, dotted),
   lighter than a story-beat */
.visit,.brief{margin:2.2rem 0;padding:.2rem 0 .2rem 1rem;border-left:2px dotted var(--camel)}
.visit .eyebrow,.brief .eyebrow{margin-bottom:.6rem;color:var(--camel)}
.visit p:last-child,.brief p:last-child{margin:0}
/* D028: a brief move, heavier than a visit, lighter than a chapter: its own section, no door, no number; its eyebrow's
   mark is a ring in the chapter's colour (the loop out and back) where a section's is a square */
section[data-st="away"] .wrap>.eyebrow::before{background:none;border:1.5px solid var(--ch-lift);border-radius:50%;width:.5rem;height:.5rem}
.trace-link{display:grid;grid-template-columns:auto 1fr;gap:.4rem 1rem;width:100%;min-height:56px;margin-top:2rem;padding:1rem 1.1rem;border:1px solid var(--line);border-radius:12px;background:var(--panel);color:var(--ink);text-align:left;cursor:pointer}
.trace-link span{grid-row:1 / span 2;align-self:center;font:500 .7rem var(--mono);letter-spacing:.13em;text-transform:uppercase;color:var(--quiet)}
.trace-link:hover{background:color-mix(in srgb,var(--brass) 10%,var(--panel));border-color:var(--brass)}

/* prints (D032): the photograph itself, no border, the brand's 12 px corners (S2) and a cream hairline at 16 %. In flow
   without JS; fixed on the stage with it (the engine places the figure on its rect and draws the flying card to match). */
.print{position:relative;display:block;width:min(100%,30rem);margin:0 0 2.4rem;border-radius:12px;box-shadow:0 0 0 1px rgba(236,227,208,.16)}
.print picture{display:block}
.print img{display:block;width:100%;height:auto;aspect-ratio:var(--ar,4/3);object-fit:cover;background:var(--panel);border-radius:12px}
html:not(.js) .print,.no-gl .print{width:fit-content!important;max-width:100%}
html:not(.js) .print img,.no-gl .print img{width:auto;max-width:100%;max-height:min(34rem,70svh)}
.js .print{position:fixed;z-index:3;margin:0;opacity:0;pointer-events:none;box-shadow:0 0 0 1px rgba(236,227,208,.16),0 1px 2px rgba(0,0,0,.35),0 14px 30px -18px rgba(0,0,0,.6)}
/* D084: the frame keeps its size even if its photograph never arrives. With the engine sizing the figure and the image
   left at aspect-ratio, a failed request let the broken-image box grow to fit its alt text - 150 px where every other
   print was 126 - and the one-height rule (D068) quietly broke on a dropped asset. The engine sets the figure's height
   now and the image fills it, so a lost photograph costs the photograph, not the layout. */
.js .print picture,.js .print img{height:100%}
.js .print img{overflow:hidden}
/* review switches for the frame (B4 v4 §3.5; D032 chose soft): print, the Phase 1c cream paper border; mat, a thin
   espresso mat. The engine draws the flying card to match. */
html[data-frame="print"] .print{padding:9px;background:var(--sheet);border-radius:0;box-shadow:0 1px 2px rgba(0,0,0,.35),0 14px 30px -18px rgba(0,0,0,.6)}
html[data-frame="print"] .print img{border-radius:0}
html[data-frame="mat"] .print{padding:9px;background:var(--panel);box-shadow:inset 0 0 0 1px var(--line),0 1px 2px rgba(0,0,0,.35),0 14px 30px -18px rgba(0,0,0,.6)}
html[data-frame="mat"] .print img{border-radius:4px}
/* ?bare=frame (his call on Louisiana): an empty frame on the sheet where a move lands on no photograph; the engine places it */
.bare-frame{position:fixed;z-index:3;left:0;top:0;border-radius:12px;background:var(--panel);box-shadow:0 0 0 1px rgba(236,227,208,.16),0 14px 30px -18px rgba(0,0,0,.6);opacity:0;pointer-events:none}
html:not(.js) .bare-frame,.no-gl .bare-frame{display:none}
html[data-comp="portrait"] .bare-frame{z-index:4}
.no-gl .print{position:relative;opacity:1;margin:0 0 2.4rem;left:auto!important;top:auto!important;width:min(100%,30rem)!important}
/* an image that is not a photograph says so on the image itself (A040; library boundary ai-images) */
.ai-tag{position:absolute;left:.6rem;top:.6rem;z-index:1;padding:.3rem .6rem;border-radius:99px;background:color-mix(in srgb,var(--ground) 88%,transparent);box-shadow:0 0 0 1px rgba(236,227,208,.22);font:500 .75rem/1.3 var(--mono);letter-spacing:.02em;color:var(--ink)}

/* captions (X_photographs.md). On a page with no camera move the caption, not the image, makes the claim (Morris over
   Burns), so every photograph but §20's two has one in the model. His call (2026-09-19): the page carries none, "more room
   for fun" (data-cap="off"); his words beside each photograph already say what it is. When the model turns them on, where
   they live is html[data-cap]
   (review switch ?cap=split|words|stage). split: under the photograph on the stage on a computer, in the words on the phone.
   words: in the words everywhere. stage: under the photograph everywhere (the phone's band rests taller to hold it). In the
   words a caption follows the line its photograph arrives on; on the stage it comes up once its photograph is most of the
   way in (the engine's .cap-on), so two captions never cross in a fade. Without JavaScript it sits under the photograph. */
.cap{margin:-.4rem 0 1.7rem;padding:.05rem 0 .05rem .8rem;border-left:1px solid color-mix(in srgb,var(--ch,var(--brass)) 55%,var(--line));font:.8rem/1.55 var(--body);color:var(--quiet)}
.cap-ai{font:500 .64rem/1.5 var(--mono);letter-spacing:.1em;text-transform:uppercase;color:var(--ink)}
.cap-stage{display:block;margin:.7rem 0 0;font:.8rem/1.55 var(--body);color:var(--quiet)}
.js .cap-stage{position:absolute;left:-.6rem;right:-.6rem;top:calc(100% + .45rem);padding:.4rem .6rem .45rem;border-radius:8px;background:color-mix(in srgb,var(--ground) 84%,transparent);color:color-mix(in srgb,var(--quiet) 70%,var(--ink));opacity:0;transition:opacity 360ms linear}
.js .print.cap-on .cap-stage{opacity:1}
.visit .cap,.brief .cap{margin:.9rem 0 0}
html:not(.js) .cap-words,.no-gl .cap-words{display:none}
html.js:not(.no-gl)[data-cap="words"] .cap-stage,html.js:not(.no-gl)[data-cap="split"][data-comp="portrait"] .cap-stage{display:none}
html.js:not(.no-gl)[data-cap="stage"] .cap-words,html.js:not(.no-gl)[data-cap="split"]:not([data-comp="portrait"]) .cap-words{display:none}
html[data-cap="stage"][data-comp="portrait"]{--band-rest:48svh}   /* review switch ?cap=stage only */
html[data-comp="portrait"] .cap-stage{left:50%;right:auto;width:calc(100vw - 1.4rem);translate:-50% 0;top:calc(100% + .3rem);padding:.3rem .5rem;font-size:.72rem;line-height:1.42;background:color-mix(in srgb,var(--ground) 88%,transparent)}
html[data-comp="portrait"] .ai-tag{font-size:.7rem;left:.4rem;top:.4rem;padding:.25rem .5rem}
html[data-motion="reduced"] .cap-stage,html[data-motion="paused"] .cap-stage{transition:none}
.no-gl .cap-stage{position:static;opacity:1}

/* the story's class per section (T_story_form_contract.md §2; data-class from the model). A, the photograph carries it: air
   before each photograph's line, so it is looked at before it is read about (the engine also brings the city to rest and gives
   the photograph more of the stage: a larger sheet, the taller band on the phone). B, the words carry it: the engine changes a
   photograph only once the reader is at its line. C, the form carries it alone: the section starts lower, and its words wait
   until the move into it has landed (.hold, set by the engine while it plays). */
html[data-comp="desktop"] section:is([data-class="a"],[data-class="a-b"]) .wrap>[data-st="photo"],html[data-comp="tablet"] section:is([data-class="a"],[data-class="a-b"]) .wrap>[data-st="photo"]{margin-top:14svh}
/* D101: the air a photograph station buys its paragraph (D089) existed only on a computer. On the phone the same station
   had 2.4 rem - 0.04 of a viewport against the computer's 0.14 - so a photograph changed in the band while the copy ran
   on underneath at an ordinary paragraph's pace. The phone's copy card cannot take 14 svh: a hole that size inside an
   opaque card is exactly the symptom D089 named. 7 svh is two and a half times an ordinary paragraph gap, which reads as
   a rest rather than a hole, and the photograph is looked at before it is read about here too. */
html[data-comp="portrait"] section:is([data-class="a"],[data-class="a-b"]) .wrap>[data-st="photo"]{margin-top:7svh}
html[data-comp="desktop"] section[data-class="c"],html[data-comp="tablet"] section[data-class="c"]{padding-top:calc(var(--sec-top) + 34svh)}
html[data-comp="portrait"] section[data-class="c"]{padding-top:26svh}
section[data-class="c"] .wrap{transition:opacity 700ms linear}
section[data-class="c"].hold .wrap{opacity:0;transition:none}

/* D100: a continuation section. Four chapters carried more photographs than their own scroll distance could hold - most
   of all Maryland, five stations inside one 270-word section, four of its gaps at or within a tenth of the engine's own
   0.3-viewport floor. The copy is his and is unchanged, so the room comes from structure: the passage breaks, and the
   break is what gives the photograph before it its beat. A continuation is the same passage taking a breath, so it
   re-announces nothing - no heading, no eyebrow, and on the phone no second chapter rule across the top of the card. */
html:is([data-comp="desktop"],[data-comp="tablet"]) :where(#story>section[data-cont]){min-height:min(96svh,880px)}
html[data-comp="portrait"] section[data-tone][data-cont] .wrap{border-top:1px solid var(--line)}

/* the game in §18 (2026-09-19: "sorry to interrupt your scroll but you've earned a game - which image of me is AI?").
   D072, his read: "it could just be more fun and engaging ... there's no explanation. You're just kind of choosing a
   picture of me and then moving on." So the pick is a reveal, not a shrug: until then each card carries a number and
   nothing else; on the pick both name themselves ON the image, each says in one line what it is, and the card picked is
   ringed in brass if it was the generated one and left quiet if it was not. Without JavaScript both arrive labelled.
   Nothing holds the scroll; with motion reduced the reveal is instant. */
.game{margin:2.4rem 0;padding:1.1rem 1.1rem 1rem;border:1px solid var(--line);border-radius:12px;background:var(--panel)}
.game-e{margin:0 0 .3rem;font:italic .95rem/1.45 var(--body);color:var(--body-ink)}
.game-q{margin:0 0 .35rem;font:700 clamp(1.3rem,2vw,1.6rem)/1.15 var(--display);letter-spacing:-.015em;color:var(--ink)}
.game-hint{margin:0 0 1rem;max-height:6rem;overflow:hidden;font:500 .78rem/1.5 var(--mono);letter-spacing:.04em;color:var(--quiet);transition:max-height 260ms ease-out,opacity 180ms linear,margin-bottom 260ms ease-out}
.game.done .game-hint{max-height:0;margin-bottom:.55rem;opacity:0;overflow:hidden;transition:max-height 260ms ease-out,opacity 180ms linear,margin-bottom 260ms ease-out}
.game-picks{display:grid;grid-template-columns:1fr 1fr;gap:.8rem}
.game-pick{display:flex;flex-direction:column;gap:.5rem;padding:0;border:0;background:none;color:var(--ink);text-align:left;cursor:pointer}
.game-frame{position:relative;display:block;width:100%;border-radius:10px;overflow:hidden;box-shadow:0 0 0 1px rgba(236,227,208,.16);transition:box-shadow 200ms linear}
.game-pick picture{display:block;width:100%}
/* both portraits are cut to one framing (D071), so the shape here is theirs, not a second crop */
.game-pick img{display:block;width:100%;height:auto;aspect-ratio:4/5;object-fit:cover;border-radius:10px;background:var(--ground)}
.game-pick:hover .game-frame,.game-pick:focus-visible .game-frame{box-shadow:0 0 0 2px var(--brass)}
/* the number: what a reader points at before there is an answer ("the second one") */
.game-n{position:absolute;left:.5rem;top:.5rem;display:grid;place-items:center;width:1.7rem;height:1.7rem;border-radius:99px;
  background:color-mix(in srgb,var(--ground) 82%,transparent);box-shadow:0 0 0 1px rgba(236,227,208,.22);
  font:500 .82rem/1 var(--mono);color:var(--ink);transition:opacity 220ms linear}
.game.done .game-n{opacity:0}
/* the badge: the answer, on the image, where the eye already is */
.game-badge{position:absolute;left:.5rem;right:.5rem;bottom:.5rem;display:block;padding:.34rem .5rem;border-radius:8px;
  background:color-mix(in srgb,var(--ground) 88%,transparent);box-shadow:0 0 0 1px rgba(236,227,208,.22);
  font:500 .68rem/1.3 var(--mono);letter-spacing:.03em;color:var(--ink);
  opacity:0;transform:translateY(.4rem);transition:opacity 260ms ease-out 80ms,transform 260ms ease-out 80ms}
.game.done .game-badge{opacity:1;transform:none}
.game.done .game-pick[data-ai="true"] .game-badge{color:var(--brass);box-shadow:0 0 0 1px color-mix(in srgb,var(--brass) 55%,transparent)}
.game.done .game-pick[data-ai="true"] .game-frame{box-shadow:0 0 0 2px var(--brass)}
.game.done .game-pick:not([data-ai="true"]) .game-frame{box-shadow:0 0 0 1px rgba(236,227,208,.16)}
.game.done .game-pick.picked .game-frame{box-shadow:0 0 0 3px var(--brass)}
.game.wrong .game-pick.picked .game-frame{box-shadow:0 0 0 2px var(--quiet)}
/* the one line that explains each picture, revealed with the badge */
.game-why{display:block;max-height:0;overflow:hidden;opacity:0;font:400 .82rem/1.45 var(--body);color:var(--body-ink);
  transition:max-height 300ms ease-out,opacity 240ms linear 120ms}
.game.done .game-why{max-height:7rem;opacity:1}
.game-tag{font:500 .72rem/1.3 var(--mono);letter-spacing:.02em;color:var(--quiet)}
.game.live .game-tag{display:none}                       /* the badge on the image carries it once JS is there */
.game.done .game-pick{cursor:default}
.game-out{margin:1rem 0 0;min-height:1.4em;font:700 1.05rem/1.3 var(--display);color:var(--brass)}
.game-after{margin:.35rem 0 0;max-height:0;overflow:hidden;opacity:0;font:400 .88rem/1.5 var(--body);color:var(--quiet);
  transition:max-height 320ms ease-out,opacity 260ms linear 140ms}
.game.done .game-after{max-height:6rem;opacity:1}
html[data-motion="reduced"] :is(.game-frame,.game-badge,.game-why,.game-after,.game-n),
html[data-motion="paused"] :is(.game-frame,.game-badge,.game-why,.game-after,.game-n){transition:none}
/* the phone's paragraph air (.wrap p) is not the game's: its own lines keep their own spacing (H17) */
html[data-comp="portrait"] .wrap .game-e{margin-bottom:.3rem}html[data-comp="portrait"] .wrap .game-q{margin-bottom:.3rem}
html[data-comp="portrait"] .wrap :is(.game-hint,.game-out,.game-after){margin-bottom:0}
html[data-comp="portrait"] .wrap .game-hint{margin-bottom:.9rem}
/* the smallest phone (320 x 568): the card has to fit under the band with nothing cut. The reveal's own lines cost
   nothing before the pick (they are collapsed), so the room is bought back from the eyebrow, the question and the hint. */
@media (max-width:360px){
  .game{padding:.7rem .7rem .6rem}
  .game-e{margin-bottom:.2rem;font-size:.88rem;line-height:1.35}
  .game-q{font-size:1.15rem;margin-bottom:.2rem}
  .game-hint{font-size:.72rem;line-height:1.4}
  .game-picks{gap:.5rem}
  html[data-comp="portrait"] .wrap .game-hint{margin-bottom:.6rem}
  .game-badge{font-size:.6rem;padding:.26rem .4rem;left:.35rem;right:.35rem;bottom:.35rem}   /* two lines, not three, so it covers less of the face */
  .game-out{margin-top:.6rem;font-size:.98rem}
}

/* ---------- footer ---------- */
#now{position:relative;z-index:2;min-height:66svh;padding:clamp(6rem,14vh,10rem) var(--pad-r) clamp(6rem,14vh,10rem) var(--pad-x);border-top:1px solid var(--line);background:var(--ground)}
#now .wrap{position:static}
#now a{display:inline-flex;align-items:center;min-height:44px;color:var(--ink);text-decoration:underline;text-decoration-color:var(--brass);text-decoration-thickness:1px;text-underline-offset:.3em}
/* With thanks (2026-09-19): everyone whose free work the page stands on, before the way back to the top */
#now .thanks{margin:3rem 0 2.2rem;padding:2rem 0 0;border-top:1px solid var(--line)}   /* not a story section: none of their padding (H08) */
#now .thanks h3{margin:0 0 1rem;font:500 .72rem/1.5 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--brass)}
#now .thanks ul{display:grid;gap:.5rem;margin:0 0 1.1rem;padding:0;list-style:none}
#now .thanks li{padding-left:1rem;border-left:1px solid var(--line)}
/* H08 (his call, 2026-09-19: the full-width band): on a computer the thanks and "Read the story again" are one band, edge
   to edge, that rises over the stage after Next steps and holds the last screen; the phone keeps them in the footer's card */
html:is([data-comp="desktop"],[data-comp="tablet"]) #now{padding-bottom:0;border-top-color:transparent}   /* no hairline across the N (H09) */
html:is([data-comp="desktop"],[data-comp="tablet"]) #now .coda{position:relative;z-index:3;display:flex;flex-direction:column;justify-content:center;align-items:flex-start;min-height:100svh;margin:clamp(6rem,14vh,10rem) calc(-1 * (var(--stage-w) + 1rem)) 0 calc(-1 * var(--pad-x));padding:6.5rem var(--pad-r) clamp(4rem,10vh,7rem) var(--pad-x);border-top:1px solid var(--line);background:var(--ground)}
html:is([data-comp="desktop"],[data-comp="tablet"]) #now .thanks{width:min(100%,78rem);margin:0 0 1.6rem;padding:0;border-top:0}
html:is([data-comp="desktop"],[data-comp="tablet"]) #now .thanks>p{max-width:var(--measure)}
html:is([data-comp="desktop"],[data-comp="tablet"]) #now .thanks ul{grid-template-columns:repeat(3,minmax(0,1fr));column-gap:clamp(1.4rem,3vw,3rem);row-gap:1.2rem;margin:1.6rem 0 1.8rem}
html[data-comp="tablet"] #now .thanks ul{grid-template-columns:repeat(2,minmax(0,1fr))}
@media (min-width:1100px){html[data-comp="tablet"] #now .thanks ul{grid-template-columns:repeat(3,minmax(0,1fr))}}
/* The site's consent script (assets/consent.js, on every tracked page) appends <p class="ntc-privacy-choices-wrap"><button> to the
   first <footer>. Left in flow it lands under the coda and pushes the band 47 px off the last screen (H08; found 2026-09-21 on the
   production-shaped page, invisible to the review build). It has a home: inside the band's bottom padding, on the coda's own
   left edge, in the footer's link idiom; on the phone a centred line under the card. 44 px, like every target on the page. */
#now .ntc-privacy-choices-wrap{margin:0;font-size:.82rem;line-height:1.4}
#now .ntc-privacy-choices{display:inline-flex;align-items:center;min-height:44px;padding:0;border:0;background:none;font:inherit;color:var(--ink);text-decoration:underline;text-decoration-color:var(--brass);text-decoration-thickness:1px;text-underline-offset:.3em;cursor:pointer}
html:is([data-comp="desktop"],[data-comp="tablet"]) #now .ntc-privacy-choices-wrap{position:absolute;z-index:4;left:var(--pad-x);bottom:.9rem}
/* the opening's prompt (2026-09-19: "tell them to scroll"): shown only while the globe is up */
#cue{position:fixed;z-index:4;left:calc((var(--copy-r,0px) + 100vw) / 2);bottom:4.4rem;translate:-50% 0;display:flex;flex-direction:column;align-items:center;gap:.6rem;margin:0;font:500 .72rem/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;color:var(--ink);opacity:0;transition:opacity 500ms linear;pointer-events:none}
#cue::after{content:"";width:1px;height:2.2rem;background:linear-gradient(var(--brass),transparent);transform-origin:top;animation:cue 2.4s cubic-bezier(.5,0,.3,1) infinite}
@keyframes cue{0%{transform:scaleY(0);opacity:1}60%{transform:scaleY(1);opacity:1}100%{transform:scaleY(1);opacity:0}}
html[data-at="globe"] #cue{opacity:1}

/* ---------- Trace dialogs ---------- */
.trace{width:min(94vw,70rem);max-height:min(88svh,54rem);margin:auto;padding:0;border:1px solid var(--line);border-radius:14px;background:var(--panel);color:var(--body-ink);box-shadow:0 2rem 6rem rgba(0,0,0,.6);overflow:auto}
.trace::backdrop{background:rgba(0,0,0,.62)}
.trace[open]{animation:trace-in 280ms cubic-bezier(.2,.8,.2,1)}
@keyframes trace-in{from{opacity:0;transform:translateY(1.2rem) scale(.985)}}
.trace-close{position:sticky;z-index:3;top:1rem;float:right;margin:1rem 1rem -4rem 0;min-width:64px;min-height:44px;padding:.65rem .85rem;border:1px solid var(--line);border-radius:99px;background:var(--panel);color:var(--ink);font:500 .72rem var(--mono);letter-spacing:.08em;text-transform:uppercase;cursor:pointer}
.trace-close:hover{border-color:var(--brass)}
.trace-grid{display:grid;grid-template-columns:minmax(0,1.2fr) minmax(18rem,.8fr);min-height:34rem}
.trace-grid figure{position:relative;margin:0;min-height:34rem;background:var(--ground);overflow:hidden}
.trace-grid figure img{display:block;width:100%;height:100%;object-fit:cover}
.trace-grid figcaption{position:absolute;left:1rem;bottom:1rem;padding:.45rem .65rem;background:var(--panel);font:500 .72rem var(--mono);letter-spacing:.04em;color:var(--ink)}
.trace-grid>div{align-self:center;padding:clamp(2rem,5vw,4rem)}
.trace-grid h2{font-size:clamp(2rem,4vw,3.4rem)}
.trace-note{margin-top:2rem;padding-top:1rem;border-top:1px solid var(--line);font:.78rem/1.6 var(--mono);color:var(--quiet)}
.return-note{position:fixed;z-index:7;left:50%;bottom:1.2rem;translate:-50% 1rem;padding:.55rem .8rem;border-radius:99px;background:var(--ink);color:var(--ground);font:.72rem var(--mono);opacity:0;pointer-events:none;transition:opacity 160ms ease,translate 160ms ease}
.return-note.on{opacity:1;translate:-50% 0}

/* ---------- desktop + tablet: the stage full-bleed, the print column right, copy left over the stage ---------- */
html[data-comp="desktop"] :where(#story>section),html[data-comp="tablet"] :where(#story>section){min-height:min(128svh,1120px);padding:var(--sec-top) calc(var(--stage-w) + 1rem) clamp(7rem,18vh,11rem) var(--pad-x)}
html[data-comp="desktop"] .prologue,html[data-comp="tablet"] .prologue{min-height:150svh}
/* the opening has three beats (the world, the country, the first town), so the prologue holds a screen and a half on every composition */
html[data-comp="portrait"] .prologue{min-height:150svh}
/* the scrim: an espresso veil under the copy column, so the city can run full-bleed behind it (B4 §3.3) */
.js #scrim{display:block;position:fixed;z-index:1;top:0;left:0;height:100svh;width:calc(var(--copy-r) + 12rem);pointer-events:none;opacity:0;
  background:linear-gradient(90deg,color-mix(in srgb,var(--ground) 88%,transparent) 0,color-mix(in srgb,var(--ground) 82%,transparent) calc(100% - 16rem),transparent 100%)}
html[data-comp="portrait"] #scrim{display:none}
/* the copy card (B3 §4): transparent at rest, opaque --panel while the stage is on the map (a move) */
html[data-comp="desktop"] .wrap,html[data-comp="tablet"] .wrap{padding:1.4rem clamp(1.2rem,2vw,1.6rem) 1.5rem;border:1px solid transparent;border-left-color:var(--line);border-radius:12px;background-color:transparent;transition:background-color 240ms linear,border-color 240ms linear}
html[data-comp="desktop"] section[data-tone] .wrap,html[data-comp="tablet"] section[data-tone] .wrap{border-left-color:color-mix(in srgb,var(--ch) 70%,var(--line))}
html[data-comp="desktop"][data-stage="travel"] .wrap,html[data-comp="tablet"][data-stage="travel"] .wrap{background-color:var(--panel);border-color:var(--line)}
html[data-comp="desktop"] .prologue .wrap,html[data-comp="tablet"] .prologue .wrap{border-left-color:transparent}
html[data-motion="reduced"] .wrap,html[data-motion="paused"] .wrap{transition:none}
/* the finale: a tall section with its copy held, so the pull-back and the last line have room (B4 §3.6) */
html[data-comp="desktop"] .finale,html[data-comp="tablet"] .finale{min-height:210svh}
html[data-comp="desktop"] .finale .wrap,html[data-comp="tablet"] .finale .wrap{position:sticky;top:30svh;border-left-color:transparent}
html[data-comp="desktop"] #now,html[data-comp="tablet"] #now{padding-right:calc(var(--stage-w) + 1rem);background:transparent}

/* ---------- portrait: the stage is a fixed top band; opaque cards scroll beneath it ---------- */
html[data-comp="portrait"]{scroll-padding-top:calc(var(--band-rest) + 2.6rem)}
html[data-comp="portrait"] #journey{z-index:3;width:100%;height:44svh;left:0;right:auto;background:var(--ground);clip-path:inset(0 0 calc(44svh - var(--band)) 0)}
html[data-comp="portrait"] #pins{z-index:3;width:100%;height:var(--band);left:0;right:auto}
html[data-comp="portrait"] .print{z-index:4}
html[data-comp="portrait"] .credit{display:none}   /* his call (2026-09-19): on the phone the credit is the footer's and With thanks' */
/* D081, his read of version 7: "as you reverse and start scrolling up the page, past sections you've already done, when
   it goes back to another city, there's like a jump and a fade where you can then see the text on the screen again and
   the box that we have is like weird. It kind of like disappears and then jumps right back."
   The cause was a layering error, not a motion one. On the phone the copy scrolls UNDER the band, and the thing painting
   the band opaque was the canvas's own background. Going up across towns the engine dips the canvas to nothing for 180 ms
   to hide the state change - and that dipped the mask with the drawing, so the copy underneath was exposed and then
   covered again. The band now has its own backing plate, which never animates. The canvas is free to fade above it.
   This is SOP section 1: furniture is furniture, and only the stage may move. */
#band-bg{display:none}
html[data-comp="portrait"] #band-bg{display:block;position:fixed;z-index:2;left:0;right:0;top:0;height:var(--band);background:var(--ground)}
#band-line{display:none}
/* D070: on the phone and the small tablet the copy passed UNDER the band and was cut by a hard 1 px edge, so words
   arrived mid-line ("it fades away a bit, and then suddenly the words are there"). The band keeps its rule, and
   under the rule the ground carries on for 2 rem and dissolves, so a paragraph fades in instead of being clipped. */
html[data-comp="portrait"] #band-line{display:block;position:fixed;z-index:3;left:0;right:0;top:var(--band);height:1px;background:var(--line)}
html[data-comp="portrait"] #band-line::after{content:"";position:absolute;left:0;right:0;top:1px;height:2rem;background:linear-gradient(var(--ground) 0,color-mix(in srgb,var(--ground) 55%,transparent) 55%,transparent 100%);pointer-events:none}
html[data-comp="portrait"] #readout{z-index:3;left:0;right:0;bottom:auto;top:var(--band);padding:.45rem 1rem;gap:.5rem;overflow:hidden;text-overflow:ellipsis;background:var(--ground)}
html[data-comp="portrait"] :where(#story>section){padding:14svh .45rem 8svh}
/* D100: on the phone a section has no minimum height, so a passage break is worth exactly its own padding - and the
   room has to sit BEFORE the break, because a continuation's station fires at its top edge. The passage that just ended
   gets the rest, and the photograph it was carrying holds in it. Measured at 402 x 874 this takes the tightest change
   on the page from 0.35 of a viewport to 0.47, which is where the rest of the page sits. */
html[data-comp="portrait"] :where(#story>section:has(+ section[data-cont])){padding-bottom:13svh}
html[data-comp="portrait"] .prologue{padding-top:calc(var(--band-rest) + 2.6rem)}
html[data-comp="portrait"] #cue{left:50%;bottom:auto;top:calc(var(--band) - 1.9rem);flex-direction:row;gap:.7rem}
html[data-comp="portrait"] #cue::after{height:1rem}
html[data-comp="portrait"] .finale{min-height:118svh}
html[data-comp="portrait"] .finale .wrap{position:sticky;top:calc(var(--band) + 3rem);transition:none}
html[data-comp="portrait"] .story-turn{color:var(--rust-card)}
html[data-comp="portrait"] .wrap{z-index:1;margin-inline:auto;padding:1.7rem 1.2rem 1.9rem;background:var(--panel);border:1px solid var(--line);border-radius:12px;box-shadow:0 1.2rem 2.4rem -1.8rem rgba(0,0,0,.7)}
html[data-comp="portrait"] section[data-tone] .wrap{border-top:3px solid var(--ch)}
/* D075, his read: "the final interface is just kind of weird ... taking up a lot of space on the mobile and iPad
   experiences when we're just dealing with symbols, and I feel like we could use our space a lot better". The mark
   stands in the band; Next steps came 18 svh below it, so the closing screen was a symbol, a stretch of nothing, and
   then the invitation. They belong on one screen. */
html[data-comp="portrait"] #now{padding:5svh .45rem 10svh}
html[data-comp="portrait"] #now .wrap{display:flow-root;border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0;padding-bottom:0;box-shadow:none}
html[data-comp="portrait"] #now .coda{display:flow-root;position:relative;z-index:1;width:min(100%,var(--measure));margin-inline:auto;padding:0 1.2rem 1.9rem;background:var(--panel);border:1px solid var(--line);border-top:0;border-radius:0 0 12px 12px;box-shadow:0 1.2rem 2.4rem -1.8rem rgba(0,0,0,.7)}
html[data-comp="portrait"] #now .ntc-privacy-choices-wrap{margin:1.2rem 0 0;text-align:center}
/* reading on the phone (Nathanael, 2026-09-19: "it's going to feel hard to read in a mobile experience … use the spacing on the
   screen and the space we're given"): the cards reach nearer the edge, the words are larger and more open, and every
   paragraph gets a clear breath before the next */
html[data-comp="portrait"] body{font-size:1.09rem;line-height:1.72}
html[data-comp="portrait"] .wrap p{margin-bottom:1.35em}
html[data-comp="portrait"] .lede{font-size:1.16em;line-height:1.55;margin-bottom:1.25em}
html[data-comp="portrait"] .wrap>.eyebrow,html[data-comp="portrait"] .door{margin-bottom:1.5rem}
html[data-comp="portrait"] h2{margin-bottom:1.3rem}
html[data-comp="portrait"] .trace{width:100vw;max-width:none;max-height:92svh;margin:auto 0 0;border-radius:14px 14px 0 0;border-bottom:0}
html[data-comp="portrait"] .trace-grid{display:block;min-height:0}
html[data-comp="portrait"] .trace-grid figure{min-height:0;height:34svh}
html[data-comp="portrait"] .trace-grid>div{padding:2rem 1.35rem 3.5rem}
@media (max-aspect-ratio:1/1){
  .controls{grid-template-columns:1fr auto;gap:.5rem}
  .prototype-mark strong,.controls nav{display:none}
}
@media (max-width:520px){
  body{font-size:1.03rem}
  .controls{left:1rem;right:1rem}
  .eyebrow{font-size:.7rem}
  .trace-link{grid-template-columns:1fr}
  .trace-link span{grid-row:auto}
}
@media (max-width:720px){.trace-grid{display:block;min-height:0}.trace-grid figure{min-height:0;height:34svh}}

/* ---------- reduced motion: a built branch in JS (every state a still); CSS only removes CSS motion ---------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  #cue::after{animation:none}
  .trace[open]{animation:none}
  .return-note{translate:-50% 0;transition:opacity 160ms linear}
}
html[data-motion="reduced"],html[data-motion="paused"]{scroll-behavior:auto}
html[data-motion="reduced"] #cue::after,html[data-motion="paused"] #cue::after{animation:none}
html[data-motion="reduced"] .trace[open],html[data-motion="paused"] .trace[open]{animation:none}
html[data-motion="reduced"] .return-note,html[data-motion="paused"] .return-note{translate:-50% 0;transition:opacity 160ms linear}

@media (prefers-contrast:more){.eyebrow,#readout,.trace-note,#pins span,.credit{color:var(--ink)}}
@media (forced-colors:active){.wrap,.trace-link,.trace{border:1px solid CanvasText}}
