/* Self-hosted webfonts (latin subset) — vendored from @fontsource at build time.
   No third-party font requests at runtime: faster first paint, no external dependency. */
@font-face{font-family:'Archivo';font-style:normal;font-weight:400;font-display:swap;src:url('/assets/fonts/archivo-400-normal.woff2') format('woff2');}
@font-face{font-family:'Archivo';font-style:normal;font-weight:500;font-display:swap;src:url('/assets/fonts/archivo-500-normal.woff2') format('woff2');}
@font-face{font-family:'Archivo';font-style:normal;font-weight:600;font-display:swap;src:url('/assets/fonts/archivo-600-normal.woff2') format('woff2');}
@font-face{font-family:'Archivo';font-style:normal;font-weight:700;font-display:swap;src:url('/assets/fonts/archivo-700-normal.woff2') format('woff2');}
@font-face{font-family:'Archivo';font-style:normal;font-weight:800;font-display:swap;src:url('/assets/fonts/archivo-800-normal.woff2') format('woff2');}
@font-face{font-family:'Archivo';font-style:italic;font-weight:400;font-display:swap;src:url('/assets/fonts/archivo-400-italic.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url('/assets/fonts/ibm-plex-mono-400-normal.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url('/assets/fonts/ibm-plex-mono-500-normal.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;src:url('/assets/fonts/ibm-plex-mono-600-normal.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:700;font-display:swap;src:url('/assets/fonts/ibm-plex-mono-700-normal.woff2') format('woff2');}
@font-face{font-family:'Instrument Serif';font-style:normal;font-weight:400;font-display:swap;src:url('/assets/fonts/instrument-serif-400-normal.woff2') format('woff2');}
@font-face{font-family:'Instrument Serif';font-style:italic;font-weight:400;font-display:swap;src:url('/assets/fonts/instrument-serif-400-italic.woff2') format('woff2');}

/* Pinyon Script — used for exactly one word on this entire site.
   A formal engraver's script, the kind of letterform that turns up on
   invitations and on the inside cover of books. It is not part of the type
   system; it is a deliberate departure from it, for the one name that
   should not be set in the same voice as a case study. */
@font-face{
  font-family:'Pinyon Script';
  font-style:normal;font-weight:400;font-display:swap;
  src:url('/assets/fonts/pinyon-script-400-normal.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

/* ---------------------------------------------------------------------------
   Copyright (c) 2026 James Allen Heifner. All rights reserved.
   Source available, not open source: read it, quote a little of it, learn from
   it. Do not copy it wholesale, deploy it, fold it into a product, or train a
   model on it. Full terms in LICENSE at the repository root.

   It is legible on purpose. If you are deciding whether to hire me, the source
   is the interview.                              thebuilder@allenhbuilds.com
   --------------------------------------------------------------------------- */
/* ============================================================
   allenhbuilds — design system
   Black · charcoal · graphite · warm white · strategic deep red
   Archivo (architectural) / Instrument Serif (editorial) / IBM Plex Mono (technical)
   ============================================================ */

/* ---------- 1. TOKENS ---------- */
:root{
  /* surface */
  --ink:#0b0b0c;
  --ink-2:#0f0f11;
  --panel:#131316;
  --panel-2:#17171b;
  --panel-3:#1c1c21;
  --line:#26262c;
  --line-2:#34343c;

  /* text */
  --paper:#f1f0ee;
  --paper-2:#fafaf8;
  --steel:#9a9aa4;     /* AA on ink */
  --graphite:#888892;  /* quiet text tier — 5.61:1 on ink, 4.84:1 on panel-3 */

  /* accent */
  --red:#d91f26;       /* fills, rules, graphics — white text passes AA on this */
  --red-txt:#ff3b31;   /* red TEXT on dark — passes AA */
  --red-hi:#ff5348;
  --red-wash:rgba(217,31,38,.10);
  --red-line:rgba(217,31,38,.30);
  --ok:#5ad195;

  /* type */
  --sans:'Archivo','Helvetica Neue',Helvetica,Arial,sans-serif;
  --serif:'Instrument Serif',Georgia,'Times New Roman',serif;
  --mono:'IBM Plex Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;

  /* metrics */
  --wrap:1280px;
  --measure:70ch;
  --gut:clamp(20px,5vw,64px);
  --sec:clamp(72px,11vw,168px);
  --r:2px;

  --ease:cubic-bezier(.2,.7,.2,1);

  /* Two aliases that were being used all over the file and defined nowhere.
     --display is the face the wordmark, the orbit place name and the big
     section heads are set in; it has always been Archivo, the same as
     --sans, it just never had a name. --dim is the muted text color used
     on captions and small uppercase labels -- pointed at --steel rather
     than --graphite on purpose, because everything using it is small type
     and small type has to clear AA. */
  --display:'Archivo','Helvetica Neue',Helvetica,Arial,sans-serif;
  --dim:#9a9aa4;
  --ease-io:cubic-bezier(.65,0,.35,1);

  color-scheme:dark;
}

/* ---------- 2. RESET ---------- */
*,*::before,*::after{box-sizing:border-box}
/* Every component below sizes its text in rem, and rem answers to HTML, not to
   body. So the one lever that moves all of it is here. Raising it on wider
   screens scales the whole system together and keeps every proportion intact —
   the alternative is editing forty declarations and getting one of them wrong. */
html{
  -webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:96px;
  font-size:16px;
}
@media (min-width:1024px){ html{font-size:17.5px} }
@media (min-width:1440px){ html{font-size:18.5px} }
@media (min-width:1800px){ html{font-size:19.5px} }
body{
  margin:0;background:var(--ink);color:var(--paper);
  font-family:var(--sans);font-size:1.0625rem;line-height:1.62;
  font-weight:400;letter-spacing:-.005em;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  overflow-x:hidden;
}
img,svg,canvas{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
input{font:inherit;color:inherit}
h1,h2,h3,h4,p,ul,ol,dl,dd,figure,blockquote{margin:0}
ul,ol{padding:0;list-style:none}
::selection{background:var(--red);color:#fff}

:focus-visible{outline:2px solid var(--red-txt);outline-offset:3px;border-radius:var(--r)}
.skip{position:absolute;left:-9999px;top:0;z-index:200;background:var(--red);color:#fff;padding:12px 18px;font:600 12px/1 var(--mono);letter-spacing:.12em}
.skip:focus{left:12px;top:12px}

/* ---------- 3. LAYOUT ---------- */
.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gut)}
.sec{padding-block:var(--sec);position:relative}
.sec + .sec{padding-top:0}
.measure{max-width:var(--measure)}

/* ---------- 4. TYPE SCALE ---------- */
.d1{font-size:clamp(3.2rem,12vw,10.5rem);font-weight:800;line-height:.86;letter-spacing:-.045em}
.d2{font-size:clamp(2rem,5.4vw,4.4rem);font-weight:700;line-height:1.02;letter-spacing:-.035em}
.d3{font-size:clamp(1.5rem,3vw,2.5rem);font-weight:700;line-height:1.12;letter-spacing:-.025em}
.d4{font-size:clamp(1.15rem,1.9vw,1.55rem);font-weight:600;line-height:1.24;letter-spacing:-.018em}

.lede{font-size:clamp(1.2rem,2.15vw,1.7rem);line-height:1.42;letter-spacing:-.02em;font-weight:400;color:var(--paper-2)}
.prose p{margin-block:1.15em;color:#cfcfd6;font-size:1.0625rem;line-height:1.68}
.prose p:first-child{margin-top:0}
.prose b,.prose strong{color:var(--paper-2);font-weight:600}
.prose i,.prose em{font-style:italic}

.serif{font-family:var(--serif);font-weight:400;letter-spacing:-.01em}

/* technical annotation */
.tag{
  font:500 11px/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;
  color:var(--steel);display:flex;align-items:center;gap:14px;margin-bottom:26px;
}
.tag i{font-style:normal;color:var(--red-txt);font-weight:600}
.tag::after{content:"";flex:1;height:1px;background:var(--line);max-width:220px}
.tag--plain::after{display:none}
.mono{font-family:var(--mono);font-size:.78rem;letter-spacing:.02em}
.k{font:600 10.5px/1 var(--mono);letter-spacing:.19em;text-transform:uppercase;color:var(--steel)}
.k--red{color:var(--red-txt)}

/* ---------- 5. HAZARD RULE (brand device) ---------- */
.hazard{
  height:9px;width:100%;
  background:repeating-linear-gradient(115deg,var(--red) 0 12px,#0b0b0c 12px 24px);
  opacity:.9;
}
.rule{height:2px;background:var(--red);width:88px;margin:26px 0}
.hair{height:1px;background:var(--line);width:100%;border:0;margin:0}

/* ============================================================
   6. BOOT SEQUENCE
   ============================================================ */
.boot{
  position:fixed;inset:0;z-index:120;background:#050506;
  display:grid;place-items:center;padding:var(--gut);
  transition:opacity .5s var(--ease),visibility .5s;
}
.boot[hidden]{display:none}
.boot.is-done{opacity:0;visibility:hidden;pointer-events:none}
.boot__in{width:min(760px,100%)}
.boot__meta{font:500 10.5px/1 var(--mono);letter-spacing:.24em;color:var(--graphite);margin-bottom:34px}
.boot__lines{font-family:var(--mono);font-size:clamp(11px,1.45vw,14px);line-height:2.05;color:var(--steel);min-height:10.25em}
.boot__lines b{color:var(--paper);font-weight:500}
.boot__lines .dots{color:var(--line-2)}
.boot__lines .res{color:var(--ok);font-weight:500}
.boot__lines .res--red{color:var(--red-txt)}
.boot__lines li{opacity:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.boot__lines li.on{opacity:1}

.boot__resolve{
  margin-top:40px;font:600 clamp(12px,1.9vw,17px)/1 var(--mono);
  letter-spacing:.3em;color:var(--paper);
  opacity:0;transform:translateY(6px);
}
.boot__resolve.on{opacity:1;transform:none;transition:opacity .7s var(--ease) .05s,transform .7s var(--ease) .05s}
.boot__bar{height:2px;width:0;background:var(--red);margin-top:22px}
.boot__bar.on{width:100%;transition:width .9s var(--ease-io)}
.boot__cta{
  margin-top:34px;opacity:0;
  font:600 11px/1 var(--mono);letter-spacing:.22em;color:var(--paper);
  border:1px solid var(--line-2);padding:15px 22px;display:inline-flex;gap:12px;align-items:center;
  transition:opacity .5s var(--ease),background .2s,border-color .2s;
}
.boot__cta.on{opacity:1}
.boot__cta:hover{background:var(--red);border-color:var(--red);color:#fff}
.boot__skip{
  position:fixed;right:var(--gut);bottom:var(--gut);
  font:500 10px/1 var(--mono);letter-spacing:.2em;color:var(--graphite);
  border:1px solid var(--line);padding:11px 15px;transition:color .2s,border-color .2s;
}
.boot__skip:hover{color:var(--paper);border-color:var(--line-2)}
.boot__scan{position:absolute;inset:0;pointer-events:none;opacity:.35;
  background:repeating-linear-gradient(180deg,rgba(255,255,255,.028) 0 1px,transparent 1px 3px)}

/* ============================================================
   7. HEADER + RAIL
   ============================================================ */
.hdr{position:sticky;top:0;z-index:60;background:rgba(11,11,12,.95);backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
.hdr__in{display:flex;align-items:center;justify-content:space-between;gap:24px;height:64px}
/* The wordmark. It used to be 13px of letterspaced monospace, which is the
   typographic equivalent of mumbling your own name. It is now set in the
   display face at the weight the hero uses, so the header and the H1 read as
   the same voice — and "builds" carries the accent, because that is the word
   doing the work. */
.mark{display:flex;flex-direction:column;gap:4px;line-height:1;
  --mk:var(--red-txt)}
.mark b{position:relative;display:inline-flex;align-items:baseline;align-self:flex-start;
  font:800 23px/1 var(--display);letter-spacing:-.035em;color:var(--paper)}
.mark b i{font-style:normal}
.mark b em{font-style:normal;color:var(--mk);transition:color .22s}
/* A hairline that grows from the left on hover — the same gesture the case
   file links use, so the header is not inventing its own vocabulary. */
.mark b::after{content:'';position:absolute;left:0;bottom:-4px;height:2px;width:0;
  background:var(--red);transition:width .28s cubic-bezier(.2,.7,.3,1)}
.mark:hover b::after{width:100%}
/* "builds" keeps the accent on hover. Turning it white was the one thing
   guaranteed to make the mark go flat at exactly the moment it should not. */
.mark__sub{font:500 8.5px/1 var(--mono);letter-spacing:.26em;color:var(--graphite);
  transition:color .22s}
.mark:hover .mark__sub{color:var(--steel)}
@media (max-width:520px){.mark b{font-size:19px}.mark__sub{letter-spacing:.2em;font-size:8px}}
@media (prefers-reduced-motion:reduce){.mark b::after{transition:none}}
.hdr__k{
  display:inline-flex;align-items:center;gap:8px;margin-left:8px;
  font:500 10px/1 var(--mono);letter-spacing:.14em;color:var(--steel);
  border:1px solid var(--line-2);padding:9px 12px;transition:.18s;
}
.hdr__k:hover{color:var(--paper);border-color:var(--steel)}
.hdr__k kbd{font:inherit;color:var(--paper)}
.hdr__cta{color:var(--paper)!important;border:1px solid var(--red);background:var(--red-wash)}
.hdr__cta:hover{background:var(--red)}
.hdr__prog{position:absolute;left:0;bottom:-1px;height:1px;background:var(--red);width:0}

.rail{
  position:fixed;left:calc(var(--gut) / 2);top:50%;transform:translateY(-50%);z-index:40;
  display:none;flex-direction:column;gap:2px;
}
.rail a{display:flex;align-items:center;gap:10px;padding:5px 4px;color:var(--graphite);transition:color .2s}
.rail a i{font-style:normal;font:500 9px/1 var(--mono);letter-spacing:.1em;width:16px}
.rail a s{text-decoration:none;width:14px;height:1px;background:currentColor;transition:width .25s var(--ease)}
.rail a:hover,.rail a.on{color:var(--red-txt)}
.rail a.on s{width:30px}
@media (min-width:1500px){.rail{display:flex}}
@media (max-width:900px){
  .hdr__k{margin-left:0}
  .mark span{display:none}
  .hdr__k .lg{display:none}
}
.hdr__k .sm{display:none}
@media (max-width:900px){.hdr__k .sm{display:inline}}

/* ============================================================
   8. HERO
   ============================================================ */
.hero{padding-top:clamp(56px,9vw,110px);padding-bottom:clamp(48px,7vw,90px)}
.filemeta{display:flex;flex-wrap:wrap;gap:10px 26px;font:500 10px/1 var(--mono);letter-spacing:.2em;color:var(--graphite);margin-bottom:clamp(28px,5vw,52px)}
.filemeta b{color:var(--steel);font-weight:500}
.filemeta .live{color:var(--ok)}
.filemeta .live::before{content:"";display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--ok);margin-right:7px;vertical-align:1px;animation:pulse 2.4s var(--ease) infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.25}}

.hero__name{margin:0}
.hero__name span{display:block;overflow:hidden}
.hero__name em{display:block;font-style:normal;transform:translateY(102%);animation:rise .9s var(--ease) forwards}
.hero__name span:nth-child(2) em{animation-delay:.08s}
@keyframes rise{to{transform:none}}

.hero__statement{
  font-family:var(--serif);font-weight:400;
  font-size:clamp(1.6rem,3.6vw,3rem);line-height:1.14;letter-spacing:-.02em;
  color:var(--paper-2);max-width:22ch;margin-top:clamp(26px,4vw,44px);
}
.hero__statement u,.hero__statement b{text-decoration:none;color:var(--red-txt);font-weight:400}
.hero__grid{display:grid;grid-template-columns:1fr;gap:clamp(26px,4vw,54px);margin-top:clamp(30px,4.5vw,56px);align-items:start}
@media(min-width:900px){.hero__grid{grid-template-columns:1.05fr .95fr;gap:clamp(40px,6vw,90px)}}
.hero__lede{font-size:clamp(1.02rem,1.5vw,1.2rem);line-height:1.55;color:var(--paper-2);letter-spacing:-.012em}
.hero__lede b{color:var(--red-txt);font-weight:600}

.acts{display:flex;flex-wrap:wrap;gap:10px;margin-top:clamp(30px,4vw,46px)}
.btn{
  display:inline-flex;align-items:center;gap:10px;
  font:600 11px/1 var(--mono);letter-spacing:.16em;
  padding:16px 22px;border:1px solid var(--line-2);color:var(--paper);
  transition:background .2s var(--ease),border-color .2s,color .2s,transform .2s var(--ease);
}
.btn:hover{border-color:var(--steel);transform:translateY(-1px)}
.btn--red{background:var(--red);border-color:var(--red);color:#fff}
.btn--red:hover{background:var(--red-hi);border-color:var(--red-hi)}
.btn--sm{padding:12px 16px;font-size:10px}
.btn--ghost{border-color:transparent;color:var(--steel)}
.btn--ghost:hover{color:var(--paper)}

/* ============================================================
   9. METRICS
   ============================================================ */
.readout{border-block:1px solid var(--line);background:linear-gradient(180deg,var(--ink-2),var(--ink))}
.metrics{display:grid;grid-template-columns:repeat(2,1fr)}
@media(min-width:700px){.metrics{grid-template-columns:repeat(3,1fr)}}
@media(min-width:1080px){.metrics{grid-template-columns:repeat(6,1fr)}}
.metrics li{padding:clamp(24px,3.5vw,40px) clamp(14px,2vw,26px);border-right:1px solid var(--line);border-bottom:1px solid var(--line)}
.metrics li:last-child{border-right:0}
@media(min-width:1080px){.metrics li{border-bottom:0}}
.metrics b{
  display:block;font-size:clamp(2.4rem,4.6vw,3.9rem);font-weight:800;line-height:.92;
  letter-spacing:-.05em;color:var(--paper-2);font-variant-numeric:tabular-nums;
}
.metrics b u{text-decoration:none;color:var(--red-txt)}
/* 10px uppercase mono at .14em under a 66px number was unreadable, and it is
   the half that says what the number MEANS -- a figure with no legible label
   is decoration. Three changes, in order of how much each one bought:
   size 10 -> 12.5, tracking .14em -> .09em (wide tracking is what makes small
   uppercase hard, not the size alone), and steel -> paper-ish so it sits at
   the weight of something meant to be read rather than a caption apologising
   for being there. The footnote goes 9 -> 11 for the same reason. */
.metrics li > span{display:block;margin-top:12px;font:500 12.5px/1.55 var(--mono);
  letter-spacing:.09em;text-transform:uppercase;color:#c8c7cd}
.metrics li > span em{display:block;font-style:normal;margin-top:7px;
  font:400 11px/1.55 var(--mono);letter-spacing:.06em;color:var(--steel);text-transform:none}

.metrics--cs{grid-template-columns:repeat(2,1fr)}
@media(min-width:760px){.metrics--cs{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}}
.metrics--cs li{border-bottom:1px solid var(--line)}
@media(min-width:760px){.metrics--cs li{border-bottom:0}}

/* ============================================================
   10. PANELS / EDITORIAL
   ============================================================ */
.panel{border:1px solid var(--line);background:var(--panel);padding:clamp(24px,4vw,56px)}
.panel--flat{border:0;background:none;padding:0}
.pull{
  font-family:var(--serif);font-size:clamp(1.35rem,2.7vw,2.15rem);line-height:1.22;letter-spacing:-.02em;
  color:var(--paper-2);border-left:2px solid var(--red);padding-left:clamp(18px,2.6vw,32px);
  margin-block:clamp(28px,4vw,46px);max-width:34ch;
}
.quote{
  font-family:var(--serif);font-size:clamp(1.3rem,2.6vw,2rem);line-height:1.24;letter-spacing:-.02em;
  color:var(--paper-2);text-align:center;padding:clamp(28px,4vw,52px) clamp(16px,3vw,40px);
  border-block:1px solid var(--red-line);background:var(--red-wash);margin-block:clamp(28px,4vw,48px);
}
.quote::before,.quote::after{content:'"';color:var(--red-txt)}

.two{display:grid;gap:clamp(24px,3.4vw,48px)}
@media(min-width:820px){.two{grid-template-columns:1fr 1fr}}
.two > div > .k{display:block;margin-bottom:14px}

/* contrast table */
.vs{border-top:1px solid var(--line);margin-top:clamp(28px,4vw,44px)}
.vs__hd{display:none;grid-template-columns:1fr 1fr;gap:clamp(18px,3vw,44px);padding:16px 0 14px}
@media(min-width:820px){.vs__hd{display:grid}}
.vs__row{
  display:grid;grid-template-columns:1fr;gap:8px;
  padding:clamp(18px,2.4vw,26px) 0;border-bottom:1px solid var(--line);
}
@media(min-width:820px){.vs__row{grid-template-columns:1fr 1fr;gap:clamp(18px,3vw,44px);align-items:baseline}}
.vs__row p{margin:0;font-size:.98rem;line-height:1.5}
.vs__them{color:var(--graphite)}
.vs__them::before{content:"— ";color:var(--line-2)}
.vs__me{color:var(--paper-2);font-weight:500;position:relative}
.vs__me::before{content:"→ ";color:var(--red-txt);font-weight:700}
@media(max-width:819px){
  .vs__them::after{content:" · the usual answer";font:500 9px/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--line-2)}
}

.chips{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--line);border:1px solid var(--line);margin-top:clamp(26px,3.5vw,44px)}
@media(min-width:820px){.chips{grid-template-columns:repeat(4,1fr)}}
.chips li{background:var(--panel-2);padding:clamp(18px,2.4vw,28px)}
.chips b{display:block;font-size:clamp(1.15rem,2vw,1.5rem);font-weight:700;letter-spacing:-.025em;color:var(--paper-2)}
.chips span{display:block;margin-top:8px;font-size:.85rem;line-height:1.45;color:var(--steel)}

/* ============================================================
   11. THE LOOP (operating system diagram)
   ============================================================ */
.loop{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);margin-top:clamp(30px,4vw,50px)}
@media(min-width:760px){.loop{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1120px){.loop{grid-template-columns:repeat(4,1fr)}}
.loop li{background:var(--panel);padding:clamp(20px,2.6vw,30px);position:relative;overflow:hidden}
.loop li::after{
  content:"";position:absolute;left:0;top:0;height:2px;width:0;background:var(--red);
  transition:width .7s var(--ease);
}
.loop li.on::after{width:100%}
.loop i{font-style:normal;font:600 10px/1 var(--mono);letter-spacing:.18em;color:var(--red-txt)}
.loop b{display:block;margin-top:16px;font-size:1.06rem;font-weight:600;letter-spacing:-.02em;line-height:1.25;color:var(--paper-2)}
.loop p{margin-top:10px;font-size:.88rem;line-height:1.55;color:var(--steel)}

/* The closing cell. Not step 08 — it is the commentary on the other seven,
   so it drops the number, takes the accent, and terminates the grid on
   purpose instead of leaving a hole where the hairline shows through. */
.loop__end{background:linear-gradient(160deg,rgba(217,31,38,.10),rgba(217,31,38,.03) 60%,transparent)}
.loop__end::after{width:100% !important;background:var(--red)}
.loop__end i{color:var(--red-txt);letter-spacing:.2em}
.loop__end b{color:var(--paper)}
.loop__end p{color:var(--paper-2)}

/* ============================================================
   12. CASE FILES
   ============================================================ */
.filters{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:clamp(24px,3vw,38px)}
.filters button{
  font:500 10px/1 var(--mono);letter-spacing:.15em;text-transform:uppercase;
  padding:11px 15px;border:1px solid var(--line);color:var(--steel);transition:.18s;
}
.filters button:hover{color:var(--paper);border-color:var(--line-2)}
.filters button[aria-pressed="true"]{background:var(--red);border-color:var(--red);color:#fff}

/* ACT MARKERS.
   Ten numbered sections down a column read as ten equal stops. The numbering
   always said HOW MANY and never said what SHAPE, which is the whole of "it's
   just how the sections work together bugs me."

   Nothing new is invented here: the rule is the same hairline used everywhere
   else, the label is the same mono at the same tracking as .tag, and the one
   sentence under it is body copy. It is the cheapest change on the list and it
   does more than any of the moves, because it tells the reader where they are
   in an argument instead of where they are in a list. */
/* padding-block-start, NOT the padding shorthand: .act also carries .wrap,
   and the shorthand zeroes .wrap's padding-inline. The label then sits 64px
   left of every section heading on the page, which is exactly the kind of
   thing that looks like a design choice until you measure it. */
.act{padding-block-start:clamp(40px,5.5vw,72px)}
.act__k{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 14px;margin:0;
  padding-bottom:14px;border-bottom:1px solid var(--line-2);
  font:600 11px/1.5 var(--mono);letter-spacing:.2em;text-transform:uppercase}
.act__k i{font-style:normal;color:var(--red-txt);letter-spacing:.24em}
.act__k span{color:var(--paper-2)}
.act__s{margin:14px 0 0;max-width:62ch;font-size:clamp(14.5px,1.5vw,16.5px);
  line-height:1.6;color:var(--steel)}

/* RANGE, FOLDED INTO THE CAREER RECORD.
   It kept its anchor -- /dossier/#range still lands -- and lost its section
   number, because three sections in a row arguing that twenty-eight years adds
   up is one argument made three times. Needs position:relative of its own now
   that it is no longer a .sec: the bike stratum is absolutely positioned and
   would otherwise hang off the section above it. */
.fold{position:relative;margin-top:clamp(56px,8vw,96px);padding-top:clamp(30px,4vw,44px);
  border-top:1px solid var(--line)}

.files{display:grid;gap:1px;background:var(--line);border:1px solid var(--line)}
@media(min-width:900px){.files{grid-template-columns:1fr 1fr}}
/* CASE FILES -- HIERARCHY, NOT EIGHT THINGS SHOUTING
   Eight cards, and every card was carrying eight typographic treatments at
   roughly equal weight: a red id, a BOXED status chip, a visibility label, a
   title, an org line, a summary, two or three big numbers with labels, and a
   call to action. Nothing was primary, so the eye had nowhere to land and the
   grid read as noise.

   Decide, then: the TITLE and the one-line summary are what you read. The
   numbers are the evidence you check second. Everything else is a file stamp
   and belongs in the background.

   RECESSED, as he put it. The cards were lighter than the page, which floats
   them on top of it -- eight raised objects fighting for the same plane. They
   are darker than the page now, with one hairline of light along the top
   edge, which is how a real recess catches light. Content sits IN the card
   instead of on it, and the grid calms down without losing a single fact. */
.file{
  background:#0e0e11;padding:clamp(22px,3vw,34px);
  display:flex;flex-direction:column;gap:14px;position:relative;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.045);
  transition:background .25s var(--ease);min-height:100%;
}
.file:hover{background:var(--panel)}
.file[hidden]{display:none}
/* One quiet stamp line, not three competing objects. The border came off the
   status: a box makes a thing an OBJECT, and eight bordered chips in a grid
   read as eight buttons you are supposed to press. It is a label. A label is
   a word. State still carries its colour -- green for running, amber for in
   development -- which is the only part of it that was ever information. */
.file__top{display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  font:500 10.5px/1 var(--mono);letter-spacing:.14em}
.file__id{font:inherit;font-weight:600;letter-spacing:.18em;color:var(--red-txt);opacity:.85}
.file__st{font:inherit;padding:0;border:0;color:var(--graphite)}
.st--live{color:var(--ok);border:0}
.st--dev{color:#e8b13a;border:0}
.file__vis{margin-left:auto;font:inherit;font-weight:400;color:var(--graphite)}
.file__ttl{font-size:clamp(1.2rem,2.1vw,1.6rem);font-weight:700;letter-spacing:-.028em;line-height:1.15;color:var(--paper-2)}
.file__sub{font-size:.95rem;line-height:1.5;color:var(--steel)}
.file__org{font:500 10px/1 var(--mono);letter-spacing:.14em;color:var(--graphite);text-transform:uppercase}
/* THE FOLD
   The evidence panel is hinged along its top edge. At rest it hangs half
   open -- far enough that you can see there is something folded down there,
   not so far that you are reading numbers you did not ask for. Lean on the
   card and it drops flat.

   Rotated, NOT collapsed. Height stays reserved the whole time, so nothing
   reflows: expanding a card in a two-column grid on hover would shove the
   row below it down the page, and a layout that moves under the pointer is
   the single most annoying thing an interface can do. The fold is pure
   transform, which the compositor handles and the layout never hears about.

   transform-origin at the top and a perspective on the card together are
   what make it read as a hinge instead of a squash -- without perspective a
   rotateX is just a vertical scale, and the eye knows the difference.

   The panel also sits 7px HIGH at rest, so opening it drops as well as
   flattens. Rotation alone unfolds correctly but reads as the panel growing
   rather than falling -- the bottom edge sweeps out without ever going
   anywhere. Seven pixels of travel is the difference between an animation
   and a hinge with a bit of weight on it. Seven and not more because the
   flex gap above is 14: any further and the folded panel starts crowding the
   summary it sits under.

   The curve carries a small overshoot, same reasoning as the menu cards --
   it lands rather than glides. */
.file{perspective:900px}
.file__m{
  transform-origin:top center;
  transform:translate3d(0,-7px,0) rotateX(58deg);
  opacity:.34;
  /* The cast shadow, and the trick that makes it safe.

     A box-shadow on a 3D-rotated element renders in that element's own space
     and gets foreshortened along with it, which normally looks wrong. Here it
     never does, because the shadow is animated to ZERO at exactly the angle
     where the distortion would show. Folded back: no shadow, nothing to
     distort. Coming flat: the shadow grows in as the panel squares up to the
     card, and lands at full strength at the one moment the panel is parallel
     to the surface it is shadowing. The state where it would look wrong is
     the state where it is not there.

     Two shadows, same reasoning as the elevation scale: a tight one that
     draws the panel's own edge against the card, and a wide soft one that
     puts air under it. */
  box-shadow:0 0 0 rgba(0,0,0,0);
  transition:transform .38s cubic-bezier(.22,.74,.28,1.08),
             opacity .3s var(--ease),
             box-shadow .34s var(--ease)}
.file:hover .file__m,
.file:focus-within .file__m{transform:none;opacity:1;
  background:rgba(255,255,255,.02);
  box-shadow:0 3px 8px rgba(0,0,0,.5), 0 14px 30px -10px rgba(0,0,0,.8)}
@media (prefers-reduced-motion:reduce){
  .file__m{transform:none;opacity:1;transition:none}
}
/* A coarse pointer never hovers, so it never gets the evidence. Phones get
   it flat and open -- the fold is a desktop affordance, not a paywall. */
@media (hover:none){
  .file__m{transform:none;opacity:1}
}

/* Evidence, read second. The numbers keep their weight because they are the
   argument; the labels stop competing with them. 9px was too small to read
   and steel was bright enough to fight -- so up in size and down in contrast,
   which is the trade that makes something legible AND quiet. */
/* The panel is a PLATE, not a rule under the text. Its own edge became
   visible the moment the hover shadow was added, and the numbers were
   sitting hard against that edge -- 300+ touching the left of a box it
   is supposed to be inside. 18px of inset, and the plate pulled back out
   by the same 18 so it still reaches toward the card's padding rather
   than shrinking the evidence into a narrower column. */
.file__m{display:flex;flex-wrap:wrap;gap:10px 22px;margin-top:auto;
  padding:16px 18px 15px;margin-left:-18px;margin-right:-18px;
  border-top:1px solid var(--line)}
.file__m div{display:flex;align-items:baseline;gap:8px}
.file__m b{font-size:1.08rem;font-weight:700;letter-spacing:-.03em;color:var(--paper-2);font-variant-numeric:tabular-nums}
.file__m span{font:500 10.5px/1.35 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--graphite)}
.file__go{display:inline-flex;align-items:center;gap:9px;font:600 10px/1 var(--mono);letter-spacing:.17em;color:var(--steel);margin-top:4px;transition:color .2s var(--ease)}
.file:hover .file__go{color:var(--paper)}
.file__go s{text-decoration:none;width:16px;height:1px;background:var(--red);transition:width .25s var(--ease)}
.file:hover .file__go s{width:30px}
.file__link{position:absolute;inset:0}

.pending{margin-top:clamp(30px,4vw,50px);border:1px dashed var(--line-2);padding:clamp(20px,3vw,32px)}
.pending ul{display:grid;gap:1px;margin-top:20px}
@media(min-width:760px){.pending ul{grid-template-columns:1fr 1fr}}
.pending li{display:flex;align-items:baseline;gap:14px;padding:12px 0;border-bottom:1px solid var(--line);font-size:.92rem;color:var(--steel)}
.pending li i{font-style:normal;font:600 10px/1 var(--mono);letter-spacing:.16em;color:var(--graphite);min-width:58px}
.pending li s{margin-left:auto;text-decoration:none;font:500 9px/1 var(--mono);letter-spacing:.14em;color:var(--graphite);white-space:nowrap}

/* ============================================================
   13. PIPELINE DIAGRAM
   ============================================================ */
.pipe{margin-top:clamp(30px,4vw,50px);border:1px solid var(--line);background:var(--panel);padding:clamp(20px,3vw,40px)}
.pipe__hd{display:flex;flex-wrap:wrap;gap:12px;align-items:baseline;justify-content:space-between;margin-bottom:clamp(24px,3vw,36px)}
.pipe__note{font:400 10px/1.5 var(--mono);letter-spacing:.1em;color:var(--graphite);max-width:44ch}
.pipe__flow{display:grid;gap:0}
.pipe__flow li{position:relative;padding-left:clamp(36px,4vw,54px);padding-bottom:clamp(20px,2.6vw,28px)}
.pipe__flow li:last-child{padding-bottom:0}
.pipe__flow li::before{ /* node */
  content:"";position:absolute;left:0;top:5px;width:11px;height:11px;
  border:1.5px solid var(--line-2);background:var(--ink);border-radius:50%;z-index:2;
  transition:border-color .4s var(--ease),background .4s var(--ease),box-shadow .4s;
}
.pipe__flow li::after{ /* connector */
  content:"";position:absolute;left:5px;top:16px;bottom:-1px;width:1px;background:var(--line);
  transform:scaleY(0);transform-origin:top;transition:transform .5s var(--ease);
}
.pipe__flow li:last-child::after{display:none}
.pipe__flow li.on::before{border-color:var(--red);background:var(--red);box-shadow:0 0 0 4px var(--red-wash)}
.pipe__flow li.on::after{transform:scaleY(1)}
.pipe__flow b{display:block;font-size:1.02rem;font-weight:600;letter-spacing:-.02em;color:var(--paper-2)}
.pipe__flow p{margin-top:6px;font-size:.9rem;line-height:1.55;color:var(--steel);max-width:56ch}
.pipe__flow i{position:absolute;left:0;top:-2px;display:none}
@media(min-width:900px){
  .pipe__flow li{padding-left:clamp(90px,9vw,130px)}
  .pipe__flow li::before{left:54px}
  .pipe__flow li::after{left:59px}
  .pipe__flow i{display:block;font-style:normal;font:600 10px/1.7 var(--mono);letter-spacing:.16em;color:var(--graphite)}
}

/* AI rules */
.rules{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);margin-top:clamp(26px,3.5vw,42px)}
@media(min-width:900px){.rules{grid-template-columns:repeat(3,1fr)}}
.rules li{background:var(--panel-2);padding:clamp(20px,2.6vw,30px);border-top:2px solid var(--line-2)}
.rules li[data-tone="yes"]{border-top-color:var(--red)}
.rules li b{display:block;font-size:1.02rem;font-weight:600;letter-spacing:-.02em;color:var(--paper-2);line-height:1.3}
.rules li span{display:block;margin-top:10px;font-size:.9rem;line-height:1.55;color:var(--steel)}
.rules li em{display:inline-block;font-style:normal;margin-bottom:12px;font:600 9px/1 var(--mono);letter-spacing:.16em;color:var(--graphite)}
.rules li[data-tone="yes"] em{color:var(--red-txt)}

/* ============================================================
   14. BEFORE / AFTER
   ============================================================ */
.ba{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);margin-block:clamp(26px,3.5vw,42px)}
@media(min-width:760px){.ba{grid-template-columns:1fr 1fr}}
.ba > div{background:var(--panel-2);padding:clamp(20px,2.8vw,32px)}
.ba > div:last-child{background:var(--panel-3);position:relative}
.ba > div:last-child::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--red)}
.ba h4{font-size:1.1rem;font-weight:700;letter-spacing:-.025em;color:var(--paper-2);margin-top:12px}
.ba ul{margin-top:16px;display:grid;gap:9px}
.ba li{font-size:.92rem;line-height:1.45;color:var(--steel);padding-left:18px;position:relative}
.ba li::before{content:"";position:absolute;left:0;top:.62em;width:8px;height:1px;background:var(--line-2)}
.ba > div:last-child li::before{background:var(--red)}

/* ============================================================
   15. CAREER TIMELINE
   ============================================================ */
.tl{display:grid;gap:clamp(24px,3vw,40px)}
@media(min-width:1000px){.tl{grid-template-columns:300px 1fr;gap:clamp(32px,4vw,64px);align-items:start}}
.tl__list{border-top:1px solid var(--line)}
.tl__btn{
  display:grid;grid-template-columns:52px 1fr;gap:14px;align-items:baseline;width:100%;text-align:left;
  padding:15px 10px 15px 0;border-bottom:1px solid var(--line);transition:.2s;position:relative;
}
.tl__btn::before{content:"";position:absolute;left:-1px;top:-1px;bottom:-1px;width:2px;background:var(--red);transform:scaleY(0);transition:transform .3s var(--ease)}
.tl__btn:hover{padding-left:8px}
.tl__btn[aria-selected="true"]{padding-left:12px}
.tl__btn[aria-selected="true"]::before{transform:scaleY(1)}
.tl__btn i{font-style:normal;font:600 10px/1 var(--mono);letter-spacing:.13em;color:var(--graphite)}
.tl__btn[aria-selected="true"] i{color:var(--red-txt)}
.tl__btn b{font-size:.94rem;font-weight:600;letter-spacing:-.015em;line-height:1.3;color:var(--steel);display:block}
.tl__btn[aria-selected="true"] b{color:var(--paper-2)}
.tl__btn s{text-decoration:none;display:block;font:400 9.5px/1.5 var(--mono);letter-spacing:.1em;color:var(--graphite);margin-top:4px}

.tl__pane{border:1px solid var(--line);background:var(--panel);padding:clamp(22px,3.4vw,46px);min-height:420px}
.tl__pane[hidden]{display:none}
.tl__hd{display:flex;flex-wrap:wrap;gap:10px 18px;align-items:baseline;margin-bottom:8px}
.tl__pane h3{font-size:clamp(1.35rem,2.6vw,2.05rem);font-weight:700;letter-spacing:-.032em;line-height:1.1;color:var(--paper-2)}
.tl__org{font:500 10.5px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--red-txt)}
.tl__note{font:400 10px/1.5 var(--mono);letter-spacing:.1em;color:var(--graphite);margin-top:6px}
.tl__mission{font-size:1.02rem;line-height:1.55;color:var(--paper-2);margin-top:20px;max-width:62ch}
.tl__grid{display:grid;gap:clamp(20px,2.6vw,32px);margin-top:clamp(24px,3vw,36px)}
@media(min-width:700px){.tl__grid{grid-template-columns:1fr 1fr}}
.tl__grid section > .k{display:block;margin-bottom:12px}
.tl__grid ul{display:grid;gap:8px}
.tl__grid li{font-size:.91rem;line-height:1.5;color:var(--steel);padding-left:16px;position:relative}
.tl__grid li::before{content:"";position:absolute;left:0;top:.62em;width:7px;height:1px;background:var(--line-2)}
.tl__grid section[data-hi] li::before{background:var(--red)}
.tl__grid section[data-hi] li{color:var(--paper-2)}
.tl__tools{display:flex;flex-wrap:wrap;gap:6px;margin-top:14px}
.tl__tools span{font:500 9.5px/1 var(--mono);letter-spacing:.1em;padding:7px 10px;border:1px solid var(--line);color:var(--steel)}
.tl__lesson{margin-top:clamp(24px,3vw,34px);border-top:1px solid var(--line);padding-top:22px;
  font-family:var(--serif);font-size:clamp(1.05rem,1.8vw,1.3rem);line-height:1.35;color:var(--paper-2);max-width:60ch}
.tl__lesson::before{content:"LESSON";display:block;font:600 9.5px/1 var(--mono);letter-spacing:.2em;color:var(--red-txt);margin-bottom:12px;font-family:var(--mono)}
.tl__links{display:flex;flex-wrap:wrap;gap:8px;margin-top:22px}

/* ============================================================
   16. RANGE / STACK / EDUCATION
   ============================================================ */
.cols{display:grid;gap:1px;background:var(--line);border:1px solid var(--line)}
@media(min-width:760px){.cols{grid-template-columns:1fr 1fr}}
@media(min-width:1120px){.cols{grid-template-columns:repeat(4,1fr)}}
.cols > div{background:var(--panel);padding:clamp(20px,2.6vw,30px)}
.cols .k{display:block;color:var(--red-txt);margin-bottom:18px}
.cols li{font-size:.9rem;line-height:1.5;color:var(--steel);padding:9px 0 9px 16px;border-bottom:1px solid var(--line);position:relative}
.cols li:last-child{border-bottom:0}
.cols li::before{content:"";position:absolute;left:0;top:1.05em;width:7px;height:1px;background:var(--red)}

.term{border:1px solid var(--line);background:#0d0d0f;margin-top:clamp(26px,3.5vw,42px)}
.term__bar{display:flex;align-items:center;gap:8px;padding:12px 16px;border-bottom:1px solid var(--line);background:var(--panel)}
.term__bar s{text-decoration:none;width:9px;height:9px;border-radius:50%;background:var(--line-2)}
.term__bar b{margin-left:10px;font:500 10.5px/1 var(--mono);letter-spacing:.09em;color:var(--steel)}
.term__body{padding:clamp(18px,2.6vw,30px);display:grid;gap:20px}
.term__body > div > .k{display:block;color:var(--red-txt);margin-bottom:8px}
.term__body p{font-family:var(--mono);font-size:.83rem;line-height:1.85;color:var(--steel);letter-spacing:.005em}
.term__body em{font-style:normal;color:var(--graphite)}

.edu{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);margin-top:clamp(26px,3.5vw,42px)}
@media(min-width:760px){.edu{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1120px){.edu{grid-template-columns:repeat(4,1fr)}}
.edu > div{background:var(--panel);padding:clamp(20px,2.6vw,28px)}
.edu b{display:block;font-size:1.02rem;font-weight:600;letter-spacing:-.02em;color:var(--paper-2);line-height:1.25}
.edu s{text-decoration:none;display:block;margin-top:6px;font:400 9.5px/1 var(--mono);letter-spacing:.14em;color:var(--graphite);text-transform:uppercase}
.edu i{display:block;font-style:normal;margin-top:14px;font-size:.9rem;line-height:1.45;color:var(--steel)}
.edu em{display:inline-block;font-style:italic;margin-top:10px;font-size:.85rem;color:var(--red-txt);font-family:var(--serif)}

/* ============================================================
   17. ASK THE DOSSIER
   ============================================================ */
.ask{border:1px solid var(--line);background:var(--panel);margin-top:clamp(26px,3.5vw,42px)}
/* WHERE TO TYPE.
   It was a row with a question mark on it: no border, no fill, no outline,
   caret invisible until you had already guessed right and clicked. Nothing
   about it said "field". Now it has a red rule down its left edge at rest --
   the same accent as the ? that was already there, extended into something
   you can aim at -- a faint fill so it reads as a well rather than as the
   panel, and a proper lit state on focus.

   focus-WITHIN, not focus: the input has no border of its own, so the thing
   that should light up is the row around it. */
.ask__in{position:relative;display:flex;align-items:center;gap:14px;
  padding:clamp(16px,2.2vw,22px) clamp(18px,2.4vw,26px);
  border-bottom:1px solid var(--line);
  background:rgba(255,255,255,.018);
  box-shadow:inset 3px 0 0 var(--red);
  transition:background .18s var(--ease),box-shadow .18s var(--ease)}
.ask__in:hover{background:rgba(255,255,255,.03)}
.ask__in:focus-within{
  background:rgba(217,31,38,.055);
  box-shadow:inset 3px 0 0 var(--red), 0 0 0 1px var(--red-line)}
.ask__in:focus-within input::placeholder{color:var(--steel)}
.ask__in span{font:600 11px/1 var(--mono);letter-spacing:.14em;color:var(--red-txt);flex-shrink:0}
.ask__in input{flex:1;background:none;border:0;outline:0;font-size:1.02rem;letter-spacing:-.01em;color:var(--paper-2);min-width:0}
.ask__in input::placeholder{color:var(--graphite)}
.ask__go{font:600 10px/1 var(--mono);letter-spacing:.16em;border:1px solid var(--line-2);padding:11px 14px;color:var(--steel);transition:.2s;flex-shrink:0}
.ask__go:hover{color:#fff;background:var(--red);border-color:var(--red)}
.ask__sugg{display:flex;flex-wrap:wrap;gap:7px;padding:clamp(16px,2.2vw,22px) clamp(18px,2.4vw,26px)}
.ask__sugg button{font:400 11px/1 var(--mono);letter-spacing:.04em;padding:9px 12px;border:1px solid var(--line);color:var(--steel);transition:.18s}
.ask__sugg button:hover{color:var(--paper);border-color:var(--steel)}
.ask__out{padding:0 clamp(18px,2.4vw,26px);max-height:0;overflow:hidden;transition:max-height .4s var(--ease),padding .4s var(--ease)}
.ask__out.on{max-height:1400px;padding-block:clamp(20px,2.6vw,28px);border-top:1px solid var(--line)}
.ask__q{font:500 10px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--graphite);margin-bottom:14px}
.ask__a{font-size:1.02rem;line-height:1.62;color:var(--paper-2);max-width:72ch}
.ask__links{display:flex;flex-wrap:wrap;gap:8px;margin-top:20px}
.ask__links a{font:600 10px/1 var(--mono);letter-spacing:.14em;padding:10px 13px;border:1px solid var(--line-2);color:var(--steel);transition:.18s}
.ask__links a:hover{color:var(--paper);border-color:var(--red)}
.ask__note{margin-top:18px;font:400 10px/1.6 var(--mono);letter-spacing:.06em;color:var(--graphite);max-width:64ch}

/* ============================================================
   18. COMMAND PALETTE
   ============================================================ */
.pal{position:fixed;inset:0;z-index:100;display:none;padding:clamp(16px,10vh,120px) var(--gut) var(--gut)}
.pal.on{display:block}
.pal__scrim{position:absolute;inset:0;background:rgba(5,5,6,.78);backdrop-filter:blur(6px)}
.pal__box{position:relative;width:min(660px,100%);margin-inline:auto;border:1px solid var(--line-2);background:var(--panel);
  box-shadow:0 1px 0 rgba(255,255,255,.05) inset, var(--cast-3)}
.pal__in{display:flex;align-items:center;gap:12px;padding:18px 20px;border-bottom:1px solid var(--line)}
.pal__in span{font:600 11px/1 var(--mono);letter-spacing:.14em;color:var(--red-txt)}
.pal__in input{flex:1;background:none;border:0;outline:0;font-size:1.02rem;color:var(--paper-2);min-width:0}
.pal__in input::placeholder{color:var(--graphite)}
.pal__in kbd{font:500 9px/1 var(--mono);letter-spacing:.1em;color:var(--graphite);border:1px solid var(--line);padding:6px 8px}
.pal__list{max-height:min(52vh,420px);overflow-y:auto}
.pal__list li a,.pal__list li button{
  display:flex;align-items:center;gap:14px;width:100%;text-align:left;
  padding:14px 20px;border-bottom:1px solid var(--line);transition:background .15s;
}
.pal__list li:last-child a,.pal__list li:last-child button{border-bottom:0}
.pal__list li.on a,.pal__list li.on button,.pal__list a:hover,.pal__list button:hover{background:var(--panel-3)}
.pal__list i{font-style:normal;font:600 10px/1 var(--mono);letter-spacing:.14em;color:var(--red-txt);min-width:22px}
.pal__list b{font-size:.96rem;font-weight:500;letter-spacing:-.012em;color:var(--paper-2)}
.pal__list s{text-decoration:none;margin-left:auto;font:400 9px/1 var(--mono);letter-spacing:.14em;color:var(--graphite)}
.pal__empty{padding:26px 20px;font-size:.94rem;color:var(--steel)}
.pal__foot{display:flex;flex-wrap:wrap;gap:16px;padding:13px 20px;border-top:1px solid var(--line);font:400 9px/1 var(--mono);letter-spacing:.12em;color:var(--graphite)}

/* ============================================================
   19. CONTACT / DOORS / FOOTER
   ============================================================ */
.doors{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);margin-top:clamp(28px,3.6vw,46px)}
@media(min-width:900px){.doors{grid-template-columns:1fr 1fr}}
.door{background:var(--panel);padding:clamp(24px,3.4vw,44px);display:flex;flex-direction:column;gap:14px;transition:background .25s var(--ease)}
.door:hover{background:var(--panel-3)}
.door .k{color:var(--red-txt)}
.door h3{font-size:clamp(1.25rem,2.2vw,1.75rem);font-weight:700;letter-spacing:-.03em;line-height:1.14;color:var(--paper-2)}
.door p{font-size:.96rem;line-height:1.55;color:var(--steel)}
.door s{text-decoration:none;margin-top:auto;padding-top:18px;display:inline-flex;align-items:center;gap:10px;font:600 10.5px/1 var(--mono);letter-spacing:.16em;color:var(--paper)}
.door s u{text-decoration:none;width:18px;height:1px;background:var(--red);transition:width .25s var(--ease)}
.door:hover s u{width:34px}

.closer{
  font-family:var(--serif);font-size:clamp(1.3rem,2.6vw,2.05rem);line-height:1.24;letter-spacing:-.02em;
  color:var(--paper-2);max-width:36ch;margin-top:clamp(30px,4vw,50px);
}
.closer b{color:var(--red-txt);font-weight:400}

.ftr{border-top:1px solid var(--line);margin-top:var(--sec)}
.ftr__in{padding-block:clamp(36px,5vw,64px);display:grid;gap:22px}
.ftr__brand{font:800 26px/1 var(--display);letter-spacing:-.035em;color:var(--paper);margin:0}
.ftr__brand i{font-style:normal}
.ftr__brand em{font-style:normal;color:var(--red-txt)}
.ftr__l{display:flex;flex-wrap:wrap;gap:8px 18px;font:400 11px/1.6 var(--mono);letter-spacing:.08em;color:var(--steel)}
.ftr__l a{transition:color .18s;border-bottom:1px solid transparent}
.ftr__l a:hover{color:var(--paper);border-bottom-color:var(--red)}
.ftr__fine{font-size:.8rem;line-height:1.6;color:var(--graphite);max-width:80ch}
.ftr__colophon{font-size:.8rem;line-height:1.6;color:var(--graphite);max-width:80ch;border-top:1px solid var(--line);padding-top:20px}
.ftr__colophon b{color:var(--steel);font-weight:500}

/* ============================================================
   20. CASE STUDY PAGE
   ============================================================ */
.cs__back{display:inline-flex;align-items:center;gap:9px;font:600 12px/1 var(--mono);letter-spacing:.10em;color:#c8c7cd;margin-bottom:clamp(26px,4vw,44px);transition:color .18s}
.cs__back:hover{color:var(--paper)}
.cs__hd{padding-top:clamp(30px,5vw,58px)}
.cs__ttl{font-size:clamp(2.1rem,6vw,4.6rem);font-weight:800;line-height:.98;letter-spacing:-.042em;margin-top:22px;max-width:18ch}
.cs__sub{font-family:var(--serif);font-size:clamp(1.2rem,2.4vw,1.85rem);line-height:1.24;letter-spacing:-.018em;color:var(--steel);margin-top:22px;max-width:38ch}
.cs__body{display:grid;gap:clamp(28px,4vw,64px);margin-top:clamp(34px,5vw,64px)}
@media(min-width:1000px){.cs__body{grid-template-columns:1fr 300px;align-items:start}}
.cs__main > section{margin-bottom:clamp(34px,4.6vw,58px)}
.cs__main > section:last-child{margin-bottom:0}
.cs__main h2{font-size:clamp(1.2rem,2vw,1.6rem);font-weight:700;letter-spacing:-.028em;color:var(--paper-2);margin-bottom:16px}
.cs__main ul{display:grid;gap:10px}
.cs__main ul li{font-size:.96rem;line-height:1.55;color:var(--steel);padding-left:20px;position:relative}
.cs__main ul li::before{content:"";position:absolute;left:0;top:.66em;width:9px;height:1px;background:var(--red)}
.cs__main ol{counter-reset:s;display:grid;gap:12px}
.cs__main ol li{counter-increment:s;font-size:.96rem;line-height:1.55;color:var(--steel);padding-left:34px;position:relative}
.cs__main ol li::before{content:counter(s,decimal-leading-zero);position:absolute;left:0;top:.15em;font:600 10px/1.6 var(--mono);letter-spacing:.12em;color:var(--red-txt)}
.cs__dl{display:grid;gap:1px;background:var(--line);border:1px solid var(--line)}
.cs__dl > div{background:var(--panel);padding:clamp(18px,2.4vw,26px)}
.cs__dl dt{font-size:1rem;font-weight:600;letter-spacing:-.02em;color:var(--paper-2);line-height:1.3}
.cs__dl dd{margin:10px 0 0;font-size:.92rem;line-height:1.55;color:var(--steel)}
.cs__side{position:sticky;top:96px;display:grid;gap:1px;background:var(--line);border:1px solid var(--line)}
.cs__side > div{background:var(--panel);padding:clamp(18px,2.2vw,24px)}
.cs__side .k{display:block;margin-bottom:12px}
.cs__side p,.cs__side li{font-size:.88rem;line-height:1.55;color:var(--steel)}
.cs__tech{display:flex;flex-wrap:wrap;gap:6px}
.cs__tech span{font:500 11.5px/1 var(--mono);letter-spacing:.07em;padding:8px 11px;border:1px solid var(--line);color:#c8c7cd}
.cs__nav{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);margin-top:var(--sec)}
@media(min-width:760px){.cs__nav{grid-template-columns:1fr 1fr}}
.cs__nav a{background:var(--panel);padding:clamp(22px,3vw,34px);transition:background .25s}
.cs__nav a:hover{background:var(--panel-3)}
.cs__nav .k{display:block;margin-bottom:12px}
.cs__nav b{font-size:1.15rem;font-weight:700;letter-spacing:-.025em;color:var(--paper-2);line-height:1.2}

/* 404 */
.nf{min-height:70vh;display:grid;place-items:center;text-align:center;padding-block:var(--sec)}
.nf__code{margin:0;font:800 clamp(5rem,20vw,14rem)/1 var(--sans);letter-spacing:-.06em;color:var(--panel-3)}
.nf p{margin-top:20px;font-size:1.05rem;color:var(--steel);max-width:44ch;margin-inline:auto}

/* ============================================================
   21. UTILITIES / MOTION
   ============================================================ */
.rise{opacity:0;transform:translateY(18px);transition:opacity .7s var(--ease),transform .7s var(--ease)}
.rise.on{opacity:1;transform:none}
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.egg{position:fixed;left:50%;bottom:28px;transform:translateX(-50%) translateY(20px);z-index:110;
  max-width:min(560px,calc(100vw - 32px));background:var(--panel);border:1px solid var(--red-line);
  padding:18px 22px;font-size:.94rem;line-height:1.5;color:var(--paper-2);
  opacity:0;transition:opacity .35s var(--ease),transform .35s var(--ease);pointer-events:none}
.egg.on{opacity:1;transform:translateX(-50%)}
.egg b{color:var(--red-txt);font-weight:600}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
  .hero__name em{transform:none}
  .rise{opacity:1;transform:none}
}

@media print{
  .boot,.hdr,.rail,.pal,.ask,.filters,.egg,.boot__skip{display:none!important}
  body{background:#fff;color:#000}
  .panel,.files,.file,.cols > div{background:#fff;border-color:#ccc}
}

/* ============================================================
   22. THE CLOSE SIMULATOR
   ============================================================ */
.sim{border:1px solid var(--line);background:var(--panel);margin-top:clamp(30px,4vw,50px);overflow:hidden}
.sim__hd{display:flex;flex-wrap:wrap;gap:16px;align-items:center;justify-content:space-between;
  padding:clamp(18px,2.4vw,26px) clamp(18px,2.6vw,32px);border-bottom:1px solid var(--line)}
.sim__state{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap}
.sim__state .k{transition:color .4s var(--ease)}
.sim__stats{display:flex;flex-wrap:wrap;gap:6px 22px;font-family:var(--mono);font-size:.8rem;letter-spacing:.06em;color:var(--steel)}
.sim__stats b{color:var(--paper-2);font-weight:600;font-size:1.05rem;font-variant-numeric:tabular-nums}
/* the figure it replaced, kept beside it. Struck, dimmed, and a size down --
   present enough to compare against, quiet enough that the new number is
   still the one you read first. */
.sim__stats s{margin-right:.55em;color:var(--graphite);font-size:.92rem;
  text-decoration:line-through;text-decoration-thickness:1px;
  font-variant-numeric:tabular-nums}
.sim__stats span{display:inline-flex;align-items:baseline;gap:.42em}

/* What the tile is, said plainly and in every state. It plays itself on the
   way past, so the explanation cannot live in the animation. */
.sim__lede{margin:0;padding:clamp(16px,2vw,22px) clamp(16px,2.4vw,26px) 0;
  max-width:64ch;color:var(--steel);font-size:.94rem;line-height:1.62}
.sim__lede b{color:var(--paper);font-weight:600}
.sim__go{display:inline-flex;align-items:center;gap:10px;font:600 10.5px/1 var(--mono);letter-spacing:.16em;
  padding:14px 18px;border:1px solid var(--red);background:var(--red-wash);color:var(--paper);transition:.2s var(--ease);white-space:nowrap}
.sim__go:hover{background:var(--red);color:#fff}

.sim__stage{position:relative;padding:clamp(22px,3.2vw,40px);min-height:clamp(240px,26vw,300px)}
.sim__grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(26px,1fr));gap:8px;max-width:760px}
.sim__grid i{display:block;aspect-ratio:1;border:1px solid var(--line-2);background:var(--panel-2);
  transition:opacity .45s var(--ease),transform .45s var(--ease),border-color .3s,background .3s}
.sim.is-run .sim__grid i.drop{opacity:0;transform:scale(.2) rotate(-12deg);border-color:var(--red);background:var(--red-wash)}
.sim.is-run .sim__grid i.keep{border-color:var(--red);background:var(--red)}
.sim__grid{transition:opacity .5s var(--ease) .9s,transform .5s var(--ease) .9s}
.sim.is-run .sim__grid{opacity:0;transform:translateY(-14px)}

.sim__after{position:absolute;inset:clamp(22px,3.2vw,40px);display:grid;gap:1px;background:var(--line);
  opacity:0;pointer-events:none;transform:translateY(16px);align-content:stretch}
@media(min-width:760px){.sim__after{grid-template-columns:repeat(4,1fr)}}
.sim.is-run .sim__after{opacity:1;pointer-events:auto;transform:none;
  transition:opacity .55s var(--ease) 1.15s,transform .55s var(--ease) 1.15s}
.sim__after > div{background:var(--panel-2);padding:clamp(16px,2vw,24px);border-top:2px solid var(--red)}
.sim__after b{display:block;font-size:clamp(1.1rem,1.9vw,1.4rem);font-weight:700;letter-spacing:-.025em;color:var(--paper-2)}
.sim__after i{display:block;font-style:normal;font:600 9.5px/1 var(--mono);letter-spacing:.16em;color:var(--red-txt);margin-bottom:12px}
.sim__after p{margin-top:9px;font-size:.86rem;line-height:1.5;color:var(--steel)}
.sim__kicker{padding:0 clamp(18px,2.6vw,32px) clamp(20px,2.6vw,28px);
  font-family:var(--serif);font-size:clamp(1.05rem,1.9vw,1.35rem);line-height:1.35;color:var(--paper-2);max-width:58ch;
  opacity:0;transition:opacity .5s var(--ease) 1.15s}
.sim.is-run .sim__kicker{opacity:1}
.sim__note{padding:0 clamp(18px,2.6vw,32px) clamp(18px,2.4vw,26px);
  font:400 10px/1.65 var(--mono);letter-spacing:.06em;color:var(--graphite);max-width:74ch}

/* ============================================================
   23. RECONCILIATION
   ============================================================ */
.recon{border:1px solid var(--line);background:var(--panel);padding:clamp(22px,3.2vw,40px);margin-top:clamp(30px,4vw,50px)}
.recon__hd{display:flex;flex-wrap:wrap;gap:10px 20px;align-items:baseline;justify-content:space-between;margin-bottom:clamp(20px,2.6vw,30px)}
.recon dl{font-family:var(--mono);font-size:clamp(11px,1.25vw,13.5px);line-height:2.5;max-width:640px}
.recon dl > div{display:flex;align-items:baseline;gap:8px}
.recon dt{color:var(--steel);white-space:nowrap}
.recon .lead-dots{flex:1;border-bottom:1px dotted var(--line-2);transform:translateY(-4px);min-width:20px}
.recon dd{margin:0;color:var(--paper-2);font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.recon__total{margin-top:16px;padding-top:16px;border-top:1px solid var(--line-2);max-width:640px;
  display:flex;align-items:baseline;gap:8px;font-family:var(--mono);font-size:clamp(12px,1.4vw,15px);letter-spacing:.08em}
.recon__total dt{color:var(--paper);font-weight:600}
.recon__total dd{color:var(--ok);font-weight:700}
.recon__ties{display:inline-flex;align-items:center;gap:8px;margin-top:22px;
  font:600 10px/1 var(--mono);letter-spacing:.18em;color:var(--ok);border:1px solid rgba(90,209,149,.35);padding:11px 14px}
.recon__note{margin-top:22px;font-size:.88rem;line-height:1.6;color:var(--steel);max-width:66ch}
.recon__note b{color:var(--paper-2);font-weight:600}

/* ============================================================
   24. FIGURES
   ============================================================ */
.fig{margin:0}
.fig img{width:100%;height:auto;display:block;background:var(--panel-2)}
.fig figcaption{margin-top:14px;font:400 10.5px/1.7 var(--mono);letter-spacing:.09em;color:var(--graphite);max-width:64ch}
.fig figcaption b{color:var(--steel);font-weight:500}

.withfig{display:grid;gap:clamp(24px,3.4vw,48px)}
@media(min-width:860px){.withfig{grid-template-columns:minmax(260px,360px) 1fr;gap:clamp(32px,4.4vw,64px);align-items:start}}
.fig--portrait picture{display:block;position:relative;border-top:2px solid var(--red)}
.fig--portrait img{filter:grayscale(1) contrast(1.06) brightness(.94);transition:filter .6s var(--ease)}
.fig--portrait:hover img{filter:grayscale(0) contrast(1) brightness(1)}
.fig--portrait figcaption{font-size:9.5px;letter-spacing:.18em;text-transform:uppercase}

.fig--wide{margin-top:clamp(28px,3.6vw,46px);border-top:2px solid var(--red)}
.fig--wide img{max-height:min(68vh,600px);object-fit:cover;object-position:center 78%}

/* ============================================================
   25. BUILD GALLERY + CHAPTER BAND
   ============================================================ */
.builds{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);margin-top:clamp(26px,3.4vw,42px)}
@media(min-width:860px){.builds{grid-template-columns:1fr 1fr}}
.builds > figure{margin:0;background:var(--panel);padding:0;display:flex;flex-direction:column}
.builds img{width:100%;height:clamp(200px,26vw,300px);object-fit:cover;display:block;
  filter:saturate(.9) contrast(1.02);transition:filter .5s var(--ease),transform .6s var(--ease)}
/* The 1.5% scale that used to be here is gone. A transform that small is
   under the threshold where the eye reads it as deliberate -- what it
   actually does is force a resample, so the picture shimmers slightly and
   looks like a rendering fault. Either move enough to be seen or do not
   move. The filter change alone carries it. */
.builds > figure:hover img{filter:saturate(1.12) contrast(1.08)}
.builds > figure{overflow:hidden}
.builds figcaption{padding:clamp(16px,2.2vw,24px);font-size:.9rem;line-height:1.55;color:var(--steel);
  border-top:2px solid var(--red);font-family:var(--sans);letter-spacing:0}
.builds figcaption b{color:var(--paper-2);font-weight:600}

.band{position:relative;margin-block:clamp(40px,6vw,80px);overflow:hidden;
  border-block:1px solid var(--line)}
.band img{width:100%;height:clamp(180px,28vw,340px);object-fit:cover;object-position:center 40%;display:block}
/* THE SCRIM.
   It was one horizontal gradient at 96% down the left, blanketing the whole
   frame height so the caption at the BOTTOM could be read. That is a lot of
   veil spent in the top two thirds where there is no text at all - and the
   top two thirds is exactly where the star field lives.

   So it follows the text instead of covering the picture. A much lighter
   horizontal pass for shape, and a bottom-weighted one that does the actual
   legibility work where the caption actually sits.

   Measured on the rendered band rather than guessed: the veil over the sky
   drops from 10.1 to 5.0 mean luminance - HALF - while the caption keeps
   11.9:1 against its background, against 12.1:1 before. And checked against
   the case that matters more, the still photograph the band falls back to
   when the shader cannot start: over a bright underlay the combined alpha is
   .77 where it used to be .80, so the caption holds at 9.1:1. */
.band::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(180deg,rgba(11,11,12,0) 0%,rgba(11,11,12,0) 48%,rgba(11,11,12,.70) 100%),
    linear-gradient(90deg,rgba(11,11,12,.40) 0%,rgba(11,11,12,.24) 34%,rgba(11,11,12,.03) 72%,rgba(11,11,12,.22) 100%)}
.band__cap{position:absolute;inset:auto 0 0 0;z-index:2;
  max-width:var(--wrap);margin-inline:auto;
  padding:clamp(18px,2.6vw,32px) var(--gut);pointer-events:none}
.band__cap span{display:block;max-width:44ch;font-size:clamp(.86rem,1.15vw,.98rem);line-height:1.55;color:#cfcfd6}
.band__cap b{display:block;margin-bottom:10px;font:600 10px/1.5 var(--mono);
  letter-spacing:.2em;text-transform:uppercase;color:var(--red-txt);max-width:40ch}

/* ============================================================
   26. SPLIT-FLAP NUMERALS
   A railway departure board. Trestle is named after a railway
   bridge; the numbers on this page land the same way.
   ============================================================ */
.metrics b .sf,.sf{display:inline-flex;gap:.06em;perspective:340px;font-size:inherit}
.sf__d{
  position:relative;display:inline-block;
  width:.66em;height:1em;line-height:1;
  background:linear-gradient(180deg,#1a1a1f 0 50%,#141418 50% 100%);
  border:1px solid var(--line-2);border-radius:2px;
  font-variant-numeric:tabular-nums;overflow:hidden;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.02);
}
.sf__d::after{ /* the hinge line */
  content:"";position:absolute;left:0;right:0;top:50%;height:1px;
  background:#0b0b0c;z-index:6;transform:translateY(-.5px)
}
/* Each half clips a full-height glyph: the top half shows its top, the
   bottom half shows its bottom. That is how a real flap is cut. */
.sf__h{position:absolute;left:0;right:0;height:50%;overflow:hidden;backface-visibility:hidden}
.sf__h > span{position:absolute;left:0;right:0;height:1em;line-height:1em;text-align:center;color:var(--paper-2);font-size:inherit}
.sf__t{top:0}     .sf__t > span{top:0}
.sf__b{bottom:0}  .sf__b > span{bottom:0}
.sf__lt > span{top:0}
.sf__lb > span{bottom:0}
.sf__lf{z-index:5;transform-style:preserve-3d}
.sf__lt{top:0;transform-origin:bottom center;background:linear-gradient(180deg,#1e1e24,#17171c)}
.sf__lb{bottom:0;transform-origin:top center;background:linear-gradient(180deg,#141418,#101014);transform:rotateX(90deg)}
.sf.is-flip .sf__lt{animation:sfTop var(--sf,110ms) cubic-bezier(.4,0,.7,.4) forwards}
.sf.is-flip .sf__lb{animation:sfBot var(--sf,110ms) cubic-bezier(.3,.6,.6,1) var(--sf,110ms) forwards}
@keyframes sfTop{to{transform:rotateX(-90deg)}}
@keyframes sfBot{to{transform:rotateX(0deg)}}
.sf__d--sep{width:.46em;background:none;border:0;box-shadow:none;overflow:visible;
  display:flex;align-items:center;justify-content:center;color:var(--red-txt);font-weight:700}
.sf__d--sep::after{display:none}
.metrics b .sf{font-size:1em}

/* ============================================================
   27. RAKING LIGHT
   A single moving light source. Panels have an angle; the rule,
   the hazard bar and the panel edges catch it and let it go.
   ============================================================ */
.lit{position:relative;isolation:isolate}
.lit::before{
  content:"";position:absolute;inset:-1px;z-index:-1;pointer-events:none;
  border-radius:inherit;opacity:var(--lit-o,0);
  background:radial-gradient(
    700px circle at var(--lx,50%) var(--ly,50%),
    rgba(255,255,255,.092), rgba(217,31,38,.075) 38%, transparent 66%);
  transition:opacity .5s var(--ease);
}
.lit::after{ /* the specular edge — this is what reads as metal */
  content:"";position:absolute;inset:0;z-index:2;pointer-events:none;
  border-radius:inherit;padding:1px;
  background:radial-gradient(
    380px circle at var(--lx,50%) var(--ly,50%),
    rgba(255,255,255,var(--lit-s,0)), transparent 70%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask-composite:exclude;
}
/* Type lit by the same source. The shadow is driven entirely from fx.js —
   direction, length, blur and opacity all come in as custom properties, so the
   compositor paints it and the main thread stays out of the way. */
.casts{
  text-shadow:
    var(--tsx,0px) var(--tsy,0px) var(--tsb,0px) rgba(0,0,0,var(--tsa,0)),
    calc(var(--tsx,0px) * .42) calc(var(--tsy,0px) * .42)
      calc(var(--tsb,0px) * .45) rgba(0,0,0,calc(var(--tsa,0) * .5));
  transition:text-shadow .12s linear;
}

/* ---------- lens flare ---------- */
.flarable{position:relative;overflow:hidden}
.flare{
  position:absolute;inset:0;z-index:3;pointer-events:none;
  opacity:var(--f-a,0);
  mix-blend-mode:screen;
  background:
    /* the source: a tight core inside a soft bloom */
    radial-gradient(closest-side circle at var(--f-x,50%) var(--f-y,50%),
      rgba(255,246,224,.85), rgba(255,232,190,.30) 26%, transparent 72%)
      0 0/22% 38% no-repeat,
    /* anamorphic streak — the horizontal smear a wide lens gives a point source */
    radial-gradient(closest-side ellipse at var(--f-x,50%) var(--f-y,50%),
      rgba(210,228,255,.42), transparent 70%)
      0 0/150% 5% no-repeat,
    /* ghosts, walking from the source out through the far side. Irregular
       spacing on purpose; even spacing reads as a widget. */
    radial-gradient(closest-side circle at
      calc(var(--f-x,50%) + (50% - var(--f-x,50%)) * 1.42)
      calc(var(--f-y,50%) + (50% - var(--f-y,50%)) * 1.42),
      rgba(255,190,120,.20), transparent 70%) 0 0/9% 15% no-repeat,
    radial-gradient(closest-side circle at
      calc(var(--f-x,50%) + (50% - var(--f-x,50%)) * 1.86)
      calc(var(--f-y,50%) + (50% - var(--f-y,50%)) * 1.86),
      rgba(120,255,205,.13), transparent 70%) 0 0/5% 9% no-repeat,
    radial-gradient(closest-side circle at var(--g-x,50%) var(--g-y,50%),
      rgba(150,190,255,.16), transparent 70%) 0 0/16% 27% no-repeat,
    radial-gradient(closest-side circle at
      calc(var(--g-x,50%) + (var(--g-x,50%) - 50%) * 0.45)
      calc(var(--g-y,50%) + (var(--g-y,50%) - 50%) * 0.45),
      rgba(217,31,38,.16), transparent 70%) 0 0/11% 19% no-repeat;
  transition:opacity .35s var(--ease);
}
@media (prefers-reduced-motion:reduce){ .flare{display:none} }

.hazard{position:relative;overflow:hidden}
.hazard::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(105deg,transparent 35%,rgba(255,255,255,.5) 50%,transparent 65%);
  transform:translateX(var(--haz,-120%));
}

/* ============================================================
   28. THE TRESTLE — procedural canvas behind the hero
   ============================================================ */
.trestle{
  position:absolute;inset:0;z-index:0;
  pointer-events:none;opacity:0;transition:opacity 1.6s var(--ease) .2s;
  -webkit-mask-image:linear-gradient(180deg,transparent 4%,#000 30%,#000 68%,transparent 96%);
  mask-image:linear-gradient(180deg,transparent 4%,#000 30%,#000 68%,transparent 96%);
}
.trestle.on{opacity:1}
.hero{position:relative;z-index:1}
/* :not(.trestle) matters. This rule lifts the hero's content above the
   canvas, but `.hero > *` and `.trestle` carry the SAME specificity and this
   one sits later in the file, so it was also winning against
   `.trestle{position:absolute;inset:0}` — the canvas host went relative,
   lost its inset, and collapsed to the canvas element's default 150px.
   The trestle has been drawing into a 1152x150 letterbox instead of filling
   a 1392px hero. Same failure as the gallery card heights: nothing errors,
   the later rule just quietly wins. */
.hero > *:not(.trestle){position:relative;z-index:2}

@media (prefers-reduced-motion:reduce){
  .sf.is-flip .sf__lt,.sf.is-flip .sf__lb{animation:none}
  .lit::before,.lit::after,.hazard::after{display:none}
  .casts{text-shadow:none}
  .trestle{display:none}
}

/* the restoration elevation is a drawing, not a photograph: show it whole */
.fig--draw img{max-height:none;object-fit:contain;background:var(--ink)}
.fig--draw figcaption{font-family:var(--sans);font-size:.94rem;line-height:1.62;
  letter-spacing:0;max-width:76ch;color:var(--steel);margin-top:20px}
.fig--draw figcaption b{color:var(--paper-2);font-weight:600}

/* ── 29 DOCUMENTS & VERIFICATION ──────────────────────────────────────────
   Sits inside CONTACT. Four résumé cards, then what else is available, then
   how to check any of it. The verification block is the point: a dossier
   that tells you how to audit it is making a different claim than one that
   asks to be believed. */
.docs{margin:clamp(46px,6vw,78px) 0 0;padding-top:clamp(30px,4vw,44px);border-top:1px solid var(--line)}
.docs__hd{display:flex;flex-wrap:wrap;gap:8px 28px;align-items:baseline;margin-bottom:26px}
.docs__note{max-width:56ch;color:var(--graphite);font-size:15px;line-height:1.65}

.docs__grid{list-style:none;margin:0;padding:0;display:grid;gap:12px;
  grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.doc{display:flex;flex-direction:column;gap:6px;padding:20px 20px 18px;
  background:var(--panel);border:1px solid var(--line);transition:border-color .2s,background .2s}
.doc:hover{border-color:var(--steel);background:var(--panel-2,var(--panel))}
.doc .k{color:var(--paper);margin:0}
.doc__v{font:500 12px/1.5 var(--mono);letter-spacing:.06em;color:var(--red-txt)}
.doc__d{color:var(--graphite);font-size:14px;line-height:1.6;margin-bottom:6px}
.doc__get{margin-top:auto;display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline;
  font:600 11px/1.6 var(--mono);letter-spacing:.14em;color:var(--paper);
  text-decoration:none;border-top:1px solid var(--line);padding-top:12px}
.doc__get em{font-style:normal;font-weight:400;letter-spacing:.08em;color:var(--graphite)}
.doc__get:hover{color:var(--red-txt)}
.doc__get:hover em{color:var(--steel)}
.doc--soon .doc__get{color:var(--steel)}
.doc--soon .doc__get:hover{color:var(--red-txt)}
.docs__pending{margin-top:16px;max-width:60ch;color:var(--graphite);font-size:14px;line-height:1.7}

.docs__also,.docs__ver{margin-top:clamp(28px,3.4vw,40px)}
.docs__also dl,.docs__ver dl{margin:14px 0 0;display:grid;gap:2px}
.docs__also dt,.docs__ver dt{font:600 11px/1.7 var(--mono);letter-spacing:.12em;color:var(--paper)}
.docs__also dd,.docs__ver dd{margin:0 0 14px;padding-left:0;max-width:70ch;
  color:var(--graphite);font-size:14.5px;line-height:1.7}
.docs__ver dd a{color:var(--red-txt);text-decoration:none;border-bottom:1px solid transparent;
  font:600 11px/1.7 var(--mono);letter-spacing:.1em;white-space:nowrap}
.docs__ver dd a:hover{border-bottom-color:currentColor}
.docs__ver{padding:clamp(22px,3vw,30px);background:var(--panel);border-left:2px solid var(--red)}
.docs__ver .docs__note{margin-top:6px}

@media print{.docs__grid{grid-template-columns:1fr 1fr}.doc__get{display:none}}

/* ── 30 CONTACT FORM ──────────────────────────────────────────────────────
   The doors above stay mailto links for people who want their own client.
   This is for everyone else. Fields are underlines, not boxes — same reason
   the tables use hairlines: a box around every input turns a short form into
   a wall of rectangles. */
.cform{margin:clamp(40px,5vw,64px) 0 0;padding-top:clamp(28px,3.6vw,40px);border-top:1px solid var(--line)}
.cform__hd{margin-bottom:24px}
.cform__note{max-width:60ch;margin-top:8px;color:var(--graphite);font-size:14.5px;line-height:1.7}
.cform__grid{display:grid;gap:18px 20px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.fld{display:flex;flex-direction:column;gap:7px}
.fld--wide{grid-column:1/-1}
.fld > span{font:600 10px/1.6 var(--mono);letter-spacing:.16em;color:var(--graphite)}
.fld input,.fld select,.fld textarea{
  width:100%;padding:11px 2px;background:transparent;color:var(--paper);
  border:0;border-bottom:1px solid var(--line);border-radius:0;
  font:400 15px/1.5 var(--sans);transition:border-color .18s,background .18s}
.fld textarea{resize:vertical;min-height:120px;line-height:1.65}
.fld select{cursor:pointer}
.fld select option{background:var(--ink);color:var(--paper)}
.fld input:hover,.fld select:hover,.fld textarea:hover{border-bottom-color:var(--steel)}
.fld input:focus,.fld select:focus,.fld textarea:focus{
  outline:none;border-bottom-color:var(--red-txt);background:rgba(255,255,255,.02)}
.fld input::placeholder,.fld textarea::placeholder{color:var(--steel);opacity:1}

/* Off-screen rather than display:none — some bots skip hidden fields but
   happily fill anything they can compute a position for. */
.cform__hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.cform__foot{display:flex;flex-wrap:wrap;gap:16px 22px;align-items:center;margin-top:26px}
.cform__out{margin:0;max-width:56ch;font-size:14px;line-height:1.65;color:var(--graphite)}
.cform__out b{color:var(--paper);font-weight:600}
.cform__out a{color:var(--red-txt);text-decoration:none;border-bottom:1px solid currentColor}
.cform__out.is-ok b{color:var(--red-txt);font-family:var(--mono);font-size:12px;letter-spacing:.08em}
.cform__out.is-warn b{color:var(--red-txt)}
.cform__wait{font:600 11px/1.6 var(--mono);letter-spacing:.2em;color:var(--steel)}
.cform__wait::after{content:'';animation:cfDots 1.2s steps(4,end) infinite}
@keyframes cfDots{0%{content:''}25%{content:'.'}50%{content:'..'}75%{content:'...'}}
.cform.is-sent .cform__grid,.cform.is-sent #cfSend{opacity:.42;pointer-events:none;transition:opacity .3s}
@media (prefers-reduced-motion:reduce){.cform__wait::after{animation:none;content:'...'}}
@media print{.cform{display:none}}

/* ── 31 THE ORBIT ─────────────────────────────────────────────────────────
   Full-bleed, black, and taller than it needs to be, because the last frame
   of the sequence is a horizon and a horizon needs sky above it. The canvas
   draws the planet; everything here just gets out of its way. */
.orbit{position:relative;background:#08080a;overflow:hidden;
  min-height:clamp(460px,74vh,760px);display:flex;align-items:flex-end}
.orbit__stage{position:absolute;inset:0}
.orbit #orbitCv{width:100%;height:100%;display:block}

/* The text sits on the dark half of the frame once the camera has dropped,
   but during the spin it can overlap the globe. A vignette from the bottom
   keeps it legible the whole way through without dimming the planet. */
.orbit__vig{position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(to top,#08080a 2%,rgba(8,8,10,.92) 22%,rgba(8,8,10,.45) 44%,rgba(8,8,10,0) 66%),
    linear-gradient(to bottom,rgba(8,8,10,.85) 0%,rgba(8,8,10,.35) 9%,rgba(8,8,10,0) 20%)}
.orbit__txt{position:relative;z-index:2;width:100%;
  max-width:var(--wrap);margin-inline:auto;
  padding-inline:var(--gut);padding-bottom:clamp(34px,5vw,60px)}
.orbit__tag{position:absolute;top:0;left:0;right:0;z-index:2;
  max-width:var(--wrap);margin-inline:auto;padding:clamp(26px,3.4vw,40px) var(--gut) 0;
  pointer-events:none}
.orbit__tag .tag{margin-bottom:0}
.orbit__place{margin:14px 0 0;font-family:var(--display);font-weight:800;
  font-size:clamp(2.1rem,5.6vw,4.1rem);line-height:.98;letter-spacing:-.035em;
  color:var(--paper);text-wrap:balance}
.orbit__place span::after{content:'';display:inline-block;width:.055em;height:.82em;
  margin-left:.09em;background:var(--red-txt);vertical-align:-.06em;animation:orbCaret 1.05s steps(2) infinite}
@keyframes orbCaret{50%{opacity:0}}
.orbit__sub{margin:18px 0 0;max-width:60ch;color:var(--steel);
  font-size:clamp(15px,1.15vw,17px);line-height:1.72}
.orbit__sub b{color:var(--paper);font-weight:600}
/* The provenance note. Small, gray, and present — a page that reads your
   location and does not say where it got it has made a different promise. */
.orbit__sub em{display:block;margin-top:14px;font-style:normal;
  font:400 11.5px/1.7 var(--mono);letter-spacing:.02em;color:var(--graphite);max-width:56ch}

@media (max-width:640px){
  .orbit{min-height:clamp(420px,68vh,620px)}
  .orbit__sub{font-size:14.5px}
}
@media (prefers-reduced-motion:reduce){
  .orbit__place span::after{animation:none}
}
/* The way back into the sequence. Quiet enough that it reads as an offer
   rather than a control, and it never appears until there is something to
   run again. */
.orbit__again{margin:24px 0 0;padding:0;border:0;background:none;
  display:inline-flex;align-items:center;cursor:pointer;
  font:400 11px/1 var(--mono);letter-spacing:.34em;text-transform:uppercase;
  color:var(--steel);opacity:0;transform:translateY(5px);
  transition:opacity .7s var(--ease),transform .7s var(--ease),color .2s ease}
.orbit__again.is-on{opacity:1;transform:none}
.orbit__again b{margin-right:.85em;font-size:14px;font-weight:400;
  letter-spacing:0;color:var(--red-txt)}
.orbit__again:hover,.orbit__again:focus-visible{color:var(--paper)}
.orbit__again:disabled{cursor:default}
@media(prefers-reduced-motion:reduce){.orbit__again{display:none}}
/* The seam. #orbit is 760px of #08080a and the page is #0b0b0c -- three
   values apart, which is invisible as a colour and unmistakable as a hard
   horizontal line where the block stops. Read as two sections shoved against
   each other, because that is what it was.

   So the scene dissolves into the document instead of ending at an edge.
   z-index 1: over the canvas, under the text, which already sets 2. */
.orbit::after{content:"";position:absolute;inset:auto 0 0 0;height:230px;
  z-index:1;pointer-events:none;
  background:linear-gradient(180deg,rgba(11,11,12,0) 0%,rgba(11,11,12,.28) 34%,
    rgba(11,11,12,.72) 66%,var(--ink) 100%)}
@media print{.orbit{display:none}.orbit__again{display:none}}

/* ── 32 TITLE PROGRESSION + WHY IT ENDED ──────────────────────────────────
   Two titles at one employer belong in one chapter. Split across two rows a
   promotion reads as a job that ended after ten weeks, which is the opposite
   of what happened. */
.tl__titles{list-style:none;margin:16px 0 0;padding:0;display:grid;gap:0}
.tl__titles li{display:flex;flex-wrap:wrap;gap:4px 16px;align-items:baseline;
  padding:9px 0 9px 16px;border-left:2px solid var(--line);position:relative}
.tl__titles li:first-child{border-left-color:var(--red)}
.tl__titles li:first-child b{color:var(--paper)}
.tl__titles li::before{content:'';position:absolute;left:-4px;top:15px;width:6px;height:6px;
  border-radius:50%;background:var(--line)}
.tl__titles li:first-child::before{background:var(--red)}
.tl__titles b{font:600 14.5px/1.5 var(--sans);color:var(--paper-2)}
.tl__titles span{font:400 11px/1.5 var(--mono);letter-spacing:.06em;color:var(--graphite)}

/* A short tenure invites one question. Answer it in the open, in the same
   voice as the rest of the file, rather than letting a date range imply
   something that is not true. */
.tl__depart{margin-top:28px;padding:20px 22px;background:var(--panel);border-left:2px solid var(--red)}
.tl__depart p:last-child{margin:10px 0 0;max-width:70ch;color:var(--steel);
  font-size:15px;line-height:1.72}

/* ── 33 FIELD ACCESS ──────────────────────────────────────────────────────
   Sealed on desktop. Not hidden as an afterthought — a pointer that cannot
   report a tilt angle has nothing to do here, and half a puzzle is worse
   than none. Everything inside reads as an instrument panel: tabular
   figures, hairline rules, one accent, no chrome. */
.field{display:none}
@media (pointer:coarse){.field{display:block}}

.field{padding:clamp(44px,7vw,72px) 0;border-top:1px solid var(--line);background:var(--ink)}
.field__hd{display:flex;flex-wrap:wrap;gap:10px 18px;align-items:baseline;
  padding-bottom:14px;border-bottom:1px solid var(--line)}
.field__state{margin:0;margin-left:auto;font:600 10px/1.6 var(--mono);letter-spacing:.2em;
  color:var(--graphite);padding:3px 9px;border:1px solid var(--line)}
.field__state.is-open{color:var(--red-txt);border-color:var(--red)}
.field__count{margin:0;font:600 11px/1.6 var(--mono);letter-spacing:.14em;color:var(--steel);
  font-variant-numeric:tabular-nums}
.field h2{margin:22px 0 0}
.field__lede{margin:14px 0 0;max-width:56ch;color:var(--graphite);font-size:15px;line-height:1.7}
.field__gate{display:flex;flex-wrap:wrap;gap:12px;margin-top:26px}
.field__msg{margin:14px 0 0;max-width:56ch;color:var(--red-txt);font-size:14px;line-height:1.65}
.field__msg:empty{display:none}

/* The locks. A row is a readout, not a card: number, name, task, live value,
   and a rule underneath that fills as you hold the position. */
.lks{list-style:none;margin:28px 0 0;padding:0;display:grid;gap:2px}
.lk{padding:16px 0 14px;border-bottom:1px solid var(--line);transition:opacity .3s}
.lk__hd{display:grid;grid-template-columns:auto 1fr auto;gap:4px 12px;align-items:baseline}
.lk__no{font:600 11px/1.6 var(--mono);letter-spacing:.1em;color:var(--graphite)}
.lk__name{font:600 12px/1.6 var(--mono);letter-spacing:.16em;color:var(--paper)}
.lk__val{grid-column:3;grid-row:1;font:600 15px/1.4 var(--mono);color:var(--steel);
  font-variant-numeric:tabular-nums;transition:color .2s}
.lk__task{grid-column:2;font:400 14px/1.6 var(--sans);color:var(--steel)}
.lk__bar{position:relative;height:2px;margin-top:12px;background:var(--line);overflow:hidden}
.lk__bar i{display:block;height:100%;width:0;background:var(--steel);
  transition:width .12s linear,background .2s}
.lk__foot{display:flex;gap:16px;align-items:baseline;margin-top:10px}
.lk__hint{flex:1;font:400 12.5px/1.6 var(--sans);color:var(--graphite)}
.lk__skip{font:600 9.5px/1.6 var(--mono);letter-spacing:.16em;color:var(--graphite);
  border:1px solid var(--line);padding:5px 9px;transition:.18s;flex:none}
.lk__skip:hover,.lk__skip:focus-visible{color:var(--paper);border-color:var(--steel)}

.lk.is-live .lk__bar i{background:var(--red)}
.lk.is-live .lk__val{color:var(--red-txt)}
.lk.is-open .lk__bar i{background:var(--red)}
.lk.is-open .lk__name{color:var(--red-txt)}
.lk.is-open .lk__skip{display:none}
.lk.is-open .lk__name::after{content:' ✓';letter-spacing:0}
.lk.is-skipped{opacity:.45}
.lk.is-skipped .lk__name::after{content:' —';letter-spacing:0}

/* The passive readout. No permission, no prompt, no storage — and saying so
   in the panel is the only reason it belongs on the page at all. */
.field__read{margin-top:34px;padding:20px 0 0;border-top:1px solid var(--line)}
.field__read dl{margin:14px 0 0;display:grid;gap:0;
  grid-template-columns:repeat(auto-fit,minmax(215px,1fr))}
.field__read dl > div{display:flex;gap:10px;align-items:baseline;
  padding:7px 0;border-bottom:1px solid var(--line)}
.field__read dt{flex:none;font:500 9.5px/1.7 var(--mono);letter-spacing:.14em;color:var(--graphite)}
.field__read dd{margin:0 0 0 auto;font:600 12px/1.7 var(--mono);color:var(--paper-2);
  font-variant-numeric:tabular-nums;text-align:right}
.field__readnote{margin:16px 0 0;max-width:58ch;font:400 11.5px/1.7 var(--mono);color:var(--graphite)}

/* The payoff. Earned, so it gets the red rule and real type. */
.field__open{margin-top:34px;padding:24px 22px;background:var(--panel);border-left:2px solid var(--red);
  animation:fieldIn .5s cubic-bezier(.2,.7,.3,1) both}
.field__open p{margin:12px 0 0;max-width:60ch;color:var(--paper-2);font-size:15.5px;line-height:1.72}
.field__open p:last-of-type{color:var(--paper);font-weight:500}
.field__open .acts{margin-top:22px}
@keyframes fieldIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){
  .field__open{animation:none}
  .lk__bar i{transition:none}
}
@media print{.field{display:none}}

/* ── 34 THE DIRECT LINE ───────────────────────────────────────────────────
   A panel, not a card. The number it hands back is the only thing on this
   site that is not in the source, so the block should feel like a different
   kind of object from everything around it: darker, ruled in red, closed. */
.line{margin:clamp(40px,5vw,64px) 0 0;padding:clamp(24px,3.2vw,34px);
  background:var(--panel);border:1px solid var(--line);border-left:2px solid var(--red)}
.line__hd{display:flex;flex-wrap:wrap;gap:10px 18px;align-items:baseline;
  padding-bottom:14px;border-bottom:1px solid var(--line)}
.line__state{margin:0 0 0 auto;font:600 9.5px/1.6 var(--mono);letter-spacing:.2em;
  color:var(--graphite);padding:3px 9px;border:1px solid var(--line)}
.line__state.is-open{color:var(--red-txt);border-color:var(--red)}
.line__h{margin:20px 0 0;font-family:var(--display);font-weight:700;
  font-size:clamp(1.25rem,2.3vw,1.7rem);line-height:1.15;letter-spacing:-.025em;color:var(--paper)}
.line__p{margin:14px 0 0;max-width:62ch;color:var(--graphite);font-size:14.5px;line-height:1.7}
.line__ask{margin:18px 0 0;max-width:60ch;color:var(--paper-2);font-size:15px;line-height:1.7}

.line__grid{display:grid;gap:16px 18px;margin-top:22px;
  grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
.line__foot{display:flex;flex-wrap:wrap;gap:14px 22px;align-items:center;margin-top:24px}
/* Said before anyone types, not after. Quietly logging it would be the thing
   the rest of this site spends nine sections arguing against. */
.line__fair{margin:0;flex:1;min-width:260px;max-width:52ch;
  font:400 11.5px/1.7 var(--mono);color:var(--graphite)}
.line__msg{margin:14px 0 0;font-size:14px;line-height:1.6;color:var(--graphite)}
.line__msg:empty{display:none}
.line__msg.is-warn{color:var(--red-txt)}

.line__out{animation:lineIn .45s cubic-bezier(.2,.7,.3,1) both}
.line__num{display:inline-block;margin-top:12px;text-decoration:none;
  font-family:var(--display);font-weight:800;letter-spacing:-.03em;
  font-size:clamp(1.9rem,5vw,3.1rem);line-height:1;color:var(--paper);
  border-bottom:2px solid var(--red);padding-bottom:6px;transition:color .2s}
.line__num:hover{color:var(--red-txt)}
.line__after{margin:16px 0 0;max-width:56ch;color:var(--steel);font-size:14.5px;line-height:1.7}
.line.is-open{border-left-color:var(--red);background:linear-gradient(150deg,rgba(217,31,38,.07),transparent 55%),var(--panel)}
@keyframes lineIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.line__out{animation:none}}
@media print{.line__form{display:none}}

/* ── 35 PHOTOGRAPHS IN MOTION ─────────────────────────────────────────────
   The camera move itself is in fx.js. This is the surface it moves behind:
   a clipping frame, a single light sweep on first arrival, and a grade that
   the browser applies on top of the file so the images sit in the page
   without having to be baked darker on disk. */
.fig, .builds figure{position:relative}
/* The clipping happens on the frame, never on the figure — a figure that
   clips also clips its own caption. */
.fig__frame{position:relative;display:block;overflow:hidden}
.band{position:relative;overflow:hidden}
.fig img, .builds img, .band img{
  display:block;width:100%;height:auto;
  /* A touch more contrast and a fraction less brightness than the file, so a
     photograph graded for a white gallery still sits on a near-black page. */
  filter:saturate(1.06) contrast(1.05) brightness(.97);
  /* .16s, not .5s. A hover response is feedback, and feedback that takes
     half a second reads as the page thinking rather than the page
     answering. Under about 200ms it feels like the image responded to
     you; over it, it feels like it lagged. */
  transition:filter .16s var(--ease)}
.fig:hover img, .builds figure:hover img{filter:saturate(1.14) contrast(1.08) brightness(1.0)}

/* The sweep. One pass, on first arrival, then the element is inert. */
.fig__frame::after, .band::after{
  content:'';position:absolute;inset:0;pointer-events:none;opacity:0;
  background:linear-gradient(105deg,
    transparent 32%,
    rgba(255,255,255,.055) 46%,
    rgba(255,255,255,.115) 50%,
    rgba(255,255,255,.055) 54%,
    transparent 68%);
  transform:translateX(-38%)}
.is-sweep .fig__frame::after, .fig__frame.is-sweep::after, .band.is-sweep::after{
  animation:sweep .85s cubic-bezier(.35,0,.25,1) .12s 1 both}
@keyframes sweep{
  0%{opacity:0;transform:translateX(-38%)}
  18%{opacity:1}
  82%{opacity:1}
  100%{opacity:0;transform:translateX(38%)}}

/* A hairline that only exists while the photograph is on screen — it frames
   the picture without putting a permanent box around it. */
.fig__frame::before{
  content:'';position:absolute;inset:0;pointer-events:none;z-index:2;
  box-shadow:inset 0 0 0 1px rgba(241,240,238,.07)}

@media (prefers-reduced-motion:reduce){
  .fig__frame::after, .band::after{display:none}
  .fig img, .builds img, .band img{transition:none}
}

/* ── 36 HOW IT FAILS ──────────────────────────────────────────────────────
   The block where the habit comes apart. It sits between the argument and
   the proposed architecture, and it is the only place on the site that is
   allowed to feel unstable: a rule that fractures, paragraphs that step out
   of alignment, and one line that snaps back straight at the end. */
.brk{position:relative;margin:clamp(40px,5.5vw,72px) 0 0;
  padding:clamp(28px,3.6vw,42px) 0 0}
.brk__rule{height:2px;margin-bottom:26px;
  background:repeating-linear-gradient(90deg,
    var(--red) 0 34px, transparent 34px 41px,
    var(--red) 41px 58px, transparent 58px 72px,
    var(--red) 72px 79px, transparent 79px 104px)}
.brk__h{margin:16px 0 0;max-width:22ch;
  font-family:var(--display);font-weight:800;letter-spacing:-.032em;line-height:1.02;
  font-size:clamp(1.7rem,4.4vw,3rem);color:var(--paper);text-wrap:balance}
.brk__body{margin-top:26px;display:grid;gap:20px;max-width:82ch}
.brk__body p{color:var(--steel);font-size:clamp(15px,1.15vw,16.5px);line-height:1.75}

/* Each paragraph starts a little further out than the last, then the pull
   quote returns to the margin. The text physically loses alignment while it
   describes losing alignment, and snaps back when the point lands. */
.brk__body p:nth-child(2){margin-left:clamp(0px,2.4vw,34px)}
.brk__body p:nth-child(3){margin-left:clamp(0px,4.6vw,66px)}
.brk__body p:nth-child(2)::before,
.brk__body p:nth-child(3)::before{
  content:'';position:absolute;left:0;width:2px;height:1.4em;margin-top:.2em;
  background:var(--red);opacity:.5}
.brk__body p{position:relative}

.brk__pull{margin:clamp(28px,3.4vw,40px) 0 0;max-width:44ch;
  padding-left:20px;border-left:2px solid var(--red);
  font-family:var(--serif);font-size:clamp(1.15rem,2.3vw,1.6rem);
  line-height:1.35;letter-spacing:-.01em;color:var(--paper-2)}

@media (max-width:700px){
  .brk__body p:nth-child(2),.brk__body p:nth-child(3){margin-left:16px}
}
@media print{.brk__rule{background:var(--red)}}

/* ── 37 HOVER: THE SECOND FRAMING ─────────────────────────────────────────
   The alternate crop sits on top of the original at zero opacity and only
   costs anything once it is hovered — it is lazy, so a visitor who never
   mouses over a photograph never downloads one. */
.fig__alt{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;opacity:0;pointer-events:none;z-index:1;
  filter:saturate(1.06) contrast(1.05) brightness(.97);
  /* .18s reads as a cut to a second frame. .55s read as the page
     dissolving, which is why the alternate crop looked like a glitch
     rather than a decision. */
  transition:opacity .18s var(--ease),transform .3s cubic-bezier(.2,.7,.3,1)}
.has-cam:hover .fig__alt{opacity:1}

/* A hairline label that names what you are looking at, so the second crop
   reads as deliberate rather than as the page glitching. */
.has-cam::after{z-index:3}
.has-cam .fig__tag,
.has-cam::before{transition:opacity .4s var(--ease)}
.has-cam{cursor:crosshair}
.has-cam img{backface-visibility:hidden}
/* The underlying image no longer dims on hover. It was fading down while
   the alternate crop faded up -- two slow filters running against each
   other, which is most of why the swap felt muddy. */
.has-cam:hover > picture > img{filter:saturate(1.06) contrast(1.05) brightness(.97)}

@media (hover:none),(prefers-reduced-motion:reduce){
  .fig__alt{display:none}
  .has-cam{cursor:default}
}

/* ── 38 THE DEDICATION ────────────────────────────────────────────────────
   The one warm thing on the site. Every other accent is #d91f26 on
   near-black; this is candlelight. A single warm passage at the end of a
   very cold document reads as deliberate — which it is.

   Her name is set in the serif, not the grotesk. The whole file is set in
   the display face; changing voice for one word is the typographic
   equivalent of lowering yours. */
.ded{position:relative;overflow:hidden;isolation:isolate;
  padding:clamp(84px,13vw,180px) 0 clamp(64px,10vw,132px);
  background:
    radial-gradient(120% 90% at 50% 42%, rgba(255,170,96,.075), transparent 62%),
    #08080a;
  border-top:1px solid var(--line)}
.ded__cv{position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none}
.ded__in{position:relative;z-index:1;width:min(1320px,100%);margin:0 auto;
  padding:0 clamp(20px,5vw,64px);max-width:820px}

.ded__k{color:rgba(255,190,130,.62)}
.ded__kicker{margin:22px 0 0;font:400 11px/1 var(--mono);letter-spacing:.42em;
  color:rgba(255,205,155,.55)}

.ded__name{margin:14px 0 0;font-family:'Pinyon Script',var(--serif);font-weight:400;
  /* Larger than any heading on the site except the name in the hero. That is
     the hierarchy the section is arguing for, so it should be visible from
     across the room. */
  font-size:clamp(4.6rem,19vw,14.5rem);line-height:1.02;letter-spacing:0;
  padding:.06em 0 .12em;color:#fff4e8}
.ded__name span{
  display:inline-block;
  /* The glow is on the type itself, not a filter on the box, so it stays
     crisp at any size and costs the compositor nothing. */
  text-shadow:
    0 0 30px rgba(255,196,132,.38),
    0 0 84px rgba(255,164,92,.26),
    0 0 190px rgba(255,142,72,.18);
  opacity:0;transform:translateY(14px);
  transition:opacity 1.5s cubic-bezier(.2,.7,.3,1), transform 1.6s cubic-bezier(.2,.7,.3,1)}
.ded.is-lit .ded__name span{opacity:1;transform:none}

.ded__body{margin-top:clamp(30px,4vw,46px);display:grid;gap:20px}
.ded__body p{max-width:62ch;color:rgba(236,222,206,.72);
  font-size:clamp(15.5px,1.2vw,17.5px);line-height:1.78;
  opacity:0;transform:translateY(10px);
  transition:opacity 1.1s var(--ease),transform 1.1s var(--ease)}
.ded.is-lit .ded__body p{opacity:1;transform:none}
.ded.is-lit .ded__body p:nth-child(1){transition-delay:.35s}
.ded.is-lit .ded__body p:nth-child(2){transition-delay:.6s}
.ded.is-lit .ded__body p:nth-child(3){transition-delay:.85s}
.ded__body em{font-style:italic;color:rgba(255,224,196,.9)}

.ded__sign{margin:30px 0 0;font-family:var(--serif);font-size:1.35rem;
  color:rgba(255,224,196,.86);
  opacity:0;transition:opacity 1.2s var(--ease) 1.1s}
.ded.is-lit .ded__sign{opacity:1}
.ded__note{margin:26px 0 0;max-width:56ch;
  font:400 11.5px/1.75 var(--mono);color:rgba(214,190,168,.38)}

@media (prefers-reduced-motion:reduce){
  .ded__name span,.ded__body p,.ded__sign{opacity:1;transform:none;transition:none}
}
@media print{.ded__cv{display:none}.ded{background:none}}

/* ── 39 THE SECOND DEDICATION ─────────────────────────────────────────────
   Sits under the first, in the same candlelight, separated by a rule rather
   than a new section — because it is the same gesture continued, not a
   different one. The name is set smaller than Crystal's on purpose: this is
   a second verse, not a second chorus. */
.ded__two{margin-top:clamp(80px,11vw,150px);padding-top:clamp(38px,4.4vw,56px);
  border-top:1px solid rgba(255,190,130,.16)}
.ded__name2{margin:12px 0 0;font-family:var(--serif);font-weight:400;
  font-size:clamp(1.75rem,5.2vw,3.4rem);line-height:.98;letter-spacing:-.02em;
  color:#ffeedc;text-wrap:balance;
  text-shadow:0 0 22px rgba(255,186,116,.24),0 0 60px rgba(255,158,84,.15)}
.ded__names{margin:16px 0 0;font:400 12px/1.6 var(--mono);letter-spacing:.28em;
  color:rgba(255,205,155,.72)}

/* The closing line returns to the site's own sentence, so it is set the way
   the site sets its arguments — mono, quiet, and flush to a warm rule. */
.ded__close{margin:clamp(26px,3.4vw,38px) 0 0;max-width:52ch;
  padding-left:18px;border-left:2px solid rgba(255,178,104,.5);
  font-size:15.5px;line-height:1.72;color:rgba(240,226,210,.8)}
.ded__close em{font-style:italic;color:#ffeedc}

.ded__two .ded__body{margin-top:clamp(24px,3vw,34px)}
.ded__two .ded__body p{opacity:0;transform:translateY(10px)}
.ded.is-lit .ded__two .ded__body p{opacity:1;transform:none}
.ded.is-lit .ded__two .ded__body p:nth-child(4){transition-delay:1.05s}
@media (prefers-reduced-motion:reduce){
  .ded__two .ded__body p{opacity:1;transform:none}
}

/* ── 40 THE DRUM ──────────────────────────────────────────────────────────
   A credit roll on a physical axis. Cards are stacked in one grid cell and
   rotated about X — the outgoing one falls away from you, the incoming one
   arrives from below, which is how a drum actually turns.

   Sized to hold a long list: only two cards are ever composited, and the
   full list is printed underneath in plain HTML so the drum is decoration
   and never the only copy of the content. */
.reel{margin-top:clamp(48px,6vw,80px);padding-top:clamp(30px,3.6vw,42px);
  border-top:1px solid rgba(255,190,130,.16)}
.reel__stage{position:relative;display:grid;margin-top:18px;
  min-height:clamp(120px,15vw,168px);perspective:900px}
.reel__card{grid-area:1/1;transform-origin:50% 100%;backface-visibility:hidden;
  opacity:0;transform:rotateX(-72deg) translateY(24px);pointer-events:none}
.reel__card.is-on{opacity:1;transform:none;pointer-events:auto;
  transition:opacity .55s var(--ease) .18s,transform .82s cubic-bezier(.16,.84,.28,1) .18s}
.reel__card.is-out{opacity:0;transform:rotateX(64deg) translateY(-22px);
  transition:opacity .42s var(--ease),transform .72s cubic-bezier(.4,0,.7,.4)}
.reel__n{margin:0;font-family:var(--serif);font-weight:400;
  font-size:clamp(1.9rem,5vw,3.2rem);line-height:1.02;letter-spacing:-.02em;color:#ffeedc;
  text-shadow:0 0 22px rgba(255,186,116,.22),0 0 58px rgba(255,158,84,.13)}
.reel__l{margin:14px 0 0;max-width:52ch;color:rgba(236,222,206,.68);
  font-size:clamp(15px,1.15vw,16.5px);line-height:1.72}

.reel__dots{list-style:none;display:flex;flex-wrap:wrap;gap:7px;margin:24px 0 0;padding:0}
.reel__dots li{width:16px;height:2px;background:rgba(255,190,130,.2);transition:background .4s}
.reel__dots li.is-on{background:rgba(255,186,116,.85)}

/* Every name, plainly. The drum shows one at a time; this shows all of them,
   and it is what a screen reader and a printer get. */
.reel__all{list-style:none;margin:30px 0 0;padding:0;display:grid;gap:9px;
  columns:2;column-gap:38px}
.reel__all li{break-inside:avoid;color:rgba(236,222,206,.5);
  font-size:14px;line-height:1.6}
.reel__all b{color:rgba(255,224,196,.86);font-weight:600}
@media (max-width:700px){.reel__all{columns:1}}

@media (prefers-reduced-motion:reduce){
  .reel__stage,.reel__dots{display:none}
  .reel__all{margin-top:18px}
}
@media print{.reel__stage,.reel__dots{display:none}}

/* ── 41 THE TICKER ────────────────────────────────────────────────────────
   One continuous line of names, running slowly left, in the serif. It takes
   any number of names without changing: the track holds the list twice and
   translates exactly -50%, so the loop closes on itself no matter how long
   the list gets.

   Names in Instrument Serif at a size you would set a book in. Separators in
   the mono, small and dim — the punctuation should not compete with the
   people. Two faces doing two jobs, which is the whole reason to have two. */
.tick{margin-top:clamp(40px,5vw,64px);padding-top:clamp(26px,3vw,34px);
  border-top:1px solid rgba(255,190,130,.16)}
.tick__lab{margin:0 0 18px;font:400 10.5px/1 var(--mono);letter-spacing:.42em;
  color:rgba(255,205,155,.5)}

/* The window fades at both edges so names arrive and leave rather than being
   chopped off by the viewport. */
.tick__win{position:relative;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent)}
.tick__track{display:flex;width:max-content;
  animation:tickRun 42s linear infinite;will-change:transform}
.tick__run{display:flex;align-items:baseline;flex:none}
.tick__n{font-family:var(--serif);font-weight:400;
  font-size:clamp(1.5rem,3.6vw,2.6rem);line-height:1.15;letter-spacing:-.015em;
  color:#ffeedc;padding:0 .12em;
  text-shadow:0 0 20px rgba(255,186,116,.20),0 0 52px rgba(255,158,84,.10)}
.tick__sep{font-style:normal;font-family:var(--mono);font-size:.72rem;
  color:rgba(255,178,104,.45);padding:0 clamp(18px,2.4vw,34px);
  transform:translateY(-.35em)}

@keyframes tickRun{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}
/* Nobody should have to chase a name they are trying to read. */
.tick:hover .tick__track{animation-play-state:paused}

@media (prefers-reduced-motion:reduce){
  .tick__track{animation:none;flex-wrap:wrap;width:auto}
  .tick__win{-webkit-mask-image:none;mask-image:none}
  .tick__run:last-child{display:none}
}
@media print{.tick{display:none}}

/* ── 42 THE PAGE THAT ISN'T FOR YOU ───────────────────────────────────────
   Hidden in the markup for everyone, unhidden for one person by a token her
   browser is holding. It gets the same measure as the public dedication —
   it is the same section, not a different site — but it is set apart with a
   warm rule and a badge, so it is unmistakably addressed rather than
   overheard. */
.her{position:relative;z-index:1;width:min(1320px,100%);margin:0 auto;
  padding:0 clamp(20px,5vw,64px) clamp(56px,8vw,104px);max-width:820px}
.her__badge{margin:0 0 26px;display:inline-block;
  font:600 9.5px/1.6 var(--mono);letter-spacing:.28em;
  color:rgba(255,196,132,.9);
  padding:6px 12px;border:1px solid rgba(255,178,104,.4);
  background:rgba(255,178,104,.05)}
.her .ded__kicker{margin-top:0}
.her__name{margin-top:10px !important}
/* Smaller than Crystal's name below it. This block is the aside; the
   dedication is still the monument. */
.her .ded__name span{font-size:clamp(2.6rem,9vw,6.2rem);opacity:1;transform:none}
.her .ded__body p{opacity:1;transform:none;transition:none;max-width:56ch}
.her .ded__body{gap:22px}
/* The apology paragraph carries the weight; give it the warm rule so the eye
   lands there first on a re-read. */
.her .ded__body p:nth-child(3){padding-left:18px;border-left:2px solid rgba(255,178,104,.45);
  color:rgba(255,232,212,.94)}
.her .ded__body p:nth-last-child(2){color:rgba(255,238,220,.96);font-size:1.15em;line-height:1.6}
.her .ded__sign{opacity:1;transition:none}

/* A hairline that closes the private half before the public one opens. */
.her::after{content:'';position:absolute;left:clamp(20px,5vw,64px);right:clamp(20px,5vw,64px);
  bottom:clamp(28px,4vw,52px);height:1px;background:rgba(255,190,130,.16)}

@media (max-width:640px){
  .her .ded__name span{font-size:clamp(2.2rem,11vw,3.6rem)}
}
@media print{.her{display:none}}

/* The canvas the gift is written on. It exists for about three seconds, once
   in a lifetime, and then removes itself. */
.gift{position:absolute;pointer-events:none;z-index:2}

/* ── 43 WIPE THE GLASS ────────────────────────────────────────────────────
   Three words under a pane of frost, on a phone only. The words are real
   text underneath — the canvas covers them, it does not replace them, so
   with no JavaScript, no touch, or reduced motion they are simply there. */
.rub{margin-top:clamp(46px,7vw,78px);padding-top:clamp(28px,3.6vw,40px);
  border-top:1px solid rgba(255,190,130,.16)}
.k--warm{color:rgba(255,196,132,.85)}

.rub__pane{position:relative;margin-top:18px;overflow:hidden;
  min-height:clamp(150px,34vw,220px);display:grid;place-items:center;
  border:1px solid rgba(255,178,104,.22);
  background:radial-gradient(120% 130% at 50% 50%,rgba(255,170,96,.07),transparent 70%)}

.rub__msg{margin:0;padding:0 18px;text-align:center;
  font-family:'Pinyon Script',var(--serif);font-weight:400;
  font-size:clamp(2.6rem,13vw,5rem);line-height:1.15;color:#fff4e8;
  text-shadow:0 0 28px rgba(255,196,132,.4),0 0 80px rgba(255,164,92,.24)}

.rub__glass{position:absolute;inset:0;width:100%;height:100%;
  touch-action:none;cursor:grab;z-index:2}

.rub__hint{position:absolute;inset:0;z-index:3;pointer-events:none;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:9px;
  text-align:center;padding:0 24px;transition:opacity .5s var(--ease)}
.rub__hint span{font:600 11px/1.6 var(--mono);letter-spacing:.26em;color:rgba(255,214,170,.9)}
.rub__hint em{font-style:normal;max-width:30ch;
  font:400 12.5px/1.7 var(--sans);color:rgba(255,206,158,.5)}
/* The instruction goes the moment she starts — nobody needs to be told
   twice how to wipe a window. */
.rub.is-rubbing .rub__hint{opacity:0}

.rub__after{margin:18px 0 0;max-width:52ch;
  font:400 13px/1.7 var(--mono);color:rgba(214,190,168,.42);
  opacity:0;transform:translateY(6px);
  transition:opacity 1s var(--ease) .4s,transform 1s var(--ease) .4s}
.rub.is-open .rub__after{opacity:1;transform:none}
.rub.is-open .rub__pane{border-color:rgba(255,178,104,.45)}

@media print{.rub__glass,.rub__hint{display:none}}


/* ============================================================
   44. THE DECAL PLATE
   ------------------------------------------------------------
   A black-ground graphic on a black page. Without a frame it reads
   as a hole in the layout, so it gets a hairline and a little air
   rather than a card — the artwork is the object, not the box. */
.fig--plate .fig__frame{
  border:1px solid var(--line);background:#000;
  padding:clamp(22px,4vw,52px) clamp(18px,4vw,60px)}
.fig--plate img{width:100%;height:auto}


/* ============================================================
   45. OFF THE RECORD — the hinge between the work and the people
   ------------------------------------------------------------
   Warmer ground than anything above it, narrower measure, serif.
   The page has been a document up to here; this reads as a letter,
   which is the whole argument the section is making. */
.bal{
  position:relative;margin-top:var(--sec);
  padding:clamp(56px,9vw,120px) 0;
  background:
    radial-gradient(120% 90% at 50% 0%,rgba(255,178,104,.075),transparent 62%),
    linear-gradient(180deg,rgba(255,255,255,.028),rgba(255,255,255,.012));
  border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.bal__in{width:min(66ch,calc(100% - clamp(20px,6vw,64px)));margin-inline:auto}
.bal__kicker{
  margin:14px 0 0;font-family:var(--mono);font-size:.7rem;letter-spacing:.16em;
  text-transform:uppercase;color:var(--dim)}
.bal__h{
  margin:18px 0 0;max-width:20ch;text-wrap:balance;
  font-family:var(--serif);font-weight:400;letter-spacing:-.01em}
.bal__body{margin-top:clamp(26px,3.6vw,40px)}
.bal__body p{
  margin:0 0 1.15em;font-family:var(--serif);
  font-size:clamp(1.12rem,1.9vw,1.38rem);line-height:1.62;
  color:color-mix(in srgb,var(--paper) 88%,transparent)}
.bal__body p:last-child{margin-bottom:0}
.bal__pull{
  margin:clamp(30px,4vw,46px) 0 0;padding-left:clamp(16px,2.2vw,26px);
  border-left:2px solid var(--red);
  font-family:var(--sans);font-weight:600;letter-spacing:-.015em;
  font-size:clamp(1.24rem,2.5vw,1.72rem);line-height:1.32;text-wrap:balance;
  color:#fff}
.bal__note{
  margin:26px 0 0;font-family:var(--mono);font-size:.74rem;letter-spacing:.06em;
  color:var(--dim)}


/* ============================================================
   46. THE PHOTOGRAPH — hers, and the writing on the back of it
   ------------------------------------------------------------
   A print, not an image: white border, weight under it, and a
   degree or so off square, because nothing anybody actually
   handled ever sits straight.

   Turning it is the part worth the trouble. One rotateY turns a
   card like a ceramic tile — every point of the surface arrives at
   once, and that is the tell. Paper leads with one edge, trails
   with the other, and bows through the middle on the way past.

   A row of independent panels turning on their own axes opens gaps
   between them and reads as a venetian blind, so the strips are
   CHAINED instead: each hangs off the edge of the one before it,
   running outward from the center in both directions. The sheet is
   then continuous by construction — there is nothing between the
   strips to tear — and it turns about its own middle while the far
   edges lag. That lag is the whole effect.

   No JavaScript. A checkbox holds the state; the rest is transforms
   and delays. Keyboard-operable for free, and it still works if
   nothing runs. */
/* --dur was 1.15s. A card turning over is a physical event and it is
   allowed more time than a hover, but not a second and a half -- past
   about three quarters of a second you stop watching it and start
   waiting for it. --step is the stagger between strips; tightening it
   keeps the turn reading as one sheet rather than nine. */
.pho{--n:9;--mid:4;--dur:.72s;--step:15ms;--bend:9deg;
  margin-top:clamp(40px,6vw,72px)}
.pho__t{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none}

.pho__card{
  display:block;position:relative;margin:18px 0 0;
  width:min(340px,74vw);aspect-ratio:340/446;
  perspective:1600px;perspective-origin:50% 46%;
  transform:rotateZ(-1.4deg);
  /* .8s to acknowledge a mouse arriving was the worst offender on the
     page -- you were past the card before it finished lifting. */
  transition:transform .26s var(--ease);
  cursor:pointer;-webkit-tap-highlight-color:transparent}
.pho__card:hover{transform:rotateZ(-.5deg) translateY(-4px)}
.pho__t:checked + .pho__card{transform:rotateZ(1deg)}
.pho__t:focus-visible + .pho__card{outline:2px solid var(--red-txt);outline-offset:12px}

/* THE SHADOW, IN THREE LAYERS.
   One blurred rectangle is why a card reads as a sticker. An object
   sitting on a surface casts three different things at once, and the eye
   knows all three:

     CONTACT  a hard, dark, narrow line where the object meets the
              surface and no light can get in at all
     MID      the shape of the object, offset, moderately soft
     CAST     a large, very soft, very faint pool that says how far the
              object is from the surface and how big the light is

   Blur and darkness move in opposite directions across those three, which
   is the part that sells it. All three belong to the card rather than to
   the strips: nine stacked shadows read as a deck of cards.

   They also have to move. When the sheet turns it lifts, and as it passes
   edge-on it casts almost nothing — so the layers narrow, lighten and
   drift down at the halfway point, then come back. A shadow that stays put
   while the object it belongs to rotates is the other big tell. */
.pho__shadow{
  position:absolute;inset:2% 1% -6%;z-index:-1;pointer-events:none;
  background:#000;filter:blur(30px);opacity:.42;
  transform-origin:50% 100%}
.pho__shadow::before,.pho__shadow::after{content:"";position:absolute;background:#000}
/* mid — the shape of the card, close in */
.pho__shadow::before{
  inset:5% 5% 4%;filter:blur(11px);opacity:.62;border-radius:2px}
/* contact — the dark line under the bottom edge */
.pho__shadow::after{
  left:7%;right:7%;bottom:5.5%;height:5px;
  filter:blur(4px);opacity:.85;border-radius:50%}
.pho__t:checked + .pho__card .pho__shadow{
  animation:phoCast var(--dur) cubic-bezier(.42,.04,.24,1) 1}
@keyframes phoCast{
  0%  {transform:translateY(0) scaleX(1);opacity:.42}
  46% {transform:translateY(16px) scaleX(.26);opacity:.16}
  100%{transform:translateY(0) scaleX(1);opacity:.42}}

.pho__sheet{position:absolute;inset:0;transform-style:preserve-3d}

.pho__col{
  position:absolute;top:0;height:100%;
  transform-style:preserve-3d}
/* the center strip carries the turn */
.pho__mid{
  left:calc(var(--mid) * 100% / var(--n));
  width:calc(100% / var(--n));
  transform-origin:50% 50%;transform:rotateY(0deg);
  transition:transform var(--dur) cubic-bezier(.42,.04,.24,1)}
.pho__t:checked + .pho__card .pho__mid{transform:rotateY(180deg)}
/* every other strip hangs off its neighbour and only ever bends */
.pho__r,.pho__l{width:100%}
.pho__r{left:100%;transform-origin:0% 50%}
.pho__l{right:100%;transform-origin:100% 50%}
.pho__t:checked + .pho__card .pho__r{
  animation:phoBendR var(--dur) cubic-bezier(.42,.04,.24,1) 1;
  animation-delay:calc(var(--d) * var(--step) * -1)}
.pho__t:checked + .pho__card .pho__l{
  animation:phoBendL var(--dur) cubic-bezier(.42,.04,.24,1) 1;
  animation-delay:calc(var(--d) * var(--step) * -1)}
@keyframes phoBendR{
  0%{transform:rotateY(0deg)}
  46%{transform:rotateY(var(--bend))}
  100%{transform:rotateY(0deg)}}
@keyframes phoBendL{
  0%{transform:rotateY(0deg)}
  46%{transform:rotateY(calc(var(--bend) * -1))}
  100%{transform:rotateY(0deg)}}

.pho__f{
  position:absolute;inset:0;overflow:hidden;
  backface-visibility:hidden;-webkit-backface-visibility:hidden;
  /* Sub-pixel seams. Each face is widened by exactly one pixel to the
     right so it overlaps its neighbour, and the slide inside is pushed
     back by the same pixel per strip — so the overlap closes the seam
     without shifting a single pixel of what the strip is showing. */
  right:-1px}
/* The bleed earns its keep on the back, where the ground is a gradient and a
   seam would show. On the front it puts a faint line down the print at every
   strip, and the photograph has no seams to hide — so the front gets none. */
.pho__front{right:0}
.pho__front .pho__slide{left:calc(var(--i) * -100%)}
/* self-shading: a strip turning edge-on to the light goes dark, and the
   shade travels outward with the bend */
.pho__f::before{
  content:"";position:absolute;inset:0;z-index:3;pointer-events:none;
  background:linear-gradient(90deg,rgba(0,0,0,.5),rgba(0,0,0,.9) 52%,rgba(0,0,0,.45));
  opacity:0}
.pho__t:checked + .pho__card .pho__f::before{
  animation:phoShade var(--dur) cubic-bezier(.42,.04,.24,1) 1;
  animation-delay:calc(var(--d) * var(--step) * -1)}
@keyframes phoShade{0%{opacity:0}44%{opacity:.26}100%{opacity:0}}

/* Each strip holds a full-width copy of the sheet and shows its own slice
   through overflow, so the writing stays text the browser renders. The back
   face is rotated 180deg inside a strip that also turns 180deg — the two
   cancel, so its slice offset is the same as the front's. */
.pho__slide{
  position:absolute;top:0;height:100%;
  left:calc(var(--i) * -100% + var(--i) * 1px);
  width:calc(var(--n) * 100%);
  display:flex;flex-direction:column}
.pho__front .pho__slide{
  background:#f2efe8;padding:13px 13px 0;
  /* the border of a print is paper, and paper has an edge */
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.07),inset 0 -22px 26px -22px rgba(0,0,0,.18)}
.pho__back{transform:rotateY(180deg)}
.pho__back .pho__slide{
  justify-content:center;
  padding:clamp(22px,4.6vw,32px) clamp(20px,4.4vw,28px);
  background:
    radial-gradient(120% 80% at 18% 8%,rgba(255,255,255,.6),transparent 60%),
    radial-gradient(90% 70% at 92% 96%,rgba(120,96,64,.12),transparent 62%),
    linear-gradient(168deg,#f3ede1,#e8dfcf 62%,#ded4c0);
  /* handled paper darkens toward its edges */
  box-shadow:inset 0 0 44px rgba(120,96,64,.16),inset 0 0 0 1px rgba(120,96,64,.1)}
/* the tooth of the paper. Faint — at full strength it reads as graph
   paper, which is the opposite of what a handwritten note should feel like. */
.pho__back .pho__slide::before{
  content:"";position:absolute;inset:0;pointer-events:none;opacity:.14;
  background:
    repeating-linear-gradient(94deg,rgba(120,96,64,.05) 0 1px,transparent 1px 4px),
    repeating-linear-gradient(3deg,rgba(120,96,64,.035) 0 1px,transparent 1px 5px)}

.pho__shot{
  display:block;flex:1 1 auto;
  background-image:var(--port);
  background-size:cover;background-position:50% 22%;
  filter:saturate(1.06) contrast(1.03);
  /* the image sits BELOW the paper surface — a print has a lip */
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.16),inset 0 3px 9px -2px rgba(0,0,0,.34)}
.pho__cap{
  display:block;flex:0 0 auto;padding:11px 2px 12px;
  font-family:var(--mono);font-size:.66rem;letter-spacing:.05em;
  color:#4a463f;text-align:center}

/* THE HAND.
   Pinyon is beautiful and, in five running lines at note size, close to
   unreadable — which defeats the point of writing something down for
   somebody. So the script does what a script is actually good at: the
   name at the top and the name at the bottom. The sentences in between
   are set in the serif, italic, at a size that does not ask her to work
   for it. It still reads as a hand-written note; it just reads. */
.pho__ink{display:block;position:relative;z-index:1}
.pho__ink span{
  display:block;
  font-family:var(--serif);font-style:italic;font-weight:400;
  font-size:clamp(1.06rem,3.35vw,1.32rem);line-height:1.44;
  letter-spacing:.002em;color:#231d15;
  text-shadow:0 1px 0 rgba(255,255,255,.45)}
.pho__ink span:first-child{
  font-family:'Pinyon Script',var(--serif);font-style:normal;
  font-size:clamp(1.5rem,4.6vw,1.95rem);line-height:1.2;
  margin-bottom:.6em;color:#1d180f}
.pho__ink span + span{margin-top:.14em}
/* the two lines the note is actually for */
.pho__ink span:nth-last-child(2){
  margin-top:.7em;font-style:italic;
  font-size:clamp(1.2rem,3.8vw,1.5rem);color:#1a150d}
.pho__ink span:last-child{
  margin-top:.42em;font-style:normal;font-weight:500;
  font-family:var(--serif);
  font-size:clamp(1.32rem,4.1vw,1.66rem);color:#150f07}
.pho__sign{
  display:block;position:relative;z-index:1;margin-top:1.15em;
  padding-right:6%;text-align:right;
  font-family:'Pinyon Script',var(--serif);
  font-size:clamp(1.5rem,4.4vw,1.9rem);color:#2c2317}

.pho__hint{
  margin:16px 0 0;font-family:var(--mono);font-size:.72rem;letter-spacing:.12em;
  text-transform:uppercase;color:var(--dim);transition:opacity .4s var(--ease)}
.pho__hint::before{content:"↻  "}
.pho__t:checked ~ .pho__hint{opacity:0}
.pho__after{
  margin:-1.05rem 0 0;max-width:44ch;
  font-family:var(--serif);font-size:1.02rem;line-height:1.55;
  color:var(--dim);opacity:0;transform:translateY(6px);
  transition:opacity .7s var(--ease) .75s,transform .7s var(--ease) .75s}
.pho__t:checked ~ .pho__after{opacity:1;transform:none}

/* One turn, no bend, no shade. The gesture survives; the theatre does not. */
@media (prefers-reduced-motion:reduce){
  .pho{--dur:.35s;--step:0ms}
  .pho__card,.pho__card:hover,.pho__t:checked + .pho__card{transition:none}
  .pho__t:checked + .pho__card .pho__r,
  .pho__t:checked + .pho__card .pho__l,
  .pho__t:checked + .pho__card .pho__f::before,
  .pho__t:checked + .pho__card .pho__shadow{animation:none}}

@media print{
  .pho__card{transform:none;height:auto;aspect-ratio:auto;perspective:none}
  .pho__sheet{display:none}}

/* ------------------------------------------------------------
   46b. THE OLD PRINT
   ------------------------------------------------------------
   Black and white is not desaturation. A silver print has a color —
   slightly warm in the highlights, slightly cool in the shadows — it
   never reaches pure black or pure white, it has grain, and it is
   darker at the corners than in the middle because every lens is.
   All four of those are what makes an eye read "old photograph"
   rather than "someone pressed the grayscale button". */
.pho--bw .pho__shot{
  filter:grayscale(1) contrast(.94) brightness(1.06) sepia(.22) saturate(.75);
  background-position:50% 42%}
.pho--bw .pho__shot::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  /* the tone: warm in the lights, cool in the darks, and never quite black */
  background:
    radial-gradient(120% 100% at 50% 44%,transparent 46%,rgba(24,20,14,.34) 100%),
    linear-gradient(178deg,rgba(255,238,208,.10),rgba(30,34,44,.14));
  mix-blend-mode:multiply}
.pho__grain{
  position:absolute;inset:0;pointer-events:none;opacity:0;
  background-image:
    repeating-radial-gradient(circle at 17% 31%,rgba(255,255,255,.10) 0 .6px,transparent .6px 2.1px),
    repeating-radial-gradient(circle at 71% 63%,rgba(0,0,0,.11) 0 .5px,transparent .5px 1.9px),
    repeating-radial-gradient(circle at 43% 88%,rgba(255,255,255,.07) 0 .4px,transparent .4px 1.6px)}
.pho--bw .pho__grain{opacity:.85;mix-blend-mode:overlay}
.pho__shot{position:relative}

/* ============================================================
   47. ALSO ON STAFF
   ------------------------------------------------------------
   A cat, given the same typographic respect as everything else in
   the file, which is the entire joke and the entire sincerity. */
.pet{
  display:grid;gap:clamp(20px,3vw,38px);align-items:center;
  margin-top:clamp(40px,6vw,72px);
  padding-top:clamp(28px,4vw,44px);border-top:1px solid var(--line)}
@media (min-width:860px){.pet{grid-template-columns:1fr 420px}}
.pet__name{
  margin:12px 0 0;font-family:var(--serif);font-weight:400;
  font-size:clamp(2.4rem,6vw,3.6rem);line-height:1;letter-spacing:-.015em;
  color:#fff}
.pet__role{
  margin:10px 0 0;font-family:var(--mono);font-size:.7rem;letter-spacing:.14em;
  color:var(--dim)}
.pet__body{margin-top:clamp(16px,2.2vw,24px);max-width:52ch}
.pet__body p{margin:0 0 .9em;line-height:1.62;
  color:color-mix(in srgb,var(--paper) 82%,transparent)}
.pet__body p:last-child{margin-bottom:0}
.fig--pet{margin:0}
.fig--pet .fig__frame{border:1px solid var(--line);border-radius:2px;overflow:hidden}
.fig--pet img{display:block;width:100%;height:auto}
.fig--pet figcaption{
  margin-top:12px;font-family:var(--mono);font-size:.7rem;letter-spacing:.04em;
  color:var(--dim)}

/* ------------------------------------------------------------
   47b. TWO ARMS
   His and hers, side by side and the same size. The pairing is
   the argument; making one of them bigger would undo it. */
.arms{display:grid;gap:clamp(16px,2.4vw,28px);margin-top:22px}
/* His frame is landscape and hers is tall. Equal columns leaves a hole
   above one of them; sizing the columns in proportion to the two aspect
   ratios makes both images finish at the same height instead. */
@media (min-width:860px){.arms{grid-template-columns:1.9fr 1fr;align-items:start}}
.arms figure{margin:0}
.arms .fig__frame{border:1px solid var(--line);overflow:hidden}
.arms img{display:block;width:100%;height:auto}
.bal__fig{margin:clamp(30px,4vw,46px) 0 0}
.bal__fig .fig__frame{border:1px solid var(--line);overflow:hidden}
.bal__fig img{display:block;width:100%;height:auto}
.bal__fig figcaption{margin-top:14px;font-size:.95rem;line-height:1.55;color:var(--dim)}

/* ============================================================
   48. THE MARK
   ------------------------------------------------------------
   Crystal's sleeve, traced to line art and used as structure
   rather than hung on the wall as a picture. It appears twice:
   very large and very faint behind the argument about work and
   family — because gears and flowers in one composition IS that
   argument, and it was drawn on her arm before it was written
   down here — and small and solid at the head of her page.

   One <symbol>, two <use>. It is never a photograph of anybody. */
.bal__mark{
  position:absolute;top:50%;right:-6%;transform:translateY(-50%);
  height:min(122%,940px);width:auto;pointer-events:none;
  color:var(--paper);opacity:.13;
  -webkit-mask-image:linear-gradient(100deg,transparent 4%,#000 46%,#000 74%,transparent 99%);
  mask-image:linear-gradient(100deg,transparent 4%,#000 46%,#000 74%,transparent 99%)}
@media (max-width:860px){.bal__mark{right:-34%;height:104%;opacity:.11}}
.bal__in{position:relative;z-index:1}

.her__mark{
  display:block;height:clamp(150px,24vw,260px);width:auto;
  /* No glow. The linework is hairline at this size and a drop-shadow turns
     the whole mark into a smudge with a drawing somewhere inside it. */
  margin:0 0 30px;color:#ffd9ae;opacity:1}

@media print{.bal__mark{display:none}}


/* ============================================================
   48b. THE STRATA — the page has layers, so it should move like it
   ------------------------------------------------------------
   Three traced drawings, laid into the document as ground rather
   than hung on it as pictures: her sleeve, mine, the bike. Each
   sits a step above black — dark enough to read as texture in
   peripheral vision, never dark enough to compete with a word of
   type — and each travels slower than the page it is printed on.

   That last part is the whole idea. A flat page scrolls as one
   sheet. Give the marks their own rate and the black stops being
   a background and becomes a distance, which is a lot of depth
   for one transform and no images.

   The blur is deliberate. Traced linework at this scale carries
   the stair-stepping of the photograph it came from; half a pixel
   of blur reads as focus falling off with distance and costs
   nothing, because these layers are never in focus anyway. */
/* The offset the parallax writes, and the ONE place the transform is
   assembled. The script used to set `transform` outright and hand back the
   rotation only for .strata--mine, so the four layers that turn -- and the
   one that is also mirrored -- snapped square the instant they scrolled
   into range. A custom property fixes it for good: the script writes a
   number, the stylesheet decides what to do with it, and a layer added
   later gets the behaviour without anybody remembering to special-case it. */
.strata{
  position:absolute;pointer-events:none;z-index:0;
  color:#d9d5cd;opacity:.26;
  filter:blur(.6px);
  transform:translate3d(0,var(--py,0px),0);
  will-change:transform}
.strata--mine{
  top:-6%;left:-14%;width:min(86%,1240px);height:auto;
  transform:translate3d(0,var(--py,0px),0) rotate(-4deg);
  -webkit-mask-image:radial-gradient(90% 120% at 34% 50%,#000 24%,transparent 82%);
  mask-image:radial-gradient(90% 120% at 34% 50%,#000 24%,transparent 82%)}
.strata--bike{
  right:-4%;bottom:-6%;width:min(62%,780px);height:auto;
  -webkit-mask-image:radial-gradient(120% 110% at 60% 45%,#000 42%,transparent 88%);
  mask-image:radial-gradient(120% 110% at 60% 45%,#000 42%,transparent 88%)}
.strata--her{
  top:50%;right:-7%;height:min(126%,980px);width:auto;
  margin-top:-63%;opacity:0.3;
  /* one layer picks up the file's red. Once, at the turn of the
     document, which is the only place it means anything. */
  color:#e2564d;
  -webkit-mask-image:linear-gradient(100deg,transparent 4%,#000 46%,#000 74%,transparent 99%);
  mask-image:linear-gradient(100deg,transparent 4%,#000 46%,#000 74%,transparent 99%)}
@media (max-width:860px){
  .strata--mine{width:150%;left:-40%;opacity:0.264}
  .strata--bike{width:104%;right:-16%;opacity:0.264}
  .strata--her{height:104%;right:-32%;opacity:0.3}}

/* the sections that hold them */
#os,#range,.bal{position:relative;overflow:hidden}
#os > *:not(.strata),#range > *:not(.strata){position:relative;z-index:1}
.bal__in{position:relative;z-index:1}

/* ---------------------------------------------------------------
   THE REST OF THE SUBSTRATE
   ---------------------------------------------------------------
   The LinkedIn banner works because the same three drawings appear
   at six sizes and no two angles, so the eye reads texture instead
   of a logo repeated. Same rule down the page: three traces, twelve
   placements, no two at the same scale, rotation or opacity, and
   every one on its own parallax rate so they sit at different
   distances rather than sliding as one sheet.

   Every layer is masked. An un-masked edge is what turns a motif
   into a sticker, and there is no faster way to make a page look
   like a template.

   Opacity was .05 to .17, written to the rule "if you notice one of
   these while reading, it is too strong." That was the wrong rule for
   this page. At .055 a #d9d5cd line over #0b0b0c lands about twelve
   levels off the background -- present in a screenshot, invisible to a
   person, which is the same as not having drawn it. The banner these
   came from wears them openly and they are the best thing on it.

   So they run .12 to .30 now. Still far under the body copy, which sits
   at #9a9aa4 and up, so nothing has to fight them to be read -- but you
   can actually see the hand that drew them, which was the entire point
   of putting them there. */
#profile,#systems,#record,#ask{position:relative;overflow:hidden}
#profile > *:not(.strata),#systems > *:not(.strata),
#record  > *:not(.strata),#ask     > *:not(.strata){position:relative;z-index:1}

/* the big one. Front of the stack, so it moves most. */
.strata--bike-profile{
  top:-10%;right:-18%;width:min(64%,860px);height:auto;opacity:0.165;
  transform:translate3d(0,var(--py,0px),0) rotate(4deg) scaleX(-1);
  -webkit-mask-image:radial-gradient(96% 104% at 62% 40%,#000 16%,transparent 78%);
          mask-image:radial-gradient(96% 104% at 62% 40%,#000 16%,transparent 78%)}

/* the small one, in the same section as the largest sleeve. One section
   carrying two scales is what stops the page reading as one repeat. */
.strata--her-os{
  bottom:-6%;right:4%;height:min(46%,360px);width:auto;opacity:0.22;
  transform:translate3d(0,var(--py,0px),0) rotate(-14deg);
  -webkit-mask-image:radial-gradient(80% 80% at 50% 50%,#000 26%,transparent 84%);
          mask-image:radial-gradient(80% 80% at 50% 50%,#000 26%,transparent 84%)}

.strata--her-systems{
  top:14%;left:-14%;height:min(96%,820px);width:auto;opacity:0.121;
  transform:translate3d(0,var(--py,0px),0) rotate(11deg);
  -webkit-mask-image:linear-gradient(74deg,transparent 8%,#000 42%,#000 66%,transparent 96%);
          mask-image:linear-gradient(74deg,transparent 8%,#000 42%,#000 66%,transparent 96%)}

/* deepest layer on the page: 0.09, barely moving, which is what puts it
   behind everything else even though nothing overlaps it. */
.strata--bike-record{
  bottom:-12%;left:-12%;width:min(52%,700px);height:auto;opacity:0.11;
  transform:translate3d(0,var(--py,0px),0) rotate(-7deg);
  -webkit-mask-image:radial-gradient(90% 96% at 38% 58%,#000 14%,transparent 76%);
          mask-image:radial-gradient(90% 96% at 38% 58%,#000 14%,transparent 76%)}

.strata--mine-ask{
  top:-14%;right:-24%;width:min(74%,980px);height:auto;opacity:0.132;
  transform:translate3d(0,var(--py,0px),0) rotate(8deg) scaleX(-1);
  -webkit-mask-image:radial-gradient(84% 100% at 56% 38%,#000 18%,transparent 74%);
          mask-image:radial-gradient(84% 100% at 56% 38%,#000 18%,transparent 74%)}

@media (max-width:860px){
  .strata--bike-profile{width:120%;right:-40%;opacity:0.132}
  .strata--her-os{height:34%;right:-8%}
  .strata--her-systems{height:70%;left:-42%;opacity:0.099}
  .strata--bike-record{display:none}
  .strata--mine-ask{width:130%;right:-46%;opacity:0.11}
}

@media (prefers-reduced-motion:reduce){.strata{transform:none!important}}
@media print{.strata{display:none}}


/* ============================================================
   49. HOW IT IS MADE — the teaching layer
   ------------------------------------------------------------
   Off by default and completely absent from the layout until it
   is asked for. Markers are position:absolute so turning the
   thing on never reflows a single line of the page underneath —
   if flipping the switch made the text jump, nobody would flip
   it twice.

   Everything sits behind .is-teaching on <html> rather than on
   <body>, so it is set before first paint on a reload and there
   is no flash of markers appearing a beat late.
   ============================================================ */
.ftr__teach{
  font:600 11px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--steel);background:none;border:0;padding:0 0 2px;cursor:pointer;
  border-bottom:1px solid transparent;transition:color .18s,border-color .18s}
.ftr__teach:hover{color:var(--paper);border-bottom-color:var(--red)}
.ftr__teach[aria-pressed="true"]{color:var(--red-txt);border-bottom-color:var(--red)}

.lsn{display:none}
.is-teaching .lsn{
  display:block;position:absolute;top:8px;right:8px;z-index:40;
  max-width:min(360px,86%);text-align:left;pointer-events:auto}
.lsn__pin{
  display:flex;align-items:baseline;gap:9px;width:100%;cursor:pointer;
  background:rgba(10,10,12,.94);border:1px solid var(--red);
  padding:8px 11px;text-align:left;
  box-shadow:var(--cast-2)}
.lsn__pin i{font:600 10px/1 var(--mono);color:var(--red-txt);font-style:normal;
  letter-spacing:.1em;flex:none}
.lsn__pin b{font:600 11.5px/1.35 var(--mono);letter-spacing:.04em;
  color:var(--paper);font-weight:600}
.lsn__pin:hover{background:rgba(18,18,22,.97)}

.lsn__body{
  background:rgba(10,10,12,.97);border:1px solid var(--line);border-top:0;
  padding:13px 13px 14px;
  box-shadow:var(--cast-2)}
.lsn__body p{font:400 13.5px/1.55 var(--sans);color:#d6d5d2;margin:0}
.lsn__src{margin-top:10px!important;
  font:400 11px/1.4 var(--mono)!important;color:var(--graphite)!important;
  letter-spacing:.02em}

/* the marker is a note about the page, not part of the page */
@media print{.lsn,.ftr__teach{display:none!important}}
@media (max-width:700px){
  .is-teaching .lsn{position:static;max-width:none;margin:14px 0 0}
}


/* ============================================================
   50. THE EVIDENCE LEDGER
   ------------------------------------------------------------
   Exceptions sit at the top in red, above the numbers I am proud
   of. That order is the whole point. At the bottom it reads as a
   disclaimer; at the top it reads as a control, and a control is
   what it is.
   ============================================================ */
.ev__exc{border:1px solid var(--red);background:rgba(217,31,38,.05);
  padding:clamp(20px,2.6vw,28px);margin:clamp(28px,3.4vw,40px) 0 clamp(24px,3vw,34px)}
.ev__exc-hd{font:600 12px/1.4 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--red-txt);margin:0 0 18px}
.ev__exc-row{padding-top:14px;border-top:1px solid rgba(217,31,38,.28)}
.ev__exc-row:first-of-type{border-top:0;padding-top:0}
.ev__exc-row b{display:block;font:600 clamp(16px,2vw,19px)/1.3 var(--sans);
  color:var(--paper);margin-bottom:8px;letter-spacing:-.01em}
.ev__exc-row p{margin:0;font:400 15px/1.6 var(--sans);color:#d4d3d0;max-width:64ch}
.ev__exc-src{margin-top:8px!important;font:400 11.5px/1.5 var(--mono)!important;
  color:var(--graphite)!important;letter-spacing:.02em}
.ev__exc-foot{margin:18px 0 0;padding-top:14px;border-top:1px solid rgba(217,31,38,.28);
  font:400 13px/1.6 var(--sans);color:var(--steel);max-width:70ch}

/* the ledger itself. Wide content scrolls in its own box so the page never does */
.ev{width:100%;border-collapse:collapse;margin:clamp(22px,3vw,34px) 0 0;
  display:block;overflow-x:auto}
.ev thead th{font:600 10.5px/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;
  color:var(--graphite);text-align:left;padding:0 18px 10px 0;
  border-bottom:1px solid var(--line);white-space:nowrap}
.ev td{padding:15px 18px 15px 0;border-bottom:1px solid var(--line-2);vertical-align:top}
.ev tr:target td{background:rgba(217,31,38,.07)}
.ev__v{font:600 clamp(17px,2.2vw,21px)/1.1 var(--mono);color:var(--paper);
  white-space:nowrap;font-variant-numeric:tabular-nums}
.ev__l{font:500 14.5px/1.45 var(--sans);color:var(--paper);min-width:15ch}
.ev__l em{display:block;font-style:normal;font-size:12.5px;line-height:1.5;
  color:var(--steel);margin-top:5px;max-width:44ch}
.ev__b{font:400 13px/1.5 var(--sans);color:#c9c8c6;min-width:24ch}
.ev__b span{display:block;font:400 11.5px/1.5 var(--mono);color:var(--graphite);margin-top:5px}
.ev__s{white-space:nowrap}
.ev__s span{display:block;font:400 10.5px/1 var(--mono);color:var(--graphite);margin-top:7px}
.ev__tag{display:inline-block;font:600 10px/1 var(--mono);letter-spacing:.16em;
  text-transform:uppercase;padding:5px 8px;border:1px solid var(--line)}
.ev__tag--verified{color:#8fbf9a;border-color:#2e4635}
.ev__tag--scoped{color:#c9b071;border-color:#4a4028}
.ev__tag--unverified{color:var(--red-txt);border-color:var(--red);
  background:rgba(217,31,38,.10)}

.ev__key{margin:clamp(26px,3vw,36px) 0 0;display:grid;gap:14px;max-width:74ch}
.ev__key dt{font:600 10.5px/1 var(--mono);letter-spacing:.18em;text-transform:uppercase;
  color:var(--steel);margin-bottom:6px}
.ev__key dd{margin:0;font:400 13.5px/1.55 var(--sans);color:#c9c8c6}

/* a claim that does not tie should look different where it is USED, too */
.metrics li[data-standing="unverified"] b{color:var(--red-txt)}
.recon__total--exc dd{color:var(--red-txt)}

/* Cross-examine any row. Quiet until you hover it — the ledger is the record,
   this is the invitation, and an invitation should not shout over the record. */
.ev__ch{display:block;margin-top:10px;background:none;border:0;padding:0;cursor:pointer;
  font:600 9.5px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--graphite);border-bottom:1px solid transparent;transition:color .18s,border-color .18s}
.ev__ch:hover{color:var(--red-txt);border-bottom-color:var(--red)}

/* An exception badge that looks like a pass is worse than no badge. The tie
   state is green because it is good news; this one is not good news and has no
   business borrowing the color. */
.recon__ties--exc{color:var(--red-txt);border-color:var(--red);
  background:rgba(217,31,38,.08)}


/* ============================================================
   TRADEMARK MARK + TERMS PAGE
   ------------------------------------------------------------
   The trade sign is set in a superscript that does NOT use the
   font's own superscript metrics -- those vary wildly between the
   fallbacks in this stack, and a mark that jumps a few pixels
   between two machines reads as a mistake rather than a claim.
   Fixed size, fixed offset, no vertical-align: super.
   ============================================================ */
.tm{
  font-size:.34em; line-height:1; font-weight:600;
  vertical-align:baseline; position:relative; top:-.85em; left:.1em;
  letter-spacing:0; color:var(--red-txt); opacity:.85;
}
.ftr__brand .tm{ top:-.78em; opacity:.6 }

.tms{ max-width:52rem; padding-top:clamp(48px,9vh,110px); padding-bottom:96px }
.tms h1{ margin:.4em 0 .6em; text-wrap:balance }
.tms h2{ margin:2.2em 0 .5em }
.tms__lede{ font-size:1.12rem; max-width:44rem }
.tms__l{ list-style:none; padding:0; margin:0 0 1.4em; max-width:44rem }
.tms__l li{
  padding:.55em 0 .55em 1.9em; position:relative;
  border-bottom:1px solid var(--line);
}
.tms__l li::before{
  content:"\002713"; position:absolute; left:0; top:.5em;
  color:var(--red-txt); font-size:.9em;
}
/* One rule, one property. The "may not" list differs from the "may"
   list by a single glyph, so it inherits everything and overrides
   exactly that -- rather than being a second copy that drifts. */
.tms__l--no li::before{ content:"\00d7"; font-size:1.1em; top:.42em }
.tms__fine{
  margin-top:2.6em; padding-top:1.2em; border-top:1px solid var(--line);
  font-size:.86rem; opacity:.72; max-width:44rem;
}

/* ============================================================
   RUN THE CLOSE
   The simulator animates the argument; this hands it over.
   Layout note: the rail is a grid with as many equal columns as
   there are stages, so adding a ninth stage in the content file
   needs no CSS change. Nothing here counts to eight.
   ============================================================ */
.run{
  border:1px solid var(--line); background:var(--panel);
  margin-top:clamp(30px,4vw,52px);
}
.run__hd{ padding:clamp(20px,3vw,34px) clamp(18px,3vw,34px) 0 }
.run__hd h3{ margin:.35em 0 .5em }
.run__lede{ margin:0; color:var(--steel); max-width:60ch }

.run__rail{
  list-style:none; margin:clamp(20px,3vw,30px) 0 0; padding:0;
  display:grid; grid-auto-flow:column; grid-auto-columns:1fr;
  border-top:1px solid var(--line); border-bottom:1px solid var(--line);
  overflow-x:auto;
}
.run__rail li{
  padding:12px 10px; border-left:1px solid var(--line-2); cursor:default;
  min-width:88px; color:var(--graphite); transition:color .18s,background .18s;
}
.run__rail li:first-child{ border-left:0 }
.run__rail li.on{ color:var(--paper-2); cursor:pointer }
.run__rail li.now{ background:var(--red-wash); color:var(--paper) }
.run__rail li i{ display:block; font:500 10px/1 var(--mono); letter-spacing:.14em;
  font-style:normal; color:var(--red-txt); margin-bottom:6px }
.run__rail li b{ font:600 12.5px/1.3 var(--sans); display:block }

.run__stage{ padding:clamp(18px,3vw,30px); min-height:230px }
.run__card{ max-width:70ch }
.run__no{ margin:0 0 6px; font:500 10.5px/1 var(--mono); letter-spacing:.16em; color:var(--graphite) }
.run__card h4{ margin:0 0 .5em; font:600 clamp(19px,2.3vw,24px)/1.2 var(--sans); letter-spacing:-.015em }
.run__d{ margin:0 0 14px; color:var(--steel) }
.run__out{ margin:0 0 14px; font:400 13px/1.6 var(--mono); color:var(--paper-2) }
.run__out b{ font-weight:500 }
.run__teach{ margin:0; padding-left:14px; border-left:2px solid var(--red);
  color:var(--steel); font-size:14.5px; max-width:60ch }

/* the exceptions */
.exc{ border:1px solid var(--line); margin-top:18px; padding:16px 18px; background:var(--panel-2) }
.exc.ok { border-color:#3f6b4c }
.exc.bad{ border-color:var(--red) }
.exc__flag{ margin:0 0 8px; font:600 10.5px/1 var(--mono); letter-spacing:.14em;
  text-transform:uppercase; color:var(--red-txt) }
.exc__who{ margin:0 0 8px; font:400 13.5px/1.5 var(--mono); color:var(--paper-2) }
.exc__amt{ font-variant-numeric:tabular-nums }
.exc__ctx{ margin:0 0 8px; color:var(--steel); font-size:15px; max-width:64ch }
.exc__why{ margin:0 0 14px; color:var(--steel); font-size:14px; max-width:64ch }
.exc__opts{ display:flex; flex-wrap:wrap; gap:8px }
.exc__opt{
  font:500 13px/1.3 var(--sans); text-align:left; background:none;
  border:1px solid var(--line); color:var(--paper-2); padding:10px 12px; cursor:pointer;
  transition:border-color .16s,color .16s,opacity .16s;
}
.exc__opt:hover:not(:disabled){ border-color:var(--red); color:#fff }
.exc__opt.picked{ border-color:var(--paper-2); color:#fff }
.exc__opt.dim{ opacity:.35 }
.exc__then{ margin:14px 0 0; font-size:14.5px; max-width:64ch; padding-left:14px;
  border-left:2px solid var(--line) }
.exc__then.ok { border-left-color:#3f6b4c; color:var(--paper-2) }
.exc__then.bad{ border-left-color:var(--red); color:var(--paper-2) }

/* what you are signing */
.sign{ border:1px solid var(--line); margin-top:18px; padding:16px 18px; background:var(--panel-2) }
.sign__grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(130px,1fr));
  gap:1px; background:var(--line-2); border:1px solid var(--line-2); margin:12px 0 }
.sign__grid div{ background:var(--panel); padding:12px 14px }
.sign__grid b{ display:block; font:600 22px/1 var(--mono); font-variant-numeric:tabular-nums; margin-bottom:6px }
.sign__grid span{ font:400 10px/1.4 var(--mono); letter-spacing:.13em;
  text-transform:uppercase; color:var(--steel) }
.sign__note{ margin:0; color:var(--steel); font-size:14px; max-width:64ch }

.skipped,.done{ max-width:66ch }
.skipped h4,.done h4{ margin:.4em 0 .6em; font:600 clamp(20px,2.6vw,27px)/1.18 var(--sans);
  letter-spacing:-.018em; text-wrap:balance }
.skipped h4{ color:var(--red-txt) }
.skipped p,.done p{ margin:0 0 12px; color:var(--steel) }
.done__grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr));
  gap:1px; background:var(--line-2); border:1px solid var(--line-2); margin-top:16px }
.done__grid div{ background:var(--panel); padding:14px }
.done__grid b{ display:block; font:600 24px/1 var(--mono); font-variant-numeric:tabular-nums; margin-bottom:6px }
.done__grid span{ font:400 10px/1.4 var(--mono); letter-spacing:.13em;
  text-transform:uppercase; color:var(--steel) }

.run__bar{ display:flex; gap:10px; flex-wrap:wrap; align-items:stretch;
  padding:0 clamp(18px,3vw,30px) clamp(18px,3vw,26px) }
.run__go{
  font:600 12px/1 var(--mono); letter-spacing:.14em; text-transform:uppercase;
  background:var(--paper); color:var(--ink); border:0; padding:15px 20px; cursor:pointer;
}
.run__go:hover:not(:disabled){ background:var(--red); color:#fff }
.run__go:disabled{ opacity:.4; cursor:not-allowed }
/* The shortcut is a real option, so it looks like one -- but it does not look
   like the recommended one. Ghosted, not hidden, and not styled as a trap. */
.run__skip{
  font:500 11px/1.25 var(--mono); letter-spacing:.1em; text-transform:uppercase;
  background:none; color:var(--graphite); border:1px dashed var(--line-2);
  padding:11px 16px; cursor:pointer; text-align:left;
}
.run__skip span{ display:block; text-transform:none; letter-spacing:0;
  font-size:11px; opacity:.7; margin-top:3px }
.run__skip:hover{ color:var(--red-txt); border-color:var(--red) }
.run__reset{
  font:500 11px/1 var(--mono); letter-spacing:.12em; text-transform:uppercase;
  background:none; color:var(--paper-2); border:1px solid var(--line);
  padding:13px 16px; cursor:pointer;
}
.run__reset:hover{ border-color:var(--red); color:#fff }
@media (max-width:640px){
  .run__rail{ grid-auto-flow:row; grid-auto-columns:auto }
  .run__rail li{ border-left:0; border-top:1px solid var(--line-2); display:flex; gap:10px; align-items:baseline }
  .run__rail li i{ margin:0 }
}

/* ============================================================
   THE WORKSHOP — the naming system, and the checklist
   The state chip is the only thing carrying color here, because
   the state is the only thing a reader is scanning for.
   ============================================================ */
.ws__note{margin:22px 0 0;color:var(--steel);font-size:14.5px;max-width:60ch}
.ws__note b{color:var(--paper-2)}
.ws{list-style:none;margin:clamp(24px,3.4vw,38px) 0 0;padding:0;
  display:grid;gap:1px;background:var(--line-2);border:1px solid var(--line-2)}
@media(min-width:820px){.ws{grid-template-columns:1fr 1fr}}
.ws li{background:var(--panel);padding:20px 22px 22px}
.ws__hd{display:flex;align-items:baseline;gap:12px;margin-bottom:12px;flex-wrap:wrap}
.ws__hd b{font:600 clamp(19px,2.2vw,23px)/1 var(--sans);letter-spacing:-.015em}
.ws__hd em{font-style:normal;font:600 9.5px/1 var(--mono);letter-spacing:.14em;
  text-transform:uppercase;padding:5px 7px;border:1px solid var(--line);color:var(--graphite)}
/* Built is stated plainly, not celebrated. Not started is stated just as
   plainly -- a checklist that hides its own gaps is a poster. */
/* the qualifier after the comma: same baseline, no border, quieter */
.ws__qual{font-style:normal;font:400 11px/1 var(--mono);letter-spacing:.1em;
  color:var(--graphite);text-transform:uppercase}
.ws li[data-state="built"] .ws__hd em{color:var(--ok);border-color:var(--ok)}
.ws li[data-state="deployed"] .ws__hd em{color:var(--red-txt);border-color:var(--red)}
.ws__real{margin:0 0 10px;color:var(--steel);font-size:14px;font-style:italic;max-width:52ch}
.ws__does{margin:0;color:var(--steel);font-size:15px;max-width:54ch}
.ws__proof{margin:12px 0 0;padding-left:12px;border-left:2px solid var(--red);
  font:400 13px/1.55 var(--mono);color:var(--paper-2);max-width:52ch}

/* ── HER LINEWORK, CARRIED FURTHER ────────────────────────────────────────
   Crystal's design, traced to vector and used as a pattern. The photograph
   of it is never published and that is not a detail I am going to forget.

   Four placements now instead of one, each at a different scale, rotation
   and crop so it reads as the same hand rather than the same sticker. The
   opacity ladder is deliberate: strongest at the turn of the document where
   it means something, faintest in the workshop where it is only texture.
   All four are masked, because an un-masked edge is what turns a motif into
   a watermark. */
#close,#contact,#workshop{position:relative;overflow:hidden}
/* CONTACT now opens with the orbit running full bleed, so the chapter itself
   carries no padding -- the body wrapper below the globe does. Without this
   the section's own top padding would push a full-bleed image down and leave
   a band of page colour above it, which is the seam we just removed coming
   straight back in a different place. */
#contact{padding-top:0}
.contact__body{padding-top:clamp(30px,3.6vw,54px)}
/* The orbit is the chapter's opening image, not a section any more: it loses
   the top border it used to need to separate itself from what came before,
   because what comes before is now the ask box and there is already a rule
   there. */
#contact > .orbit{margin-top:0}
#close > *:not(.strata),#contact > *:not(.strata),#workshop > *:not(.strata){
  position:relative;z-index:1}

.strata--her-close{
  top:-8%;left:-16%;height:min(118%,1020px);width:auto;opacity:0.187;
  transform:translate3d(0,var(--py,0px),0) rotate(6deg) scaleX(-1);
  -webkit-mask-image:radial-gradient(78% 96% at 30% 42%,#000 18%,transparent 76%);
  mask-image:radial-gradient(78% 96% at 30% 42%,#000 18%,transparent 76%)}

.strata--her-contact{
  bottom:-14%;right:-12%;height:min(104%,880px);width:auto;opacity:0.264;
  color:#e2564d;
  transform:translate3d(0,var(--py,0px),0) rotate(-9deg);
  -webkit-mask-image:radial-gradient(86% 92% at 64% 62%,#000 22%,transparent 80%);
  mask-image:radial-gradient(86% 92% at 64% 62%,#000 22%,transparent 80%)}

.strata--her-shop{
  top:22%;right:-22%;height:min(92%,760px);width:auto;opacity:0.132;
  transform:translate3d(0,var(--py,0px),0) rotate(3deg);
  -webkit-mask-image:linear-gradient(255deg,transparent 6%,#000 44%,#000 70%,transparent 98%);
  mask-image:linear-gradient(255deg,transparent 6%,#000 44%,#000 70%,transparent 98%)}

@media (max-width:860px){
  .strata--her-close{height:88%;left:-40%;opacity:0.154}
  .strata--her-contact{height:80%;right:-34%;opacity:0.22}
  .strata--her-shop{display:none}
}

/* The rate-limit note. Steel, not red: hitting the cap is the system working,
   not the system failing, and coloring it like an error would say otherwise. */
/* THE SWITCH
   Two positions, one rule, one rail. Not a toggle with an on and an off --
   both positions are legitimate answers, so neither is styled as the failure
   state. The selected one takes the paper; the other stays steel and readable,
   because a control you cannot read is a control nobody flips. */
.ask__mode{display:flex;flex-wrap:wrap;align-items:center;gap:0;
  margin:0 0 clamp(14px,1.8vw,20px)}
.ask__mode button{
  appearance:none;cursor:pointer;
  padding:9px 15px;border:1px solid var(--line-2);background:var(--panel);
  font:600 .68rem/1 var(--mono);letter-spacing:.19em;color:var(--steel);
  transition:color .16s var(--ease),background .16s var(--ease),
             border-color .16s var(--ease),box-shadow .16s var(--ease)}
.ask__mode button + button{margin-left:-1px}
.ask__mode button:hover{color:var(--paper);border-color:var(--steel);z-index:1;position:relative}
.ask__mode button[aria-pressed="true"]{
  color:var(--paper);background:var(--panel-3);border-color:var(--steel);
  box-shadow:var(--cast-1);z-index:2;position:relative}
/* the against side wears the file's one accent when it is the live one --
   this is the position that costs him something, so it should look like it */
.ask__mode button[data-mode="challenge"][aria-pressed="true"]{
  border-color:var(--red);background:var(--red-wash);color:var(--paper)}
/* A sibling of the switch now, not a flex child of it. As a child with
   flex-basis:100% it STILL sat on the same line, because max-width clamps the
   hypothetical main size before the browser decides where lines break -- so
   it saw a 568px item next to two buttons in 1150px and fitted them together.
   Measured, not guessed. Out here it simply cannot happen. */
.ask__hint{margin:14px 0 clamp(16px,2vw,22px);max-width:62ch;
  font:400 .85rem/1.6 var(--sans);color:var(--steel)}
@media(max-width:520px){
  .ask__mode button{flex:1 1 auto;padding:10px 8px;font-size:.62rem;letter-spacing:.12em}
}
@media (prefers-reduced-motion:reduce){.ask__mode button{transition:none}}

.ask__rate{
  margin:14px 0 0;padding-left:13px;border-left:2px solid var(--line);
  font:400 13px/1.6 var(--sans);color:var(--steel);max-width:60ch}

/* ============================================================
   25b. THE SLIDING FILTER (build gallery)
   ------------------------------------------------------------
   A graded veil sits over the photograph at rest and slides off
   to the right on hover, focus or tap. It is a real filter, not
   a picture of one: backdrop-filter desaturates and darkens
   whatever is still behind it, so the boundary between covered
   and revealed is a live edge moving across the image.

   It lives inside .fig__frame, which wraps ONLY the picture. The
   caption underneath is a sibling of the frame, so nothing below
   the image moves, reflows or changes line count while the veil
   travels. That is deliberate: a reveal that shoves the text
   around reads as a layout bug, not an effect.
   ============================================================ */
.builds .fig__frame{position:relative;overflow:hidden}

.builds .slide{
  position:absolute;inset:0;z-index:4;pointer-events:none;
  transform:translate3d(0,0,0);
  will-change:transform;
  transition:transform .58s cubic-bezier(.22,.61,.36,1);
  /* fallback for engines without backdrop-filter */
  background:linear-gradient(100deg,
    rgba(11,11,12,.62) 0%,rgba(11,11,12,.50) 45%,rgba(11,11,12,.42) 100%);
}
@supports (backdrop-filter: saturate(1)) or (-webkit-backdrop-filter: saturate(1)){
  .builds .slide{
    background:linear-gradient(100deg,
      rgba(11,11,12,.34) 0%,rgba(11,11,12,.22) 45%,rgba(11,11,12,.16) 100%);
    -webkit-backdrop-filter:saturate(.28) contrast(.94) brightness(.78);
    backdrop-filter:saturate(.28) contrast(.94) brightness(.78);
  }
}

/* the leading edge — a hairline so you can see the thing move */
.builds .slide i{
  position:absolute;top:0;bottom:0;right:0;width:2px;
  background:linear-gradient(180deg,
    rgba(217,31,38,0) 0%,var(--red) 22%,var(--red) 78%,rgba(217,31,38,0) 100%);
  opacity:.85;
}

.builds > figure:hover .slide,
.builds > figure:focus .slide,
.builds > figure:focus-within .slide,
.builds > figure.is-open .slide{transform:translate3d(101%,0,0)}

.builds > figure{outline:none}
.builds > figure:focus-visible{outline:2px solid var(--red);outline-offset:-2px}

@media (prefers-reduced-motion: reduce){
  .builds .slide{transition:opacity .2s linear}
  .builds > figure:hover .slide,
  .builds > figure:focus .slide,
  .builds > figure:focus-within .slide,
  .builds > figure.is-open .slide{transform:none;opacity:0}
}

/* ============================================================
   25c. BUILD GALLERY — GRID AND FRAME CORRECTIONS
   ------------------------------------------------------------
   Two things were wrong and both were mine.

   1. The fixed card height set back in section 25 was dead. The
      generic rule further down the file --
        .fig img, .builds img, .band img { height:auto }
      -- carries the same specificity as .builds img and sits
      later, so it won. Every photograph was rendering at its own
      aspect ratio: 444px, 359px, 345px, three different heights,
      three different caption baselines, nothing lining up. The
      fix is specificity, not another !important -- the frame is
      in the selector now, so this rule outranks the generic one
      honestly.

   2. Three cards in a two-column grid leaves a dead cell. auto-fit
      collapses the track that has nothing in it, so the row always
      fills exactly, at three items or at four.
   ============================================================ */
.builds{grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
@media(min-width:860px){.builds{grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}}

.builds .fig__frame img{
  height:clamp(220px,21vw,290px);
  object-fit:cover;object-position:center 55%;
}
/* the caption takes the slack so every panel in a row ends level,
   whatever the copy underneath happens to weigh */
.builds > figure{height:100%}
.builds figcaption{flex:1 1 auto}

/* ============================================================
   25d. THE GATE
   ------------------------------------------------------------
   Stands where the build gallery used to sit at the end of the
   Commonwealth chapter. Its whole job is to be skippable: a
   reader here for the finance record scans past it, a reader who
   wants to know what I make with my hands has somewhere to go.
   Bordered rather than filled, so it reads as a way out of the
   page and not as another card of content.
   Named .gate, not .door: the contact section already owns .door
   for its four mailto tiles, and two components sharing a class
   name is a collision waiting for whichever one loads last.
   ============================================================ */
.gate{display:block;margin-top:clamp(30px,4vw,48px);padding:clamp(20px,2.6vw,30px);
  border:1px solid var(--line);border-left:2px solid var(--red);
  text-decoration:none;color:inherit;background:var(--panel);
  transition:border-color .16s var(--ease),background .16s var(--ease)}
.gate:hover{border-color:var(--graphite);border-left-color:var(--red);background:#17171b}
.gate__k{display:block;font-family:var(--mono);font-size:.74rem;letter-spacing:.22em;
  color:var(--red-txt)}
.gate__t{display:block;margin-top:12px;max-width:70ch;font-size:.95rem;line-height:1.6;
  color:var(--steel)}
.gate__go{display:block;margin-top:16px;font-family:var(--mono);font-size:.8rem;
  letter-spacing:.10em;color:var(--paper-2)}
.gate__go i{font-style:normal;display:inline-block;transition:transform .16s var(--ease)}
.door:hover .gate__go i{transform:translateX(5px)}

/* header nav now carries all ten chapters, so it needs to be smaller and
   to drop its numbers before it drops its labels */


/* ============================================================
   25e. THE HANGING — museum treatment for the build gallery
   ------------------------------------------------------------
   His bar for photography on this site: good enough to hang in a
   museum. A museum does three things a web page usually does not.
   It mats the print instead of running it to the frame edge, so
   the picture has air around it and reads as an object rather
   than a background. It hangs everything in a set at the same
   size, so your eye compares the work and not the framing. And it
   puts the words on a small plate beside the piece, set quietly,
   in a different voice from the piece itself.

   All three are here. The grade is the fourth: one curve, one
   vignette shape, one saturation across all three photographs,
   done at build time in the files, not in CSS filters.
   ============================================================ */
.builds{background:transparent;border:0;gap:clamp(18px,2vw,28px)}

.builds > figure{
  background:var(--panel);
  border:1px solid var(--line);
  padding:clamp(14px,1.5vw,20px);          /* the mat */
  padding-bottom:0;
}
.builds .fig__frame{border:1px solid #000;background:#000}

/* the plate */
.builds figcaption{
  border-top:0;
  padding:clamp(16px,1.8vw,22px) 2px clamp(18px,2vw,24px);
  font-size:.85rem;line-height:1.62;color:var(--steel);
  border-bottom:2px solid var(--red);
  margin:0 -2px;
}
.builds figcaption b{
  display:block;margin-bottom:7px;
  font-family:var(--mono);font-size:.72rem;letter-spacing:.20em;
  font-weight:500;color:var(--paper-2);text-transform:uppercase;
}

/* ============================================================
   26b. WHERE TO NEXT
   ------------------------------------------------------------
   Three doors at the bottom of any page that is not the dossier
   itself. Sized so the whole set is one glance, not a menu you
   have to read. The "back to top" line stays, quietly, underneath
   -- somebody who wants it can still have it.
   ============================================================ */
.ways{margin-top:clamp(46px,6vw,86px);padding-top:clamp(26px,3vw,38px);
  border-top:1px solid var(--line)}
.ways__grid{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);
  margin-top:clamp(16px,2vw,24px);grid-template-columns:repeat(auto-fit,minmax(250px,1fr))}
.ways__grid a{display:block;padding:clamp(18px,2.2vw,26px);background:var(--panel);
  text-decoration:none;color:inherit;
  transition:background .16s var(--ease)}
.ways__grid a:hover{background:#17171b}
.ways__k{display:block;font-family:var(--mono);font-size:.68rem;letter-spacing:.22em;
  color:var(--red-txt)}
.ways__grid b{display:block;margin-top:11px;font-size:1.02rem;font-weight:600;
  color:var(--paper-2);line-height:1.3}
.ways__d{display:block;margin-top:9px;font-size:.85rem;line-height:1.58;color:var(--steel)}
.ways__back{margin-top:clamp(16px,2vw,22px);font-family:var(--mono);font-size:.76rem;
  letter-spacing:.10em}
.ways__back a{color:var(--graphite);text-decoration:none}
.ways__back a:hover{color:var(--steel)}

/* ============================================================
   HEADER  —  the drawer
   ------------------------------------------------------------
   What was here before: ten chapters as two rows of small grey
   type, the wordmark carrying nothing, and the widest part of
   the page spent on the least useful thing on it.

   Two category buttons instead, split where the page splits,
   each dropping a set of cards -- number, name, and a line
   saying what is actually in there. The space that saves goes
   to a readout of where you currently are, which is the one
   piece of navigation a long single-page file genuinely needs.

   Nothing here is a hover-only menu. It opens on click and
   stays open until you choose, click away, or press Escape,
   because a panel that vanishes when the pointer crosses a
   two-pixel gap is a menu you have to be careful around.
   ============================================================ */
.hdr__nav{flex:1 1 auto;display:flex;align-items:center;
  gap:clamp(10px,1.6vw,26px);min-width:0;margin-left:clamp(16px,3vw,52px)}
.hdr__menu{display:flex;align-items:stretch;gap:2px}

.drop{position:relative;display:flex}
.drop__btn{
  display:inline-flex;align-items:center;gap:.85em;
  height:64px;padding:0 clamp(10px,1.1vw,16px);
  border:0;background:none;cursor:pointer;
  font:600 .74rem/1 var(--mono);letter-spacing:.19em;
  color:var(--steel);white-space:nowrap;
  transition:color .16s var(--ease)}
.drop__btn i{font-style:normal;font-weight:400;font-size:.9em;letter-spacing:.06em;
  color:var(--graphite);transition:color .16s var(--ease)}
/* the caret. Drawn, not typed -- a glyph at this size renders differently in
   every family and one of them is always a quarter pixel off the baseline. */
.drop__btn s{width:7px;height:7px;text-decoration:none;
  border-right:1.4px solid currentColor;border-bottom:1.4px solid currentColor;
  transform:translateY(-2px) rotate(45deg);
  transition:transform .22s var(--ease),border-color .16s var(--ease);opacity:.7}
.drop__btn:hover,.drop__btn[aria-expanded="true"]{color:var(--paper)}
.drop__btn:hover i,.drop__btn[aria-expanded="true"] i{color:var(--red-txt)}
.drop__btn[aria-expanded="true"] s{transform:translateY(1px) rotate(-135deg);opacity:1}
/* the chapter you are in lives in one of the two drawers; say which */
.drop__btn[data-in]{color:var(--paper)}
.drop__btn[data-in]::after{content:"";position:absolute;left:clamp(10px,1.1vw,16px);
  right:clamp(10px,1.1vw,16px);bottom:14px;height:1px;background:var(--red)}

.drop__panel{
  position:absolute;top:calc(100% + 1px);left:50%;transform:translateX(-50%);
  width:min(640px,calc(100vw - 40px));z-index:5;
  /* Translucent, like the header it hangs off. The page reading faintly
     through the panel is what makes it feel like a sheet laid over the
     document rather than a hole punched in it -- and it keeps the drawer
     tied to the bar above, which uses the same treatment.

     .955 with a 20px blur. It started at .88, which read as glass but let
     enough of the hero through to fight 12px mono type -- every point of
     alpha spent on atmosphere comes straight out of the cards' legibility.
     At .955 the page is still faintly there behind it and the words win. There is a solid fallback for
     browsers without backdrop-filter, because a see-through panel with no
     blur behind it is just unreadable text over a photograph. */
  background:var(--ink-2);border:1px solid var(--line);
  border-top:1px solid var(--red-line);
  box-shadow:0 1px 0 rgba(255,255,255,.05) inset, var(--cast-3);
  padding:8px}
@supports (backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px)){
  .drop__panel{background:rgba(15,15,17,.955);
    -webkit-backdrop-filter:blur(20px) saturate(1.15);
            backdrop-filter:blur(20px) saturate(1.15)}
}
.drop__grid{display:grid;grid-template-columns:1fr 1fr;gap:2px}

.ncard{
  display:grid;grid-template-columns:auto 1fr;grid-template-rows:auto auto;
  column-gap:.9em;row-gap:5px;align-items:baseline;
  padding:13px 14px;text-decoration:none;
  border:1px solid transparent;border-radius:var(--r);
  transition:background .16s var(--ease),border-color .16s var(--ease)}
/* five cards in a two-column grid leaves one odd; give it the full width
   rather than a hole, so the panel reads as a set instead of a mistake. */
.ncard:nth-child(5){grid-column:1 / -1}
.ncard i{grid-row:1;font-style:normal;font:400 .7rem/1 var(--mono);
  letter-spacing:.1em;color:var(--graphite);transition:color .16s var(--ease)}
.ncard b{grid-row:1;font:600 .84rem/1 var(--mono);letter-spacing:.13em;
  text-transform:uppercase;color:var(--paper);white-space:nowrap}
.ncard span{grid-row:2;grid-column:2;font:400 .78rem/1.45 var(--sans);
  color:var(--graphite);letter-spacing:0}
/* THE DROP
   Cards fall into the tray rather than the panel fading in as one block.

   Fast, and then stopped -- not eased for a long time, which reads as syrup.
   The curve was picked by evaluating candidates rather than by eye, because
   "how far past does it go" is arithmetic and guessing at it wastes a round
   trip. cubic-bezier(.2,.7,.25,1.4) over 20px of throw:

     90% of the distance covered by 29% of the duration   <- the fast part
     peaks 2.34px past rest at 62%                        <- the landing
     settles back to zero                                 <- the stop

   The first version of this was .17,.89,.32,1.12, which measured out at a
   0.47px overshoot -- arithmetically an overshoot and visually nothing at
   all. The overshoot is the whole point: an object with mass arriving at a
   detent goes slightly past and comes back. Half a pixel is a rounding
   error; two is a card landing.

   34ms apart, left to right. Enough that the eye reads five separate cards
   landing rather than one sheet; little enough that the whole row is down in
   under a third of a second and nobody is waiting on it.

   The panel toggles `hidden`, so it goes through display:none between opens
   -- which restarts these animations for free, every time, with no class to
   add and no state to reset. */
@keyframes ncard-drop{
  from{opacity:0;transform:translate3d(0,-20px,0)}
  to  {opacity:1;transform:none}
}
.drop__panel:not([hidden]) .ncard{
  animation:ncard-drop .24s cubic-bezier(.2,.7,.25,1.4) both}
.drop__panel:not([hidden]) .ncard:nth-child(2){animation-delay:.034s}
.drop__panel:not([hidden]) .ncard:nth-child(3){animation-delay:.068s}
.drop__panel:not([hidden]) .ncard:nth-child(4){animation-delay:.102s}
.drop__panel:not([hidden]) .ncard:nth-child(5){animation-delay:.136s}
/* the tray itself arrives first and instantly, so the cards land INTO
   something rather than materialising alongside it */
@keyframes drop-tray{from{opacity:0}to{opacity:1}}
.drop__panel:not([hidden]){animation:drop-tray .09s linear both}
@media (prefers-reduced-motion:reduce){
  .drop__panel:not([hidden]),
  .drop__panel:not([hidden]) .ncard{animation:none}
}

.ncard:hover,.ncard:focus-visible{background:var(--panel);border-color:var(--line)}
.ncard:hover i,.ncard:focus-visible i{color:var(--red-txt)}
.ncard:hover span,.ncard:focus-visible span{color:var(--steel)}
.ncard[aria-current="true"]{background:var(--red-wash);border-color:var(--red-line)}
.ncard[aria-current="true"] i{color:var(--red-txt)}

/* Where you are. Empty until the scrollspy fills it, and it takes the
   leftover width rather than a fixed one, so a long chapter name pushes
   nothing off the bar -- it just truncates, which is what a readout should
   do when it runs out of room. */
.hdr__now{margin:0 0 0 auto;padding-left:clamp(14px,2vw,26px);
  border-left:1px solid var(--line);
  min-width:0;display:none;align-items:baseline;gap:.8em;
  font:500 .72rem/1 var(--mono);letter-spacing:.16em;color:var(--graphite);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hdr__now.is-on{display:flex}
.hdr__now i{font-style:normal;color:var(--red-txt);font-weight:400}
.hdr__now span{color:var(--steel);text-transform:uppercase;
  overflow:hidden;text-overflow:ellipsis}

@media(max-width:1240px){ .hdr__now{display:none!important} }
@media(max-width:1000px){
  .hdr__menu{display:none}      /* MENU takes over -- see .hdr__k .sm */
}
@media(max-width:520px){
  .drop__panel{width:calc(100vw - 24px)}
  .drop__grid{grid-template-columns:1fr}
  .ncard:nth-child(5){grid-column:auto}
}
@media (prefers-reduced-motion:reduce){
  .drop__btn s,.drop__btn,.ncard{transition:none}
}
@media print{.hdr__menu,.hdr__now{display:none}}

/* ============================================================
   THE PATH — file-reference breadcrumb on inner pages
   ------------------------------------------------------------
   Sits between the header and the page. Mono, small, quiet, and
   it reads as a file reference rather than a website trail:
   AHB-1998 · 03 BUILT SYSTEMS · JEA-001.
   ============================================================ */
.crumb{border-bottom:1px solid var(--line);background:var(--ink-2)}
.crumb__in{display:flex;align-items:center;flex-wrap:wrap;gap:0;
  padding-block:11px;font:500 11px/1.6 var(--mono);letter-spacing:.15em}
.crumb a{color:var(--steel);text-decoration:none;transition:color .16s var(--ease)}
.crumb a:hover{color:var(--paper)}
.crumb a i{font-style:normal;color:var(--graphite);margin-right:.5em}
.crumb a:hover i{color:var(--red-txt)}
.crumb s{text-decoration:none;color:var(--graphite);margin:0 .85em}
.crumb b{color:var(--paper-2);font-weight:600}
.crumb__none{color:var(--graphite)}

/* ============================================================
   THE WORKSHOP MARKS — eight structures, one drawing hand
   ------------------------------------------------------------
   Each tool is named after a real structure, so each one gets
   drawn. The rule that keeps eight marks looking like a set
   rather than eight clip-art icons is that they share
   everything except the subject: one 48-unit grid, one stroke
   weight, one cap and join, no fills, and currentColor -- which
   means each mark inherits the state colour of the card it sits
   in. Built is green, deployed is red, not started is quiet,
   and I did not have to draw three versions of anything.
   ============================================================ */
.ws__mark{flex:0 0 auto;width:34px;height:34px;display:block;
  color:var(--graphite);transition:color .2s var(--ease)}
.ws li[data-state="built"] .ws__mark{color:var(--ok)}
.ws li[data-state="deployed"] .ws__mark{color:var(--red-txt)}
.ws__mark svg{width:100%;height:100%;display:block;
  fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke}
.ws__hd{align-items:center}

/* ============================================================
   DEPTH
   ------------------------------------------------------------
   The page was flat: every panel sat on the same plane at the
   same value, so nothing told your eye what was in front of
   what. Depth here is not drop shadows on everything -- that is
   how a dark interface turns to mud. It is three things a real
   scene has: surfaces at different heights catch different
   amounts of light, an edge nearest the light is brighter than
   the edge away from it, and the thing you are pointing at
   lifts while the things around it stay put.

   So: a one-pixel top highlight on raised panels (the lit edge),
   a soft shadow beneath (the occlusion), and a lift on hover for
   the things that are actually interactive. Everything else
   stays flat on purpose, because if all of it is raised then
   none of it is.
   ============================================================ */
:root{
  /* ONE ELEVATION SCALE.

     Seven different hand-written box-shadows had accumulated -- .62 here,
     .7 there, -18px on one and -12px on the next -- so nothing on the page
     agreed about how far off the surface it was sitting. These are the only
     three, all of them transparent black so they darken whatever they land
     on instead of painting grey over it, and every layer that floats above
     the page takes one.

     Two shadows in each: a tight one that draws the object's own edge, and a
     wide soft one that puts distance under it. On a near-black page the tight
     one is what actually separates the panel from the ground -- a single soft
     shadow just dissolves. */
  --cast-1:0 2px 6px rgba(0,0,0,.34), 0 8px 20px -6px rgba(0,0,0,.46);
  --cast-2:0 6px 16px rgba(0,0,0,.40), 0 18px 44px -12px rgba(0,0,0,.60);
  --cast-3:0 10px 24px rgba(0,0,0,.46), 0 34px 80px -20px rgba(0,0,0,.72);

  --lift-1:0 1px 0 rgba(255,255,255,.045) inset,
           0 2px 6px rgba(0,0,0,.35),
           0 8px 24px rgba(0,0,0,.22);
  --lift-2:0 1px 0 rgba(255,255,255,.07) inset,
           0 4px 12px rgba(0,0,0,.45),
           0 16px 44px rgba(0,0,0,.30);
}
.ws li,
.builds > figure,
.ways__grid a,
.gate{box-shadow:var(--lift-1);transition:box-shadow .22s var(--ease),
                                          transform .22s var(--ease),
                                          background .16s var(--ease)}
.ways__grid a:hover,
.gate:hover{box-shadow:var(--lift-2);transform:translateY(-2px)}
.builds > figure:hover{box-shadow:var(--lift-2)}

/* The chapter bands and the header sit ABOVE the page rather than
   on it, so they get the shadow on the underside only. */
.hdr{box-shadow:0 1px 0 rgba(255,255,255,.04), var(--cast-1)}
.band{box-shadow:inset 0 1px 0 rgba(255,255,255,.05),
                 inset 0 -1px 0 rgba(255,255,255,.03)}

@media (prefers-reduced-motion: reduce){
  .ways__grid a:hover,.gate:hover{transform:none}
}

/* the drawn band: no photograph to sit behind, so the scrim is lighter and
   the linework carries it */
/* The band took its height from the <img> inside it. There is no img any
   more, so it has to state its own -- same clamp the photograph used, or the
   whole band collapses to nothing and takes the caption with it. */
.band--drawn{background:var(--ink);height:clamp(200px,28vw,340px)}
.band--drawn .tower{position:absolute;inset:0;width:100%;height:100%;
  color:var(--steel);opacity:.9}
.band--drawn::after{background:linear-gradient(90deg,
  rgba(11,11,12,.97) 0%,rgba(11,11,12,.86) 34%,
  rgba(11,11,12,.10) 72%,rgba(11,11,12,.50) 100%)}

/* The caption sits bottom-left over open sky, which is the brightest part of
   this photograph. The 90deg scrim alone does not carry it, so a second one
   runs bottom-up underneath the text only. */
.band::before{content:"";position:absolute;inset:auto 0 0 0;height:62%;z-index:2;
  pointer-events:none;
  background:linear-gradient(0deg,rgba(11,11,12,.92) 0%,rgba(11,11,12,.62) 38%,
                                  rgba(11,11,12,0) 100%)}

/* Her linework over the chapter band. Faintest placement on the site — the
   photograph is doing the work here and the motif is only meant to be the
   same hand showing up again, not a watermark. Masked, as all four of the
   others are: an un-masked edge is what turns a motif into a sticker. */
.band{position:relative;overflow:hidden}
.strata--her-band{
  top:-30%;right:-8%;left:auto;height:min(190%,760px);width:auto;opacity:0.154;
  transform:translate3d(0,var(--py,0px),0) rotate(-9deg);
  -webkit-mask-image:radial-gradient(70% 90% at 62% 46%,#000 12%,transparent 74%);
          mask-image:radial-gradient(70% 90% at 62% 46%,#000 12%,transparent 74%);
}
/* NOT `.band > *:not(.strata){position:relative}`. That is what I wrote first
   and it clobbered `.band__cap{position:absolute;inset:auto 0 0 0}`, so the
   caption stopped being pinned to the bottom and flowed to the top of the
   band, over the brightest part of the sky, unreadable. Only the frame needs
   lifting above the motif; the caption already sets its own z-index. */
.band > .fig__frame{z-index:1}
.band__cap{z-index:3}

/* SMEAR
   Two levels, so the browser re-rasterises when you cross a threshold rather
   than on every frame of a flick. Isotropic blur rather than an SVG
   directional filter: at these radii, in motion, nobody can tell the
   difference between a vertical streak and a soft one, and CSS blur is an
   order of magnitude better optimised than filter:url() on a large image.

   The transition is asymmetric on purpose. Blur arrives instantly, because
   motion blur that fades IN is a lens rack rather than movement. It leaves
   over 160ms, because a hard cut back to sharp reads as a glitch.

   will-change is NOT set here. It would promote every photograph on the page
   to its own layer permanently, for an effect that runs for a fraction of a
   second -- memory spent all the time to save work almost none of the time. */
.smear-1{filter:blur(2.6px)}
.smear-2{filter:blur(6px)}
.fig__frame img{transition:filter .16s linear}
.smear-1,.smear-2{transition:none}
@media (prefers-reduced-motion:reduce){
  .smear-1,.smear-2{filter:none}
}
@media print{.smear-1,.smear-2{filter:none}}

/* THE LIVE ONE, OVER THE STILL
   The canvas sits exactly on the frame at z-index 2 — above the photograph,
   below the caption. It is transparent until the shader has actually drawn a
   frame, so a slow compile shows the still rather than a hole, and a browser
   that cannot run it never reveals an empty rectangle at all. The fade is
   long on purpose: a hard cut between two nearly identical images reads as a
   glitch, a slow one reads as the picture coming to life. */
/* THE REVEAL
   A band comes up out of black as you reach it, the way a projector does
   rather than the way a web page does.

   Three rules keep it from becoming a liability. It is armed by script, so
   with no JavaScript there is never a black rectangle sitting over a picture.
   It is only armed on bands that are BELOW the fold at load, so one already
   on screen is never blacked out just to fade it back in — which would be a
   worse first impression than no effect at all. And it covers the caption
   too: the whole frame arrives at once, because revealing a picture from
   under text that was already sitting there is not a reveal, it is a wipe. */
.band__veil{position:absolute;inset:0;z-index:5;display:block;
  background:var(--ink);opacity:0;pointer-events:none;
  transition:opacity 1.5s var(--ease)}
.band.is-armed .band__veil{opacity:1;transition:none}
.band.is-seen  .band__veil{opacity:0;transition:opacity 1.5s var(--ease)}
@media (prefers-reduced-motion:reduce){.band__veil{display:none}}
@media print{.band__veil{display:none}}

/* The band's controls. Bottom right, opposite the caption, so they read as
   instrument rather than as part of the writing. Invisible until fx.js says
   the shader is actually running -- PAUSE on a still frame would be a lie,
   and the still is what a browser without WebGL gets. */
.band__ctl{position:absolute;right:clamp(18px,3vw,40px);bottom:clamp(18px,2.6vw,32px);
  z-index:4;display:flex;gap:10px;align-items:center;
  opacity:0;pointer-events:none;transition:opacity .5s var(--ease)}
.band.is-live .band__ctl{opacity:1;pointer-events:auto}
.band__btn{display:inline-flex;align-items:center;gap:.7em;cursor:pointer;
  padding:9px 13px;border:1px solid rgba(255,255,255,.18);
  background:rgba(8,8,10,.55);backdrop-filter:blur(8px);
  font:600 10px/1 var(--mono);letter-spacing:.16em;color:var(--paper);
  text-decoration:none;transition:background .18s var(--ease),border-color .18s var(--ease)}
.band__btn:hover,.band__btn:focus-visible{background:rgba(8,8,10,.85);border-color:var(--steel)}
.band__btn b{font-weight:400;font-size:9px;color:var(--red-txt);letter-spacing:0}
@media(max-width:620px){.band__ctl{gap:7px}.band__btn{padding:8px 10px;font-size:9px;letter-spacing:.1em}}
@media print{.band__ctl{display:none}}

.band__gl{position:absolute;inset:0;width:100%;height:100%;display:block;
  z-index:2;opacity:0;pointer-events:none;
  transition:opacity 1.1s var(--ease)}
.band.is-live .band__gl{opacity:1}
@media (prefers-reduced-motion:reduce){.band__gl{display:none}}
@media print{.band__gl{display:none}}

/* THIRD time this exact bug. `.fig img, .builds img, .band img{height:auto}`
   sits later in this file than `.band img{height:clamp(180px,28vw,340px)}` at
   the SAME specificity, so height:auto wins and the band has been rendering
   at the photograph's natural aspect — 597px tall at 1440 wide instead of the
   340 it was designed for. Nothing errors; the band is just too big.
   Fixed the honest way, by putting the frame in the selector so this rule
   genuinely outranks the generic one, not with !important. */
/* Bigger, because of where it sits now. This is no longer an image dropped
   between two unrelated chapters -- it is the hinge of the whole document,
   the point where THE WORK becomes THE RECORD, and it is the one piece on
   the page that is running live. 420px was a divider's height. This is a
   moment's height.

   The poster/shader framing survives the change for free: u_fit is
   min(1, 2.5*h/w), and 2.5 is the capture's own aspect, so it switches
   regimes on its own as the box goes from wider-than-the-capture to
   taller. Both sides land on the same disc radius either way. */
.band{height:clamp(340px,44vw,660px)}
.band .fig__frame{position:absolute;inset:0;display:block;height:100%}
/* Centred, because the chapter band is the eclipse now and the corona is
   radial around a disc dead in the middle of the frame — crop off centre and
   you lose half the streamers on one side. The 20% that used to live here was
   for the fire tower, whose cab sits in the top fifth; that photograph has
   moved to /personal/, which sets its own crop. */
.band .fig__frame img{height:100%;width:100%;object-fit:cover;
  /* the crop travels with the picture now, declared beside it in content,
     so moving a photograph between bands does not mean moving a rule too */
  object-position:var(--crop,center 50%)}

/* ------------------------------------------------------------
   THE LEAD BAND  —  /personal/
   ------------------------------------------------------------
   Same construction as the chapter band, three differences: it
   is taller, because it is the first thing on the page rather
   than a divider inside one; it sits flush under the breadcrumb
   with no top margin; and the crop holds the middle instead of
   the top fifth, because the subject here is the fall of the
   water and the pool under it, not a cab in the sky.

   FOURTH time: this block sat ABOVE `.band{height:clamp(...)}`
   at the same specificity, so the generic rule won and the lead
   rendered at the chapter's 420 instead of its own 540. Same bug
   as the note below, one section later. It lives here now, after
   everything it has to beat, which is the fix that does not need
   an !important to hold.
   ------------------------------------------------------------ */
.band--lead{margin:0 0 0;height:clamp(300px,38vw,540px);border-radius:0}
/* the tower sits inside the page rather than leading it, so it gets air
   above and below and comes in a touch shorter */
.band--tower{margin:clamp(48px,6vw,88px) 0 0;height:clamp(260px,32vw,460px)}
.strata--her-tower{
  bottom:-16%;right:-8%;height:min(150%,600px);width:auto;opacity:0.154;
  transform:translate3d(0,var(--py,0px),0) rotate(-6deg);
  -webkit-mask-image:radial-gradient(76% 90% at 62% 52%,#000 14%,transparent 78%);
          mask-image:radial-gradient(76% 90% at 62% 52%,#000 14%,transparent 78%)}
@media(max-width:720px){.strata--her-tower{display:none}}
.band--lead .fig__frame img{object-position:var(--crop,center 52%)}
.band--lead .band__cap{padding-bottom:clamp(18px,2.4vw,30px)}
.band--lead .band__cap b{color:var(--red-txt)}
/* the eyebrow is one line or it is nothing. Broken across two it reads as
   two labels rather than one place. */
@media(min-width:721px){.band--lead .band__cap b{white-space:nowrap}}
/* a shade deeper than the chapter's, because there is more bright
   water under this text than there was sky under the other. */
.band--lead::before{height:72%;
  background:linear-gradient(0deg,rgba(11,11,12,.94) 0%,rgba(11,11,12,.70) 34%,rgba(11,11,12,0) 100%)}
.strata--her-lead{
  top:-18%;left:-10%;height:min(150%,620px);width:auto;opacity:0.132;
  transform:translate3d(0,var(--py,0px),0) rotate(7deg) scaleX(-1);
  -webkit-mask-image:radial-gradient(72% 88% at 40% 48%,#000 14%,transparent 76%);
          mask-image:radial-gradient(72% 88% at 40% 48%,#000 14%,transparent 76%)}
@media(max-width:720px){
  .band--lead{height:clamp(240px,52vw,340px)}
  .strata--her-lead{display:none}
}


/* ── 44 THE PORTRAIT DEVELOPS ─────────────────────────────────────────────
   The conceit of this whole page is a file, and the first photograph in a
   file is usually the one somebody clipped out of a newspaper. So the
   portrait arrives that way and then resolves into the actual picture:
   black, then a coarse dot screen on cream stock with the contrast blown
   out, then clean monochrome, then colour. Four states, one element.

   The dot screen is two offset radial gradients at a 3px pitch, which is
   what a newspaper halftone physically is -- ink dots on a staggered grid,
   coarse enough to see. hard-light rather than multiply, so the dots bite
   into the highlights instead of laying a flat grey over the whole frame.

   The filter list is written out in the same order in every keyframe.
   Browsers only interpolate a filter chain function by function; change
   the order or the length between two frames and it stops animating and
   starts cutting. The last frame is deliberately the site's standard
   photographic grade, so the portrait ends exactly where every other
   picture on the page sits rather than a shade brighter. */
.fig--portrait picture{overflow:hidden}

/* Two effects arriving on the same photograph at the same time read as one
   glitch. The develop is the moment here, so the highlight sweep stands
   down for this one figure. */
.fig--portrait .fig__frame::after{display:none}

/* The black it comes out of. */
.fig--portrait picture::before{
  content:'';position:absolute;inset:0;z-index:3;pointer-events:none;
  background:#050506;opacity:0}

/* The screen. Cream stock, staggered dots. */
.fig--portrait picture::after{
  content:'';position:absolute;inset:0;z-index:2;pointer-events:none;opacity:0;
  mix-blend-mode:hard-light;
  background-color:rgba(240,234,219,.20);
  background-image:
    radial-gradient(circle at 50% 50%,rgba(0,0,0,.88) 0 33%,transparent 35%),
    radial-gradient(circle at 50% 50%,rgba(0,0,0,.88) 0 33%,transparent 35%);
  background-size:3px 3px,3px 3px;
  background-position:0 0,1.5px 1.5px}

.fig--portrait.is-dev picture::before{animation:pt-black 2.4s var(--ease) both}
.fig--portrait.is-dev picture::after {animation:pt-screen 2.4s var(--ease) both}
.fig--portrait.is-dev img            {animation:pt-dev   2.4s var(--ease) both}

@keyframes pt-black{
  0%{opacity:1} 13%{opacity:1} 30%{opacity:0} 100%{opacity:0}}

/* The 1.5px slip is a press out of register for a beat before it settles.
   Small enough to feel like printing rather than like a broken layout. */
@keyframes pt-screen{
  0%  {opacity:0;transform:translateX(-1.5px)}
  13% {opacity:0;transform:translateX(-1.5px)}
  24% {opacity:1;transform:translateX(1.5px)}
  46% {opacity:1;transform:none}
  72% {opacity:0;transform:none}
  100%{opacity:0;transform:none}}

@keyframes pt-dev{
  0%  {filter:grayscale(1) sepia(.38) saturate(1) contrast(2.40) brightness(.06)}
  13% {filter:grayscale(1) sepia(.38) saturate(1) contrast(2.40) brightness(.10)}
  30% {filter:grayscale(1) sepia(.44) saturate(1) contrast(2.05) brightness(1.16)}
  50% {filter:grayscale(1) sepia(.30) saturate(1) contrast(1.55) brightness(1.05)}
  72% {filter:grayscale(1) sepia(0)   saturate(1) contrast(1.14) brightness(.99)}
  100%{filter:grayscale(0) sepia(0)   saturate(1.06) contrast(1.05) brightness(.97)}}

/* Reduced motion gets the end state and nothing else -- no black, no screen,
   no develop. It is the same photograph, it just does not perform. */
@media (prefers-reduced-motion:reduce){
  .fig--portrait picture::before,.fig--portrait picture::after{display:none}
  .fig--portrait.is-dev img{animation:none;
    filter:grayscale(0) sepia(0) saturate(1.06) contrast(1.05) brightness(.97)}
}

/* Armed, but not yet run. fx.js sets this the moment it boots, which is long
   before the profile section is scrolled to, so the picture is already black
   when it comes up the page rather than flashing monochrome and then going
   dark. Without JS the class never lands and the photograph simply sits
   there, graded, the way it did before any of this existed. */
.fig--portrait.is-armed picture::before{opacity:1}
.fig--portrait.is-armed img{
  filter:grayscale(1) sepia(.38) saturate(1) contrast(2.40) brightness(.06)}
@media (prefers-reduced-motion:reduce){
  .fig--portrait.is-armed img{
    filter:grayscale(0) sepia(0) saturate(1.06) contrast(1.05) brightness(.97)}
}

/* ── 45 THE SKY READOUT ───────────────────────────────────────────────────
   Under the eclipse: the two measurements that make it more than a pretty
   loop. The 10.7 cm solar flux, which sets the shape of the corona above,
   and the next eclipse over whoever is reading, computed in their browser
   from NASA's own elements.

   Set like an instrument panel rather than a card: a hairline grid, the
   numbers big and tabular, the sourcing small and always visible. The
   sourcing is not fine print here — an unsourced number on this site is
   the one thing the whole site argues against.

   Each measurement is ONE grid item containing its own label, number,
   sourcing and paragraph. The first version made them four siblings and let
   the grid place them, which put every paragraph on its own row with the
   other half of the panel empty beside it. A grid places what you give it. */
.sky{
  display:grid;gap:1px;background:var(--line);border:1px solid var(--line);
  margin-top:calc(-1 * clamp(40px,6vw,80px) + clamp(20px,2.6vw,30px))}
@media(min-width:900px){.sky{grid-template-columns:repeat(2,minmax(0,1fr))}}
.sky > *{min-width:0;background:#0e0e11;padding:clamp(16px,2.2vw,24px);margin:0}

.sky__b{display:flex;flex-direction:column;gap:7px}
.sky__k{margin:0;font:600 10px/1 var(--mono);letter-spacing:.2em;
  text-transform:uppercase;color:var(--steel)}
.sky__v{margin:0;font:400 1.02rem/1.25 var(--sans);color:var(--paper)}
.sky__v b{font:700 clamp(1.8rem,3.4vw,2.35rem)/1 var(--sans);letter-spacing:-.03em;
  color:var(--paper-2);font-variant-numeric:tabular-nums}
.sky__v em{font-style:normal;font:600 11px/1 var(--mono);letter-spacing:.14em;
  text-transform:uppercase;color:var(--graphite);margin-left:5px}
.sky__n{margin:0;font:400 12.5px/1.55 var(--sans);color:var(--graphite);max-width:44ch}
.sky__n b{color:var(--paper);font-weight:600}
/* The paragraph is the last thing in the block and gets the space above it,
   so the number and its sourcing stay one object and the prose reads as a
   note underneath rather than a caption crowding the figure. */
.sky__p{margin:9px 0 0;font:400 13.5px/1.68 var(--sans);color:#c8c7cd;max-width:52ch}
.sky__p b{color:var(--paper-2);font-weight:600}

/* Spans both columns, on the darkest ground in the panel. The `.sky > *`
   rule above is one class plus a universal and therefore MORE specific than
   a bare class, so this has to be written as a child selector or the darker
   background silently loses. Fourth time this exact trap has bitten. */
.sky > .sky__src{grid-column:1/-1;background:var(--ink);
  font:400 11.5px/1.7 var(--mono);letter-spacing:.01em;color:var(--graphite)}
.sky > .sky__src a{color:var(--steel);text-decoration:none;
  border-bottom:1px solid rgba(154,154,164,.3)}
.sky > .sky__src a:hover{color:var(--paper);border-bottom-color:var(--red)}

/* ── 46 INTERLOCK ─────────────────────────────────────────────────────────
   The named whole, above the list of parts. It is set as a plate rather than
   a card: a red rule at the top, the name large enough to be the second
   thing you read on the chapter after the heading, and the prose in one
   measured column. No border on four sides — a box around it would make it
   look like an eighth tool, which is the one thing it is not. */
.eng{margin-top:clamp(28px,3.6vw,44px);padding-top:clamp(20px,2.4vw,28px);
  border-top:2px solid var(--red)}
.eng__k{margin:0;font:600 10px/1 var(--mono);letter-spacing:.24em;
  text-transform:uppercase;color:var(--red-txt)}
.eng__n{margin:10px 0 0;font:800 clamp(1.9rem,4.6vw,3rem)/1 var(--sans);
  letter-spacing:-.035em;color:var(--paper-2)}
.eng__b{margin-top:clamp(16px,2vw,22px);display:grid;gap:14px;max-width:64ch}
.eng__b p{margin:0;font:400 1rem/1.68 var(--sans);color:#c8c7cd}
.eng__b b{color:var(--paper-2);font-weight:600}
/* The railway definition is the setup and reads as the quieter voice; the
   line about what it adds up to is the payoff and gets the weight. */
.eng__real{color:var(--graphite)!important}
.eng__proof{border-left:2px solid var(--line-2);padding-left:16px;
  color:var(--paper)!important}

/* ── 47 THE INTERLOCKING DIAGRAM ──────────────────────────────────────────
   Drawn in the same language as everything else on this site: hairlines,
   no fills to speak of, one accent, and every mark meaning something. The
   state of a route is carried by how the track is DRAWN — solid and lit for
   laid track, dashed and dark for surveyed — so the picture reads correctly
   before anybody finds the legend.

   It is 1000 units wide and it stays 1000 units wide. Scaling it to a phone
   would put 11px labels at 4px, which is a diagram nobody can read wearing a
   coat of paint that says otherwise. It scrolls sideways in its own box
   instead, and the list underneath carries the same information in words for
   anyone who would rather not swipe. */
.ilk{margin:clamp(26px,3.4vw,40px) 0 0}
.ilk__scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;
  background:#0e0e11;border:1px solid var(--line);padding:clamp(14px,2vw,22px)}
.ilk__scroll svg{display:block;max-width:none;height:auto;margin-inline:auto}

/* Defaults for the whole plate. Everything below is a deliberate departure. */
.ilk__scroll svg{color:var(--steel)}
.ilk text{font-family:var(--mono)}
.ilk__name{font-size:13px;font-weight:600;letter-spacing:.12em;fill:var(--paper)}
/* `.ilk text` is a class AND an element, so it is MORE specific than a bare
   class and it was quietly winning: every route description rendered in the
   mono face, about 20% wider than it was measured for, which ran the long
   ones under the signal masts and pushed the output line off the edge of the
   viewBox. Fifth time this exact trap has cost me an hour. Same fix as the
   others: put the element in the selector rather than reaching for !important.
   The layout arithmetic here assumes the sans face -- it is not cosmetic. */
.ilk text.ilk__asks{font-family:var(--sans);font-size:11.5px;
  letter-spacing:.005em;fill:var(--graphite)}
.ilk__eng{font-size:13px;font-weight:700;letter-spacing:.22em;fill:var(--paper-2)}
.ilk__outk{font-size:11px;font-weight:600;letter-spacing:.2em;fill:var(--red-txt)}

/* Track. Laid track is solid and the ties show; a surveyed route is a dashed
   line with no ties on it, because there are no ties on it. */
.ilk__trk{fill:none;stroke:var(--steel);stroke-width:2}
.ilk__ties line{stroke:var(--line-2);stroke-width:1.4}
.ilk__sig line{stroke:var(--steel);stroke-width:1.6}
.ilk__sig circle{fill:none;stroke:var(--graphite);stroke-width:1.6}

.ilk__r.is-laid .ilk__trk{stroke:var(--paper)}
.ilk__r.is-laid .ilk__sig circle{fill:var(--red);stroke:var(--red)}
.ilk__r.is-laid .ilk__sig line{stroke:var(--paper)}

.ilk__r.is-planned{color:var(--graphite)}
.ilk__r.is-planned .ilk__trk{stroke:var(--line-2);stroke-dasharray:5 6}
.ilk__r.is-planned .ilk__ties{display:none}
.ilk__r.is-planned .ilk__name{fill:var(--graphite);font-weight:500}
.ilk__r.is-planned .ilk__sig line{stroke:var(--line-2);stroke-dasharray:3 3}

/* The bed. A trapezoid section with stone in it, the way it is drawn on a
   track profile — and under everything, which is the whole point of it. */
.ilk__bedfill{fill:url(#ilkBallast);stroke:none}
.ilk__bed line{stroke:var(--line-2);stroke-width:1.4}
.ilk__bed circle{fill:var(--line-2)}
.ilk pattern circle{fill:#3a3a44}

/* The plant. */
.ilk__plant rect{fill:#131316;stroke:var(--red);stroke-width:1.4}
.ilk__rule{stroke:var(--line-2);stroke-width:1}
.ilk__lev line{stroke:var(--steel);stroke-width:2;stroke-linecap:round}
.ilk__out{fill:none;stroke:var(--paper);stroke-width:2}
.ilk__head{fill:var(--paper);stroke:none}

.ilk__cap{margin-top:14px;display:flex;flex-wrap:wrap;gap:6px 26px;
  font:400 12px/1.6 var(--sans);color:var(--graphite)}
.ilk__cap span{display:flex;align-items:center;gap:9px}
.ilk__key{width:9px;height:9px;border-radius:50%;flex:0 0 auto;
  border:1.5px solid var(--graphite)}
.ilk__key--on{background:var(--red);border-color:var(--red)}

/* ============================================================
   THE FRONT DOOR  (/)
   ============================================================

   Everything here is prefixed fd- and nothing else on the site uses
   the prefix. That is on purpose. The front door is the one page that
   is NOT the dossier -- no rail, no chapter drawer, no scrollspy -- so
   it gets its own small vocabulary rather than borrowing classes that
   carry the dossier's behaviour with them. Grepping fd- tells you
   everything this page can touch, which is the same reason the tool
   marks are one folder and not scattered through the tree.

   The tokens are the site's. Only the layout is new.
   ============================================================ */

.fd-hdr{border-bottom:1px solid var(--line);position:sticky;top:0;z-index:20;
  background:rgba(11,11,12,.92);backdrop-filter:blur(8px)}
.fd-bar{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:18px 0}
.fd-wm{font-family:var(--display);font-weight:800;letter-spacing:-.035em;line-height:1;
  display:inline-flex;align-items:baseline;white-space:nowrap;font-size:23px;text-decoration:none}
.fd-wm i{font-style:normal;color:var(--paper)}
.fd-wm em{font-style:normal;color:var(--red-txt)}
.fd-wm sup{font-size:.30em;font-weight:600;letter-spacing:0;color:var(--red-txt);opacity:.85;
  position:relative;top:-.92em;left:.10em}
.fd-nav{display:flex;align-items:center;gap:30px}
.fd-nav a{font-family:var(--mono);font-size:10.5px;font-weight:500;text-transform:uppercase;
  letter-spacing:.24em;color:var(--steel);text-decoration:none;transition:color .2s var(--ease)}
.fd-nav a:hover{color:var(--paper)}
.fd-nav a.fd-key{color:var(--paper);border:1px solid var(--line-2);padding:9px 15px}
.fd-nav a.fd-key:hover{border-color:var(--red)}
/* Below 720 the secondary links go and the wordmark carries the bar on its
   own. A five-item mono nav at 320px wraps into a paragraph. */
@media(max-width:720px){.fd-nav{gap:18px}.fd-nav a:not(.fd-key){display:none}}

/* The red wash runs left to right rather than top to bottom so the weight
   lands under the sign-in corner instead of under the headline. */
.fd-hero{position:relative;border-bottom:1px solid var(--line);overflow:hidden;
  background-image:linear-gradient(to right,rgba(217,31,38,0) 0%,rgba(217,31,38,0) 30%,
    rgba(217,31,38,.045) 45%,rgba(217,31,38,.09) 62%,rgba(217,31,38,.135) 80%,rgba(217,31,38,.17) 100%)}
.fd-hero>.wrap{padding-top:clamp(56px,9vw,116px);padding-bottom:clamp(48px,7vw,96px);position:relative}
.fd-eyebrow{font-family:var(--mono);font-weight:500;font-size:10.5px;text-transform:uppercase;
  letter-spacing:.28em;color:var(--steel);display:flex;align-items:center;gap:14px;margin:0}
.fd-eyebrow::after{content:"";height:1px;background:var(--line);flex:1}
.fd-eyebrow b{color:var(--red-txt);font-weight:500}
.fd-h1{font-family:var(--serif);font-weight:400;font-size:clamp(38px,6.4vw,78px);line-height:1.04;
  letter-spacing:-.012em;margin:30px 0 0;max-width:19ch;text-wrap:balance;color:var(--steel)}
.fd-h1 b{font-weight:400;color:var(--paper)}
.fd-sub{margin:26px 0 0;max-width:56ch;color:#c8c5c8;font-size:clamp(15.5px,1.4vw,17.5px)}
.fd-sub b{color:var(--paper);font-weight:500}
.fd-rule{height:3px;width:64px;background:var(--red);margin:34px 0 0;border:0}

/* Two doors, one hairline between them. The gap IS the border: a 1px grid gap
   over a --line background, so the rule never doubles at the seam and never
   goes missing at a fractional device pixel. */
.fd-doors{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:var(--line);
  border:1px solid var(--line);margin-top:clamp(44px,6vw,72px)}
@media(max-width:800px){.fd-doors{grid-template-columns:1fr}}
.fd-door{background:var(--panel);padding:clamp(26px,3.4vw,42px);text-decoration:none;display:block;
  transition:background .25s var(--ease)}
.fd-door:hover{background:var(--panel-2)}
.fd-door .fd-n{font-family:var(--mono);font-size:10px;letter-spacing:.26em;color:var(--red-txt);
  text-transform:uppercase}
.fd-door h2{font-family:var(--display);font-weight:800;letter-spacing:-.02em;
  font-size:clamp(24px,2.6vw,32px);margin:16px 0 0;line-height:1.1;color:var(--paper)}
.fd-door p{color:var(--steel);margin:12px 0 0;font-size:15px;max-width:40ch}
.fd-go{font-family:var(--mono);font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;
  color:var(--paper);margin-top:24px;display:inline-flex;align-items:center;gap:10px}
.fd-go s{text-decoration:none;color:var(--red-txt);transition:transform .25s var(--ease);
  display:inline-block}
.fd-door:hover .fd-go s{transform:translateX(5px)}

.fd-sec{padding:var(--sec) 0}
.fd-sec--tight{padding-top:0}
.fd-tag{font-family:var(--mono);font-weight:500;font-size:10.5px;text-transform:uppercase;
  letter-spacing:.28em;color:var(--steel);display:flex;align-items:center;gap:14px;margin:0 0 26px}
.fd-tag b{color:var(--red-txt);font-weight:500}
.fd-tag::after{content:"";height:1px;background:var(--line);flex:1}
.fd-lede{font-family:var(--serif);font-weight:400;font-size:clamp(25px,3.1vw,40px);line-height:1.16;
  letter-spacing:-.01em;margin:0;max-width:30ch;text-wrap:balance;color:var(--paper)}
.fd-body{color:#c1bec1;max-width:var(--measure);margin:22px 0 0;font-size:16px}
.fd-body a{color:var(--red-txt)}
.fd-foot{color:var(--graphite);max-width:var(--measure);margin:26px 0 0;font-size:13.5px}
.fd-foot a{color:var(--red-txt)}

.fd-proof{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line);
  border:1px solid var(--line);margin-top:44px}
@media(max-width:860px){.fd-proof{grid-template-columns:1fr}}
.fd-pf{background:var(--panel);padding:30px 26px 28px}
.fd-pf .fd-k{font-family:var(--mono);font-size:10px;letter-spacing:.24em;text-transform:uppercase;
  color:var(--graphite)}
.fd-pf .fd-v{font-family:var(--display);font-weight:800;font-size:clamp(28px,3vw,38px);
  letter-spacing:-.025em;margin-top:12px;font-variant-numeric:tabular-nums;line-height:1;
  color:var(--paper)}
.fd-pf .fd-v u{text-decoration:none;font-size:.44em;letter-spacing:.14em}
.fd-pf .fd-u{font-family:var(--mono);font-size:10.5px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--red-txt);margin-top:9px}
.fd-pf p{color:var(--steel);font-size:14.5px;margin:16px 0 0;max-width:34ch}

.fd-tower{border:1px solid var(--line);background:var(--panel);
  background-image:linear-gradient(to right,rgba(217,31,38,0) 0%,rgba(217,31,38,0) 30%,
    rgba(217,31,38,.045) 45%,rgba(217,31,38,.09) 62%,rgba(217,31,38,.135) 80%,rgba(217,31,38,.17) 100%);
  padding:clamp(28px,3.6vw,46px);display:flex;gap:36px;align-items:center;flex-wrap:wrap;
  justify-content:space-between}
.fd-tower .fd-lock{flex:1 1 340px}
.fd-tower .fd-k{font-family:var(--mono);font-size:10px;letter-spacing:.24em;text-transform:uppercase;
  color:var(--graphite)}
.fd-tower h3{font-family:var(--display);font-weight:800;letter-spacing:-.02em;
  font-size:clamp(22px,2.4vw,29px);margin:14px 0 0;color:var(--paper)}
.fd-tower p{color:var(--steel);margin:12px 0 0;max-width:48ch;font-size:15px}
.fd-signin{font-family:var(--mono);font-size:11px;letter-spacing:.22em;text-transform:uppercase;
  border:1px solid var(--line-2);padding:15px 24px;text-decoration:none;color:var(--paper);
  display:inline-flex;align-items:center;gap:12px;background:none;cursor:pointer;
  transition:border-color .2s var(--ease),background .2s var(--ease)}
.fd-signin:hover{border-color:var(--red);background:var(--red-wash)}
.fd-signin b{color:var(--red-txt);font-weight:500}

.fd-status{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:1px;
  background:var(--line);border:1px solid var(--line);margin-top:40px}
.fd-st{background:var(--panel);padding:22px 24px}
.fd-st .fd-k{font-family:var(--mono);font-size:10px;letter-spacing:.24em;text-transform:uppercase;
  color:var(--graphite)}
.fd-st .fd-v{margin-top:10px;font-size:15px;color:#c8c5c8}
.fd-st .fd-v b{color:var(--paper);font-weight:500}
/* The dot is decoration; the words beside it carry the state. Colour is never
   the only thing saying whether a thing is live. */
.fd-dot{width:7px;height:7px;border-radius:50%;display:inline-block;margin-right:9px;
  vertical-align:1px;background:var(--graphite)}
.fd-dot--on{background:var(--ok)}
.fd-dot--mid{background:var(--red)}

/* No margin-top. The section above already ends on a full --sec of padding,
   and stacking a second one put a screen and a half of nothing between the
   status block and the footer. Caught in the render, not in the source. */
.fd-ftr{border-top:1px solid var(--line);padding:44px 0 66px}
.fd-fbar{display:flex;justify-content:space-between;gap:28px;flex-wrap:wrap;align-items:flex-start}
.fd-flinks{display:flex;gap:26px;flex-wrap:wrap}
.fd-ftr a{color:var(--steel);text-decoration:none;font-family:var(--mono);font-size:10.5px;
  letter-spacing:.2em;text-transform:uppercase}
.fd-ftr a:hover{color:var(--paper)}
.fd-fine{color:var(--graphite);font-size:12.5px;margin-top:22px;max-width:66ch;line-height:1.7}
.fd-fine a{font-family:inherit;font-size:inherit;letter-spacing:0;text-transform:none;
  color:var(--steel);text-decoration:underline}

/* The case-file index at /work/. The cards are the dossier's own .file
   markup, so this page inherits every future fix to them for free. */
.fd-idx{padding:clamp(46px,7vw,86px) 0 0}
.fd-idx .files{margin-top:clamp(30px,4vw,46px)}

/* The three beats under WHAT IT IS. Same hairline-grid construction as the
   doors -- 1px gap over a --line ground -- so the two blocks read as one
   system rather than two people's work. Three columns, because three is
   what fits before the type has to shrink. */
.fd-what{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line);
  border:1px solid var(--line);margin-top:clamp(34px,4.5vw,52px)}
@media(max-width:900px){.fd-what{grid-template-columns:1fr}}
.fd-beat{background:var(--panel);padding:clamp(24px,2.8vw,34px)}
.fd-beat .fd-n{font-family:var(--mono);font-size:10px;letter-spacing:.26em;color:var(--red-txt);
  text-transform:uppercase}
.fd-beat h2{font-family:var(--display);font-weight:800;letter-spacing:-.02em;
  font-size:clamp(19px,2vw,23px);margin:14px 0 0;line-height:1.15;color:var(--paper)}
.fd-beat p{color:var(--steel);margin:12px 0 0;font-size:14.5px;max-width:38ch}
