/* IOVI design system v7. Editorial document meshed with otl.network patterns, dynamic components,
   manifesto chapters and entry veil. Ultramarine is exactly #1f3aff in both modes, used sparingly. */
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(fonts/ibm-plex-mono-latin-400-normal.woff2) format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(fonts/ibm-plex-mono-latin-500-normal.woff2) format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;src:url(fonts/ibm-plex-mono-latin-600-normal.woff2) format('woff2');}
*{margin:0;padding:0;box-sizing:border-box;} html{scroll-behavior:smooth;}
:root{--bg:#dee3ea;--paper:#e9edf2;--fg:#14171c;--mut:#55606e;--sig:#1f3aff;--onsig:#e9edf2;--line:#14171c;--hair:rgba(20,23,28,.17);}
[data-theme="dark"]{--bg:#14161a;--paper:#1b1e23;--fg:#e9ebee;--mut:#9aa1ab;--sig:#1f3aff;--onsig:#eff1f3;--line:#e9ebee;--hair:rgba(233,235,238,.16);}
body{font-family:var(--fam-display);background:var(--bg);color:var(--fg);line-height:1.5;-webkit-font-smoothing:antialiased;transition:background .3s ease,color .3s ease;min-height:100vh;}

/* corner-bracket frame */
.frame{position:fixed;inset:16px;pointer-events:none;z-index:30;}
.frame i{position:absolute;width:18px;height:18px;border:1.5px solid var(--line);}
.frame .tl{top:0;left:0;border-right:none;border-bottom:none;}
.frame .tr{top:0;right:0;border-left:none;border-bottom:none;}
.frame .bl{bottom:0;left:0;border-right:none;border-top:none;}
.frame .br{bottom:0;right:0;border-left:none;border-top:none;}

/* sticky top bar */
.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:space-between;gap:18px;flex-wrap:wrap;padding:18px 44px;background:var(--bg);border-bottom:1.5px solid var(--line);transition:background .3s ease;}
.tb-mark{display:flex;align-items:center;line-height:0;text-decoration:none;}
.tb-mark svg{height:22px;width:auto;}
.tb-mark .lk-light{display:block;} .tb-mark .lk-dark{display:none;}
[data-theme="dark"] .tb-mark .lk-light{display:none;} [data-theme="dark"] .tb-mark .lk-dark{display:block;}
.tb-right{display:flex;align-items:center;gap:26px;flex-wrap:wrap;}
.tb-nav{display:flex;gap:22px;flex-wrap:wrap;font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.14em;text-transform:uppercase;}
.tb-nav a{color:var(--mut);text-decoration:none;padding:2px 0;border-bottom:1.5px solid transparent;}
.tb-nav a:hover{color:var(--fg);}
.tb-nav a[aria-current="page"]{color:var(--fg);font-weight:600;border-bottom-color:var(--line);}
.toggle{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--mut);background:transparent;border:1.5px solid var(--hair);border-radius:999px;padding:8px 14px;cursor:pointer;}
.toggle:hover{color:var(--fg);border-color:var(--line);}
@media(max-width:860px){.topbar{padding:14px 22px;}.tb-right{gap:14px;}.tb-nav{gap:14px;}}

.doc{max-width:1080px;margin:0 auto;padding:0 44px 72px;}
@media(max-width:640px){.doc{padding:0 24px 56px;}}

/* hero + generative canvas */
.hero{text-align:center;padding:64px 0 0;position:relative;}
.hero-canvas{position:absolute;left:50%;top:-20px;transform:translateX(-50%);width:min(1000px,100vw);height:500px;pointer-events:none;z-index:-1;}
@media(prefers-reduced-motion:reduce){.hero-canvas{display:none;}}
.hero-art{margin:0 auto 30px;}
.hero-art svg{height:84px;width:auto;display:block;margin:0 auto;}
.hero-art .ink{stroke:var(--fg);}
.hero-art .soft{stroke:var(--mut);opacity:.55;}
.hero-art .flow{animation:flowdash 9s linear infinite;}
@keyframes flowdash{to{stroke-dashoffset:-64;}}
.hero-art .draw{opacity:0;animation:settle .7s cubic-bezier(.2,.7,.2,1) forwards;}
.hero-art .d2{animation-delay:.2s;}
@keyframes settle{from{opacity:0;transform:translateY(9px);}to{opacity:1;transform:none;}}
.kicker{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.28em;text-transform:uppercase;color:var(--mut);}
.hero h1{font-weight:700;letter-spacing:-.03em;line-height:1.04;font-size:clamp(34px,5vw,58px);max-width:30ch;margin:20px auto 0;}
.hero-lede{font-family:'IBM Plex Mono',monospace;font-size:13px;line-height:1.75;color:var(--mut);max-width:80ch;margin:24px auto 0;}
.hero-lede.full{margin-top:14px;}
.hero.inner{padding-top:56px;}
.hero.inner h1{font-size:clamp(30px,4.2vw,46px);}
.hero .kicker,.hero .themes,.hero h1,.hero .hero-lede,.hero .joinwrap,.hero .joinnote{opacity:0;animation:fadeup .55s cubic-bezier(.2,.7,.2,1) forwards;}
.hero .kicker{animation-delay:.25s;}
.hero .themes{animation-delay:.35s;}
.hero h1{animation-delay:.45s;}
.hero .hero-lede{animation-delay:.6s;}
.hero .joinwrap{animation-delay:.75s;}
.hero .joinnote{animation-delay:.85s;}
@keyframes fadeup{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}
@media(prefers-reduced-motion:reduce){
.hero-art .flow{animation:none;}
.hero-art .draw{animation:none;opacity:1;}
.hero .kicker,.hero .themes,.hero h1,.hero .hero-lede,.hero .joinwrap,.hero .joinnote{animation:none;opacity:1;}
}

/* rotating themes */
.themes{position:relative;height:18px;margin-top:16px;font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--mut);}
.themes span{position:absolute;left:0;right:0;top:0;opacity:0;transform:translateY(7px);transition:opacity .5s cubic-bezier(.2,.7,.2,1),transform .5s cubic-bezier(.2,.7,.2,1);}
.themes span.on{opacity:1;transform:none;}

/* thesis callout */
.callout{border-left:1.5px solid var(--line);padding:6px 0 6px 26px;margin-top:34px;font-size:20px;font-weight:500;letter-spacing:-.01em;line-height:1.45;max-width:56ch;}

/* partners strip */
.partners{margin-top:64px;text-align:center;}
.pn-cap{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.22em;text-transform:uppercase;color:var(--mut);margin-bottom:22px;}
.pl-marquee{overflow:hidden;}
.pl-track{display:flex;align-items:baseline;gap:56px;width:max-content;animation:tick 55s linear infinite reverse;}
.pl-marquee:hover .pl-track,.pl-track.paused{animation-play-state:paused;}
.plogo{color:var(--mut);opacity:.65;white-space:nowrap;transition:opacity .25s ease,color .25s ease;cursor:default;}
.plogo:hover{opacity:1;color:var(--fg);}
@keyframes tick{to{transform:translateX(-50%);}}
@media(prefers-reduced-motion:reduce){.pl-track{animation:none;flex-wrap:wrap;width:auto;justify-content:center;}.pl-ctl{display:none;}}

/* primary action */
.joinwrap{margin-top:40px;text-align:center;}
.joinbtn{display:inline-block;background:var(--sig);color:var(--onsig);font-family:'IBM Plex Mono',monospace;font-weight:600;font-size:13px;letter-spacing:.06em;text-transform:uppercase;padding:20px 46px;border-radius:8px;text-decoration:none;border:1.5px solid var(--sig);cursor:pointer;}
.joinbtn:hover{opacity:.92;}
.joinbtn.sm{padding:13px 26px;font-size:11px;}
.note{font-family:'IBM Plex Mono',monospace;font-size:11px;color:var(--mut);line-height:1.6;margin-top:14px;}
.joinnote{text-align:center;margin-top:16px;}

/* page section sub-nav */
.body-grid{display:grid;grid-template-columns:190px 1fr;gap:0 56px;align-items:start;}
.pagenav{position:sticky;top:96px;display:flex;flex-direction:column;gap:13px;font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.16em;text-transform:uppercase;padding-top:96px;}
.pagenav .pn-h{color:var(--mut);letter-spacing:.2em;border-bottom:1.5px solid var(--hair);padding-bottom:10px;margin-bottom:2px;}
.pagenav a{color:var(--mut);text-decoration:none;display:flex;gap:10px;align-items:center;transition:color .25s ease;}
.pagenav a::before{content:"";flex:0 0 6px;height:6px;border-radius:50%;background:var(--hair);transition:background .25s ease;}
.pagenav a:hover{color:var(--fg);}
.pagenav a.on{color:var(--fg);font-weight:600;}
.pagenav a.on::before{background:var(--sig);}
.body-main{min-width:0;}
.body-main .sec:first-child{margin-top:56px;}
@media(max-width:900px){
.body-grid{grid-template-columns:1fr;}
.pagenav{position:static;flex-direction:row;flex-wrap:nowrap;overflow-x:auto;gap:22px;padding:14px 0;margin-top:40px;border-top:1.5px solid var(--line);border-bottom:1.5px solid var(--line);}
.pagenav .pn-h{display:none;}
.pagenav a{white-space:nowrap;}
}

/* numbered sections */
.sec{margin-top:96px;scroll-margin-top:96px;}
.sec-head{display:flex;align-items:baseline;gap:10px;font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.16em;text-transform:uppercase;padding-top:16px;margin-bottom:34px;position:relative;}
.sec-head::before{content:"";position:absolute;top:0;left:0;right:0;height:1.5px;background:linear-gradient(90deg,var(--line) 0%,var(--line) 55%,transparent 100%);}
.sec-head .n{color:var(--sig);font-weight:600;}
.sec-head .sl{color:var(--mut);}
.sec-head b,.sec-head h2{color:var(--fg);font-weight:600;}
.sec-head .tag{margin-left:auto;color:var(--mut);letter-spacing:.1em;}
.anch{opacity:0;margin-left:2px;color:var(--mut);text-decoration:none;transition:opacity .2s ease;}
.sec-head:hover .anch{opacity:1;}
.anch:hover{color:var(--fg);}
@media(max-width:640px){.sec{margin-top:64px;}.sec-head .tag{display:none;}}

/* two-column editorial split */
.cols{display:grid;grid-template-columns:1fr 1fr;gap:0 72px;position:relative;}
.cols::before{content:"";position:absolute;left:50%;top:4px;bottom:4px;border-left:1px solid var(--hair);}
.cols .cl{padding-right:8px;}
.cols .cr{color:var(--mut);}
.cols p{font-size:15px;line-height:1.7;margin:0 0 16px;}
.cols p:last-child{margin-bottom:0;}
.cols .cr p{font-size:14px;line-height:1.7;}
.cols strong{font-weight:600;color:var(--fg);}
.cols .cr strong{color:var(--mut);}
@media(max-width:760px){.cols{grid-template-columns:1fr;gap:22px 0;}.cols::before{display:none;}.cols .cl{padding-right:0;}}

/* prose fallback */
.prose{font-size:15px;line-height:1.7;max-width:64ch;}
.prose p{margin:0 0 16px;} .prose p:last-child{margin-bottom:0;}

/* card grids */
.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:36px;}
.cards.two{grid-template-columns:repeat(2,1fr);}
@media(max-width:820px){.cards,.cards.two{grid-template-columns:1fr;}}
.card{display:block;border:1.5px solid var(--hair);border-radius:12px;background:var(--paper);padding:26px 24px 28px;text-decoration:none;color:inherit;transition:border-color .2s ease,transform .2s ease,box-shadow .2s ease;}
a.card:hover{border-color:var(--line);transform:translateY(-4px);box-shadow:0 12px 30px rgba(22,19,13,.09);}
[data-theme="dark"] a.card:hover{box-shadow:0 12px 30px rgba(0,0,0,.35);}
.card .ic{height:24px;margin-bottom:18px;}
.card .ic svg{height:24px;width:auto;display:block;}
.card .ic .ink{stroke:var(--fg);} .card .ic .node{fill:var(--fg);}
.card .tag{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--sig);margin-bottom:10px;}
.card h3{font-size:17px;font-weight:700;letter-spacing:-.01em;margin-bottom:10px;}
.card p{font-size:13.5px;line-height:1.65;color:var(--mut);}
.reveal .card{opacity:0;transform:translateY(12px);transition:opacity .45s cubic-bezier(.2,.7,.2,1),transform .45s cubic-bezier(.2,.7,.2,1),border-color .2s ease,box-shadow .2s ease;}
.reveal.vis .card{opacity:1;transform:none;}
.reveal.vis .card:nth-child(2){transition-delay:.08s;}
.reveal.vis .card:nth-child(3){transition-delay:.16s;}
.reveal.vis .card:nth-child(4){transition-delay:.24s;}
.reveal.vis .card:nth-child(5){transition-delay:.32s;}
.reveal.vis .card:nth-child(6){transition-delay:.4s;}
.reveal.vis a.card:hover{transition-delay:0s;transform:translateY(-4px);}
@media(prefers-reduced-motion:reduce){.reveal .card{opacity:1;transform:none;transition:none;}}

/* interactive stack */
.stack{display:grid;grid-template-columns:270px 1fr;gap:28px;margin-top:34px;align-items:stretch;}
.stack-layers{display:flex;flex-direction:column;gap:10px;}
.layer{text-align:left;background:var(--paper);border:1.5px solid var(--hair);border-radius:10px;padding:12px 16px;cursor:pointer;font-family:'IBM Plex Mono',monospace;letter-spacing:.12em;text-transform:uppercase;font-size:10px;color:var(--mut);transition:border-color .25s ease;}
.layer b{display:block;font-family:var(--fam-display);text-transform:none;letter-spacing:-.01em;font-size:15px;font-weight:700;color:var(--fg);margin-top:2px;}
.layer:hover,.layer.active{border-color:var(--line);}
.layer.active{animation:blip .45s cubic-bezier(.2,.7,.2,1);}
@keyframes blip{0%{box-shadow:0 0 0 0 var(--hair);}60%{box-shadow:0 0 0 5px var(--hair);}100%{box-shadow:0 0 0 0 rgba(0,0,0,0);}}
.stack-detail{border:1.5px solid var(--hair);border-radius:12px;background:var(--paper);padding:26px;}
.stack-detail .sd{display:none;}
.stack-detail .sd.on{display:block;animation:fadeup .32s cubic-bezier(.2,.7,.2,1);}
.stack-detail .sd-tag{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);margin-bottom:8px;}
.stack-detail .sd-name{font-weight:700;font-size:19px;letter-spacing:-.01em;margin-bottom:10px;}
.stack-detail .sd-desc{font-size:14px;line-height:1.65;color:var(--mut);max-width:52ch;}
.stack-detail .sd-wgs{margin-top:18px;font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.1em;text-transform:uppercase;}
.stack-detail .sd-wgs a{color:var(--fg);text-decoration:none;border-bottom:1.5px solid var(--line);margin-right:14px;padding-bottom:1px;}
.stack-detail .sd-wgs a:hover{opacity:.75;}
@media(max-width:760px){.stack{grid-template-columns:1fr;}}
@media(prefers-reduced-motion:reduce){.layer.active{animation:none;}.stack-detail .sd.on{animation:none;}}

/* expertise list */
.exp-h{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);margin-bottom:8px;}
.exp{list-style:none;font-family:'IBM Plex Mono',monospace;font-size:11.5px;line-height:2.1;letter-spacing:.05em;color:var(--mut);}
.exp li::before{content:"+ ";color:var(--fg);}

/* skill finder */
.skills{display:flex;flex-wrap:wrap;gap:10px;margin-top:22px;}
.skills.center{justify-content:center;}
.sk{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.05em;border:1.5px solid var(--hair);border-radius:999px;padding:9px 16px;background:var(--paper);color:var(--mut);cursor:pointer;transition:border-color .2s ease,color .2s ease,background .2s ease;}
.sk:hover{border-color:var(--line);color:var(--fg);}
.sk.sel{background:var(--fg);color:var(--bg);border-color:var(--fg);}
.sk-reset{background:none;border:none;font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);border-bottom:1.5px solid var(--hair);cursor:pointer;margin-top:18px;padding:0 0 2px;display:none;}
.sk-reset.on{display:inline-block;}
.sk-reset:hover{color:var(--fg);border-color:var(--line);}
.yours{display:none;font-family:'IBM Plex Mono',monospace;font-size:9px;letter-spacing:.12em;text-transform:uppercase;border:1.5px solid var(--sig);color:var(--sig);border-radius:999px;padding:3px 10px;margin-left:12px;white-space:nowrap;vertical-align:middle;}

/* track browser */
.trackgrid{display:grid;grid-template-columns:240px 1fr;gap:28px;align-items:start;margin-top:8px;}
.track-rail{display:flex;flex-direction:column;gap:8px;position:sticky;top:96px;}
.tr-item{display:flex;align-items:center;gap:10px;text-align:left;background:var(--paper);border:1.5px solid var(--hair);border-radius:10px;padding:13px 16px;cursor:pointer;font-family:var(--fam-display);font-size:14px;font-weight:600;color:var(--fg);transition:border-color .25s ease,opacity .3s ease;}
.tr-item .trn{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.1em;color:var(--mut);transition:color .25s ease;}
.tr-item:hover,.tr-item.active{border-color:var(--line);}
.tr-item.active{animation:blip .45s cubic-bezier(.2,.7,.2,1);}
.tr-item.active .trn{color:var(--sig);font-weight:600;}
.tr-item.match .trn{color:var(--sig);font-weight:600;}
.tr-item.match::after{content:"";width:6px;height:6px;border-radius:50%;background:var(--sig);margin-left:auto;}
.tr-item.dim{opacity:.45;}
.track-viewer{border:1.5px solid var(--hair);border-radius:12px;background:var(--paper);padding:28px;min-height:300px;}
.tp{display:none;}
.tp.on{display:block;animation:fadeup .32s cubic-bezier(.2,.7,.2,1);}
.tp-tag{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);margin-bottom:10px;}
.tp-name{font-size:20px;font-weight:700;letter-spacing:-.01em;margin-bottom:12px;}
.tp-desc{font-size:14.5px;line-height:1.65;color:var(--mut);max-width:58ch;margin-bottom:22px;}
.tp .exp{margin-bottom:6px;}
.tp-join{margin-top:24px;}
.tp-body p{font-size:14.5px;line-height:1.7;margin:0 0 14px;}
.tp-body p:last-child{margin-bottom:0;}
.tp-body p.mut{color:var(--mut);}
.tp-body strong{font-weight:600;}
.tp-next{background:none;border:none;font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--fg);border-bottom:1.5px solid var(--line);cursor:pointer;margin-top:22px;padding:0 0 2px;}
.tp-next:hover{opacity:.7;}
@media(max-width:820px){
.trackgrid{grid-template-columns:1fr;}
.track-rail{position:static;flex-direction:row;flex-wrap:nowrap;overflow-x:auto;padding-bottom:6px;}
.tr-item{white-space:nowrap;}
}
@media(prefers-reduced-motion:reduce){.tr-item.active{animation:none;}.tp.on{animation:none;}}

/* architecture figure */
.archfig{margin-top:26px;}
.archfig svg{width:100%;max-width:680px;height:auto;display:block;}
.as-box{fill:var(--paper);stroke:var(--hair);stroke-width:1.5;}
.as-box.emph{stroke:var(--line);}
.as-wgbox{fill:var(--bg);stroke:var(--hair);stroke-width:1.5;}
.as-name{font-family:var(--fam-display);font-weight:700;font-size:14px;fill:var(--fg);}
.as-l{font-family:'IBM Plex Mono',monospace;font-size:9px;letter-spacing:.1em;fill:var(--mut);}
.as-wg{font-family:'IBM Plex Mono',monospace;font-size:9.5px;letter-spacing:.08em;fill:var(--fg);}
.as-flow{stroke:var(--mut);stroke-width:1;stroke-dasharray:3 5;}
.as-arrow{stroke:var(--fg);stroke-width:1.2;}
.as-tip{fill:var(--fg);}
.figcap{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);margin-top:14px;}
.reveal .as-anim{opacity:0;transition:opacity .5s cubic-bezier(.2,.7,.2,1);}
.reveal.vis .as-anim{opacity:1;}
.reveal.vis .as-anim:nth-of-type(2){transition-delay:.08s;}
.reveal.vis .as-anim:nth-of-type(3){transition-delay:.16s;}
.reveal.vis .as-anim:nth-of-type(4){transition-delay:.24s;}
.reveal.vis .as-anim:nth-of-type(5){transition-delay:.32s;}
@media(prefers-reduced-motion:reduce){.reveal .as-anim{opacity:1;transition:none;}}

/* terminal card */
.term{background:#14161a;border-radius:10px;padding:20px 22px;font-family:'IBM Plex Mono',monospace;font-size:12px;line-height:2;color:#e9ebee;max-width:420px;}
.term .tp1{color:#1f3aff;font-weight:600;}
.term .tl2{color:#9aa1ab;padding-left:16px;}

/* steps */
.steps .stn{font-family:'IBM Plex Mono',monospace;font-size:12px;font-weight:600;letter-spacing:.1em;color:var(--sig);margin-bottom:14px;}
.steps .card p a{color:inherit;}

/* swatches (design page) */
.sw{display:inline-block;width:14px;height:14px;border-radius:4px;border:1.5px solid var(--hair);vertical-align:-2px;margin-right:10px;}
.type-sample{border:1.5px solid var(--hair);border-radius:12px;background:var(--paper);padding:24px;margin-top:16px;}
.type-sample .ts-cap{font-family:'IBM Plex Mono',monospace;font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);margin-bottom:10px;}

/* fielded record table */
.record{border-top:1.5px solid var(--line);}
.record .r{display:flex;gap:24px;border-bottom:1.5px solid var(--line);padding:13px 0;}
.record .k{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--mut);flex:0 0 150px;}
.record .v{font-family:'IBM Plex Mono',monospace;font-size:12.5px;color:var(--fg);flex:1;min-width:0;line-height:1.5;}
@media(max-width:560px){.record .r{flex-wrap:wrap;gap:4px 24px;}.record .k{flex-basis:100%;}}

/* FAQ accordion */
.faq{border-top:1.5px solid var(--line);}
.fq{border-bottom:1.5px solid var(--line);}
.fq summary{list-style:none;cursor:pointer;display:flex;align-items:baseline;gap:14px;padding:18px 0;font-weight:600;font-size:16px;letter-spacing:-.01em;}
.fq summary::-webkit-details-marker{display:none;}
.fq summary::after{content:"+";margin-left:auto;font-family:'IBM Plex Mono',monospace;font-size:15px;color:var(--mut);display:inline-block;transition:transform .25s ease,color .25s ease;}
.fq[open] summary::after{transform:rotate(45deg);color:var(--sig);}
.fq summary:hover::after{color:var(--fg);}
.fq .fq-a{padding:0 0 20px;max-width:70ch;}
.fq .fq-a p{font-size:14.5px;line-height:1.7;color:var(--mut);margin:0 0 12px;}
.fq .fq-a p:last-child{margin-bottom:0;}
.fq .fq-a a{color:inherit;}
@media(prefers-reduced-motion:reduce){.fq summary::after{transition:none;}}

/* blog cards (Medium feed) */
.bloggrid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:36px;}
@media(max-width:820px){.bloggrid{grid-template-columns:1fr;}}
.bcard .bc-img{height:150px;border-radius:8px;background-size:cover;background-position:center;background-color:var(--bg);margin-bottom:16px;border:1.5px solid var(--hair);}
.blog-empty{grid-column:1/-1;border:1.5px dashed var(--hair);border-radius:12px;padding:44px 24px;text-align:center;font-family:'IBM Plex Mono',monospace;font-size:12px;line-height:1.8;color:var(--mut);}
.blog-empty a{color:var(--fg);}

/* manifesto chapters */
.mchap{display:grid;grid-template-columns:1fr 230px;gap:24px 60px;align-items:start;}
.mch-body p{font-size:15.5px;line-height:1.75;margin:0 0 18px;max-width:60ch;}
.mch-body p:last-child{margin-bottom:0;}
.mch-body strong{font-weight:600;}
.mch-fig{margin:6px 0 0;color:var(--fg);}
.mch-fig svg{width:100%;max-width:220px;height:auto;display:block;opacity:.9;}
.mch-fig figcaption{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--mut);margin-top:12px;}
@media(max-width:760px){.mchap{grid-template-columns:1fr;}.mch-fig{max-width:190px;}}

/* entry veil: typed founding narrative */
.introveil{position:fixed;inset:0;z-index:80;background:#14161a;display:none;align-items:center;justify-content:center;padding:32px;opacity:1;transition:opacity .8s ease;}
.introveil.on{display:flex;}
.introveil.out{opacity:0;pointer-events:none;}
.iv-inner{max-width:64ch;text-align:center;}
.iv-type{font-family:'IBM Plex Mono',monospace;font-size:clamp(15px,2vw,19px);line-height:1.9;color:#e9ebee;min-height:5.7em;}
.iv-type::after{content:"_";color:#1f3aff;animation:ivblink 1s steps(1) infinite;}
.iv-type.done::after{content:"";}
@keyframes ivblink{50%{opacity:0;}}
.iv-line{font-family:'IBM Plex Mono',monospace;font-size:clamp(12px,1.5vw,14px);line-height:1.9;color:#9aa1ab;margin-top:28px;opacity:0;transform:translateY(8px);transition:opacity .7s ease,transform .7s ease;}
.iv-line.on{opacity:1;transform:none;}
.iv-skip{position:fixed;bottom:26px;right:30px;background:none;border:1.5px solid rgba(233,235,238,.3);border-radius:999px;color:#9aa1ab;font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;padding:9px 16px;cursor:pointer;}
.iv-skip:hover{color:#e9ebee;border-color:#e9ebee;}

/* modal */
.modal{position:fixed;inset:0;z-index:50;display:none;align-items:center;justify-content:center;padding:20px;background:rgba(11,10,7,.5);backdrop-filter:blur(9px);-webkit-backdrop-filter:blur(9px);}
.modal.open{display:flex;}
.modal-card{display:flex;flex-direction:column;width:min(700px,94vw);height:min(90vh,900px);background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 24px 70px rgba(0,0,0,.4);}
.modal-head{display:flex;justify-content:space-between;align-items:center;padding:15px 20px;border-bottom:1px solid #e6e2d8;font-family:'IBM Plex Mono',monospace;font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:#16130d;}
.modal-close{background:none;border:none;font-size:22px;line-height:1;color:#6b6457;cursor:pointer;padding:0 4px;}
.modal-close:hover{color:#16130d;}
.gform{flex:1;width:100%;min-height:0;border:0;background:#fff;}
.modal-foot{padding:10px 16px;text-align:center;font-family:'IBM Plex Mono',monospace;font-size:11px;color:#6b6457;border-top:1px solid #e6e2d8;}
.modal-foot a{color:#1f3aff;text-decoration:none;}
.modal-foot a:hover{text-decoration:underline;}

/* agent chat shell (parked: markup removed from pages, styles kept for reactivation) */
.chatbtn{position:fixed;bottom:24px;right:24px;z-index:44;font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.12em;text-transform:uppercase;background:var(--paper);color:var(--fg);border:1.5px solid var(--line);border-radius:999px;padding:12px 20px;cursor:pointer;}
.chatbtn:hover{background:var(--bg);}
.chat{position:fixed;bottom:78px;right:24px;z-index:45;width:min(340px,calc(100vw - 48px));background:var(--paper);border:1.5px solid var(--line);border-radius:12px;display:none;flex-direction:column;overflow:hidden;box-shadow:0 18px 50px rgba(0,0,0,.18);}
.chat.open{display:flex;}
.chat-head{display:flex;justify-content:space-between;align-items:center;padding:12px 14px;border-bottom:1.5px solid var(--hair);font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);}
.chat-head button{background:none;border:none;color:var(--mut);font-size:18px;cursor:pointer;line-height:1;}
.chat-head button:hover{color:var(--fg);}
.chat-msgs{padding:14px;display:flex;flex-direction:column;gap:10px;max-height:280px;overflow-y:auto;}
.msg{font-size:13px;line-height:1.55;padding:10px 12px;border-radius:10px;max-width:92%;}
.msg.bot{background:var(--bg);border:1.5px solid var(--hair);color:var(--fg);align-self:flex-start;}
.msg.bot a{color:inherit;}
.msg.me{background:var(--fg);color:var(--bg);align-self:flex-end;}
.chat-in{display:flex;border-top:1.5px solid var(--hair);}
.chat-in input{flex:1;border:none;background:transparent;padding:12px 14px;font-family:var(--fam-display);font-size:13px;color:var(--fg);outline:none;}
.chat-in input::placeholder{color:var(--mut);}
.chat-in button{border:none;background:transparent;color:var(--fg);font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:0 14px;cursor:pointer;}

/* colophon footer */
footer{border-top:1.5px solid var(--line);margin-top:96px;padding:24px 0 0;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:14px;}
footer .fl{font-family:'IBM Plex Mono',monospace;font-size:11px;color:var(--mut);letter-spacing:.04em;}
footer .stamp{color:var(--mut);}
footer .fr{display:flex;align-items:center;gap:20px;flex-wrap:wrap;}
footer .fr a{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--mut);text-decoration:none;display:flex;align-items:center;}
footer .fr a:hover{color:var(--fg);}

/* scroll reveal */
.reveal{opacity:0;transform:translateY(14px);transition:opacity .38s cubic-bezier(.2,.7,.2,1),transform .38s cubic-bezier(.2,.7,.2,1);}
.reveal.vis{opacity:1;transform:none;}
@media(prefers-reduced-motion:reduce){.reveal{opacity:1;transform:none;transition:none;}}

/* ============ v3 redesign additions (v10) ============ */

/* numbered left index rail (Paradigm-style) */
.railgrid{grid-template-columns:230px 1fr;}
.railgrid .pagenav{gap:11px;padding-top:64px;}
.railgrid .pagenav a{font-size:10px;letter-spacing:.13em;}
.railgrid .pagenav a::before{display:none;}
.railgrid .pagenav .rn{color:var(--mut);font-weight:400;margin-right:9px;transition:color .25s ease;}
.railgrid .pagenav a.on .rn{color:var(--sig);font-weight:600;}
@media(max-width:900px){.railgrid{grid-template-columns:1fr;}.railgrid .pagenav .rn{margin-right:5px;}}

/* proof strip */
.proof{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:36px;}
@media(max-width:820px){.proof{grid-template-columns:repeat(2,1fr);}}
.pf{border:1.5px solid var(--hair);border-radius:12px;background:var(--paper);padding:22px;text-decoration:none;color:inherit;display:block;transition:border-color .2s ease;}
a.pf:hover{border-color:var(--line);}
.pf .n{font-size:34px;font-weight:700;letter-spacing:-.02em;line-height:1;}
.pf .d{font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);margin-top:10px;line-height:1.7;}

/* interstitial epigraphs */
.epi{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.24em;text-transform:uppercase;color:var(--mut);text-align:center;margin:112px 0 -34px;}

/* redirect stubs */
.stub{min-height:70vh;display:flex;align-items:center;justify-content:center;text-align:center;font-family:'IBM Plex Mono',monospace;font-size:13px;line-height:2;color:var(--mut);}
.stub a{color:inherit;}

/* pinned scroll-build stack */
.pinwrap{height:220vh;}
.pinstage{position:sticky;top:88px;display:flex;flex-direction:column;justify-content:flex-start;gap:44px;padding-top:20px;}
.pinstage .sec-head{width:100%;margin-bottom:0;}
.sbuild{display:grid;grid-template-columns:300px 1fr;gap:48px;width:100%;align-items:center;}
.sb-col{display:flex;flex-direction:column;gap:10px;}
.sb-layer{background:var(--paper);border:1.5px solid var(--hair);border-radius:10px;padding:13px 18px;font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);opacity:0;transform:translateY(26px);will-change:transform,opacity;}
.sb-layer b{display:block;font-family:var(--fam-display);text-transform:none;letter-spacing:-.01em;font-size:16px;font-weight:700;color:var(--fg);margin-top:2px;}
.sb-layer.done{border-color:var(--line);}
.sb-note{display:none;}
.sb-note.on{display:block;animation:fadeup .3s cubic-bezier(.2,.7,.2,1);}
.sb-tag{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);margin-bottom:10px;}
.sb-desc{font-size:16px;line-height:1.7;color:var(--fg);max-width:46ch;}
.sb-wgs{margin-top:20px;font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.1em;text-transform:uppercase;}
.sb-wgs a{color:var(--fg);text-decoration:none;border-bottom:1.5px solid var(--line);margin-right:14px;padding-bottom:1px;}
.sb-wgs a:hover{opacity:.75;}
@media(max-width:760px){.sbuild{grid-template-columns:1fr;gap:26px;}.pinstage{align-items:flex-start;padding-top:20px;}}
@media(prefers-reduced-motion:reduce){
.pinwrap{height:auto;}
.pinstage{position:static;height:auto;}
.sb-layer{opacity:1;transform:none;}
.sb-note{display:block;margin-bottom:20px;}
}

/* ============ v9/v10: dark cinematic + brutalist + living document ============ */

/* the scene engine owns all entrances: neutralize the old reveal + view() systems */
.reveal,.reveal .card{opacity:1;transform:none;}
.reveal .card{transition:border-color .2s ease,box-shadow .2s ease;}
@supports (animation-timeline: view()){
@media (prefers-reduced-motion: no-preference){
.proof .pf,.mch-fig svg{animation:none;}
}}

/* crosshair register marks per section */
.sec{position:relative;}
.sec::after{content:"+";position:absolute;top:6px;right:-2px;font-family:'IBM Plex Mono',monospace;font-size:12px;color:var(--mut);opacity:.5;}

/* brutalist scale */
.sec{margin-top:56px;}
.sec-head{align-items:baseline;gap:16px;margin-bottom:34px;padding-top:20px;}
.sec-head b,.sec-head h2{font-family:var(--fam-display);text-transform:none;letter-spacing:-.025em;font-size:clamp(27px,3.4vw,46px);line-height:1.05;font-weight:700;}
.sec-head .n{font-size:12px;}
.hero{padding-top:88px;}
.hero h1{font-size:clamp(40px,6.4vw,86px);line-height:1.0;max-width:22ch;}
.hero-lede{font-size:14px;}
/* Hero deck: the one-sentence tier between the h1 and the mono microline, added
   2026-09-11 for the anchor-contrast hero (copy review pass 3). Only the home hero
   emits .hero-deck, so the other generators sharing this file are unaffected. */
.hero-deck{font-size:19px;font-weight:500;letter-spacing:-.01em;line-height:1.5;max-width:56ch;margin:22px auto 0;color:var(--fg);text-wrap:balance;}
.hero .hero-deck{opacity:0;animation:fadeup .55s cubic-bezier(.2,.7,.2,1) forwards;animation-delay:.68s;}
@media(prefers-reduced-motion:reduce){.hero .hero-deck{animation:none;opacity:1;}}
.mch-body p{font-size:16.5px;line-height:1.8;}
@media(max-width:640px){.sec{margin-top:48px;}}

/* The hero used to sit in front of a soft radial bloom in dark mode. It was the
   last blurred glow on the site after the closing panel lost its own, and it is the
   most template-recognisable device in the whole system, so it is gone. The heroes
   sit on flat canvas like every other surface. */

/* ============ v10: every section is a pinned build scene ============ */
.scene .scn-wrap{height:160vh;}
.scene .scn-stage{position:sticky;top:88px;padding-bottom:8px;}
.scn-u{opacity:0;transform:translateY(30px);will-change:transform,opacity;}
@media(max-width:760px),(prefers-reduced-motion:reduce){
.scene .scn-wrap{height:auto!important;}
.scene .scn-stage{position:static;min-height:0;}
.scn-u{opacity:1;transform:none;}
}

/* kinetic hero headline: words rise after the veil; one outlined word */
.hero h1{text-wrap:balance;}
.hero h1 .hw{display:inline-block;opacity:0;transform:translateY(.35em);}
html.hero-go .hero h1 .hw{animation:hwin .6s cubic-bezier(.2,.7,.2,1) forwards;}
html.hero-go .hero h1 .hw:nth-child(1){animation-delay:.05s;}
html.hero-go .hero h1 .hw:nth-child(2){animation-delay:.14s;}
html.hero-go .hero h1 .hw:nth-child(3){animation-delay:.23s;}
html.hero-go .hero h1 .hw:nth-child(4){animation-delay:.32s;}
html.hero-go .hero h1 .hw:nth-child(5){animation-delay:.41s;}
@keyframes hwin{to{opacity:1;transform:none;}}
.hero h1 .ol{color:transparent;-webkit-text-stroke:2px var(--fg);text-stroke:2px var(--fg);}
.hero{will-change:transform,opacity;}
@media(prefers-reduced-motion:reduce){
.hero h1 .hw{opacity:1;transform:none;animation:none!important;}
}
/* neutralize the v7 h1 fade so words own the entrance */
html.hero-go .hero h1{opacity:1;animation:none;}

/* ============ v11: professional layout pass (grid, left alignment, rhythm) ============ */

/* left-aligned editorial hero (Paradigm x Linear) */
.hero{text-align:left;padding:96px 0 0;}
.hero-art{margin:0 0 32px;}
.hero-art svg{margin:0;height:40px;}
.hero-art .soft{display:none;}
.themes{text-align:left;}
.themes span{left:0;right:auto;}
.hero h1{margin:20px 0 0;max-width:15ch;font-size:clamp(46px,7.2vw,96px);letter-spacing:-.035em;}
.hero-lede{font-family:var(--fam-display);font-size:19px;line-height:1.55;color:var(--fg);margin:32px 0 0;max-width:56ch;letter-spacing:0;}
.hero-lede.full{margin-top:14px;font-size:15.5px;color:var(--mut);line-height:1.65;}
.joinwrap{text-align:left;margin-top:40px;}
.joinnote{text-align:left;}
.partners{margin-top:96px;text-align:left;}
.pn-cap{text-align:left;}

/* grid discipline: consistent measure, aligned label column, 8px rhythm */
.doc{max-width:1240px;}
.body-grid{gap:0 64px;}
.railgrid{grid-template-columns:200px 1fr;}
.sec{margin-top:64px;}
.sec-head{margin-bottom:40px;}
.sec-head .n{display:inline-block;min-width:36px;}
.mchap{grid-template-columns:minmax(0,7fr) minmax(0,3fr);gap:24px 80px;}
.mch-body p{max-width:58ch;}
.callout{font-size:23px;max-width:44ch;}
.sb-desc{max-width:44ch;}
.hero.inner h1{font-size:clamp(34px,4.6vw,56px);}
@media(max-width:640px){.hero h1{font-size:clamp(38px,10vw,52px);}}

/* ============ v12: the ordering machine hero + blog tab ============ */
.hero.v3grid{display:grid;grid-template-columns:minmax(0,6fr) minmax(0,5fr);gap:64px;align-items:center;padding:72px 0 0;}
.hv-l .kicker{margin-bottom:6px;}
.hero.v3grid h1{font-size:clamp(52px,6.6vw,100px);max-width:9ch;margin:18px 0 0;}
.hero.v3grid .hero-lede{margin-top:26px;}
.hv-r{position:relative;}
#orderCanvas{width:100%;max-width:560px;aspect-ratio:1/1;display:block;margin:0 auto;}
.hv-cap{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--mut);text-align:center;margin-top:10px;}
@media(max-width:900px){
.hero.v3grid{grid-template-columns:1fr;gap:40px;}
#orderCanvas{max-width:420px;}
}
html.hero-go .hero.v3grid .hw:nth-child(4){animation-delay:.32s;}

/* ============ v13: clean centered hero (OTL register) on slate ============ */
.hero.hero-c{text-align:center;padding:72px 0 0;display:block;}
.hero-c .hero-art{margin:0 auto 30px;}
.hero-c .hero-art svg{margin:0 auto;height:72px;}
.hero-c .hero-art .soft{display:block;}
.hero-c h1{margin:22px auto 0;max-width:26ch;font-size:clamp(34px,4.6vw,62px);letter-spacing:-.03em;line-height:1.12;}
.hero-c h1 .acc{font-family:'IBM Plex Mono',monospace;font-style:italic;font-weight:400;color:var(--mut);letter-spacing:-.02em;}
.hero-c .hero-lede{margin:24px auto 0;text-align:center;font-family:'IBM Plex Mono',monospace;font-size:13px;line-height:1.75;color:var(--mut);max-width:66ch;}
.hero-c .joinwrap{text-align:center;margin-top:36px;}
.hero-c .joinnote{text-align:center;}
.hero-c + .partners{margin-top:72px;text-align:center;}
.hero-c + .partners .pn-cap{text-align:center;}

/* sun / moon theme toggle */
.toggle{padding:8px 11px;line-height:0;display:inline-flex;align-items:center;}
.toggle .ic-sun{display:none;}
.toggle .ic-moon{display:block;}
[data-theme="dark"] .toggle .ic-moon{display:none;}
[data-theme="dark"] .toggle .ic-sun{display:block;}


/* ============ v14: audit remediation ============ */
html{scroll-padding-top:96px;}
/* small ultramarine text gets an accessible tint in dark mode only (buttons/wordmark keep exact #1f3aff) */
:root{--sig-text:#1f3aff;}
[data-theme="dark"]{--sig-text:#6e82ff;}
.sec-head .n,.railgrid .pagenav a.on .rn,.card .tag,.pf .tag,.steps .stn,.term .tp1{color:var(--sig-text);}
/* skip link */
.skip{position:fixed;top:-48px;left:16px;z-index:99;background:var(--fg);color:var(--bg);font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.1em;text-transform:uppercase;padding:10px 16px;border-radius:0 0 8px 8px;text-decoration:none;transition:top .2s ease;}
.skip:focus{top:0;}
/* visible focus everywhere */
:focus-visible{outline:2px solid var(--sig);outline-offset:3px;border-radius:2px;}
/* footer icon links: 24px minimum target */
footer .fr a{min-width:24px;min-height:24px;justify-content:center;}
/* mobile: section heads wrap instead of overflowing */
@media(max-width:640px){
.sec-head{flex-wrap:wrap;row-gap:4px;}
.sec-head b,.sec-head h2{font-size:clamp(21px,6.2vw,28px);min-width:0;overflow-wrap:anywhere;}
.anch{display:none;}
.blog-empty{padding:32px 14px;}
}
.sec-head h2{margin:0;}

/* mobile overflow: grid items must be allowed to shrink; clip any straggler at the root */
html,body{overflow-x:clip;}
@media(max-width:900px){
.railgrid{grid-template-columns:minmax(0,1fr);}
.body-grid{grid-template-columns:minmax(0,1fr);}
.pagenav{min-width:0;max-width:100%;}
}


/* stack scene plate caption */
.sb-col .figcap{margin-top:18px;}

/* ============ v15: schematic figures, proof sources, benchmark plate ============ */

/* WG track schematics */

/* proof tiles: hover reveals the source (verifiability as a gesture) */
.pf{position:relative;overflow:hidden;}
.pf .src{position:absolute;left:0;right:0;bottom:0;padding:9px 22px;font-family:'IBM Plex Mono',monospace;font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--mut);background:var(--paper);border-top:1.5px solid var(--hair);transform:translateY(101%);transition:transform .25s cubic-bezier(.2,.7,.2,1);}
.pf:hover .src,.pf:focus-visible .src,.pf:focus .src{transform:none;}
@media(prefers-reduced-motion:reduce){.pf .src{transition:none;}}

/* benchmark plate */
.bench{border:1.5px solid var(--hair);border-radius:12px;background:var(--paper);padding:30px 30px 24px;max-width:720px;}
.bn-title{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--mut);margin-bottom:24px;}
.b-row{display:grid;grid-template-columns:170px 1fr 70px;gap:16px;align-items:center;margin-bottom:14px;}
.b-l{font-size:14px;font-weight:600;}
.b-track{height:14px;border:1.5px solid var(--hair);border-radius:4px;overflow:hidden;background:var(--bg);}
.b-fill{height:100%;background:var(--mut);opacity:.55;min-width:5px;}
.b-fill.sigb{background:var(--sig);opacity:1;}
.b-v{font-family:'IBM Plex Mono',monospace;font-size:12px;text-align:right;}
.bn-note{font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.08em;color:var(--mut);margin-top:18px;line-height:1.7;}
.b-pending{margin-top:16px;padding:12px 16px;border:1.5px dashed var(--hair);border-radius:8px;font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);}
@media(max-width:640px){.b-row{grid-template-columns:1fr;gap:6px;}.b-v{text-align:left;}}

/* ============ v16: typographic spec plates replace pictorial figures ============ */
.specb{border:1.5px solid var(--hair);border-radius:10px;background:var(--paper);padding:16px 18px 8px;font-family:'IBM Plex Mono',monospace;}
.sp-hd{font-size:9.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--mut);padding-bottom:11px;border-bottom:1.5px solid var(--hair);}
.sp-r{display:flex;gap:14px;padding:9px 0;}
.sp-r+.sp-r{border-top:1px solid var(--hair);}
.sp-k{flex:0 0 84px;font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);padding-top:2px;}
.sp-v{font-size:11.5px;letter-spacing:.02em;color:var(--fg);line-height:1.55;}
@media(max-width:760px){.mch-fig{max-width:360px;}}

/* ============ v17: Genzio-feel pass. Soft fade-ups, rounded depth, atmosphere ============ */

/* motion: fade-up reveals replace the scrubbed scene engine */
.rv{opacity:0;transform:translateY(28px);transition:opacity .8s cubic-bezier(.2,.7,.2,1),transform .8s cubic-bezier(.2,.7,.2,1);}
.rv.in{opacity:1;transform:none;}
.rv.in .card,.rv.in .pf,.rv.in .step,.rv.in .chl,.rv.in .fq{animation:rvup .7s cubic-bezier(.2,.7,.2,1) backwards;}
.rv.in .pf:nth-child(2),.rv.in .step:nth-child(2),.rv.in .card:nth-child(2){animation-delay:.08s;}
.rv.in .pf:nth-child(3),.rv.in .step:nth-child(3),.rv.in .card:nth-child(3){animation-delay:.16s;}
.rv.in .pf:nth-child(4){animation-delay:.24s;}
@keyframes rvup{from{opacity:0;transform:translateY(22px);}to{opacity:1;transform:none;}}
@media(prefers-reduced-motion:reduce){
.rv{opacity:1;transform:none;transition:none;}
.rv.in .card,.rv.in .pf,.rv.in .step,.rv.in .chl,.rv.in .fq{animation:none;}
}

/* hero atmosphere: slow-drifting ultramarine wash, light-theme friendly */
.hero{position:relative;}
.atmo{position:absolute;inset:-120px -10vw 0;pointer-events:none;z-index:-1;overflow:visible;filter:blur(70px);}
.atmo i{position:absolute;border-radius:50%;display:block;}
.atmo .a1{width:520px;height:520px;left:8%;top:-60px;background:radial-gradient(closest-side,rgba(31,58,255,.16),transparent 70%);animation:drift1 19s ease-in-out infinite alternate;}
.atmo .a2{width:420px;height:420px;right:6%;top:40px;background:radial-gradient(closest-side,rgba(31,58,255,.10),transparent 70%);animation:drift2 23s ease-in-out infinite alternate;}
.atmo .a3{width:380px;height:380px;left:44%;top:180px;background:radial-gradient(closest-side,rgba(20,23,28,.07),transparent 70%);animation:drift1 27s ease-in-out infinite alternate-reverse;}
[data-theme="dark"] .atmo .a1{background:radial-gradient(closest-side,rgba(31,58,255,.22),transparent 70%);}
[data-theme="dark"] .atmo .a3{background:radial-gradient(closest-side,rgba(233,235,238,.05),transparent 70%);}
@keyframes drift1{from{transform:translate(0,0) scale(1);}to{transform:translate(70px,40px) scale(1.12);}}
@keyframes drift2{from{transform:translate(0,0) scale(1.05);}to{transform:translate(-60px,30px) scale(.95);}}
@media(prefers-reduced-motion:reduce){.atmo{display:none;}}

/* rounded surfaces with soft depth */
:root{--shadow:0 1px 2px rgba(20,23,28,.05),0 10px 30px rgba(20,23,28,.07);--shadow-h:0 2px 4px rgba(20,23,28,.06),0 16px 44px rgba(20,23,28,.11);}
[data-theme="dark"]{--shadow:0 1px 2px rgba(0,0,0,.3),0 12px 34px rgba(0,0,0,.35);--shadow-h:0 2px 4px rgba(0,0,0,.35),0 18px 48px rgba(0,0,0,.5);}
.card,.pf,.fq,.step,.chl,.stack,.record,.bench{border-radius:18px;border:1px solid var(--hair);background:var(--paper);box-shadow:var(--shadow);}
.card,.pf{transition:box-shadow .3s ease,transform .3s ease,border-color .3s ease;}
a.pf:hover,.card:hover{box-shadow:var(--shadow-h);transform:translateY(-3px);border-color:var(--hair);}
.pf{padding:28px;}
.pf .n{font-size:44px;}
.proof{gap:18px;}

/* process steps */
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:40px;}
@media(max-width:820px){.steps{grid-template-columns:1fr;}}
.step{padding:30px 28px;}
.step .sn{font-family:'IBM Plex Mono',monospace;font-size:13px;color:var(--sig);letter-spacing:.08em;}
.step h3{font-size:21px;letter-spacing:-.015em;margin:14px 0 10px;}
.step p{font-size:14.5px;line-height:1.7;color:var(--mut);}

/* manifesto chapter list */
.chlist{margin-top:36px;display:flex;flex-direction:column;gap:12px;}
.chl{display:grid;grid-template-columns:44px minmax(0,220px) 1fr 30px;gap:18px;align-items:center;padding:20px 26px;text-decoration:none;color:inherit;transition:box-shadow .3s ease,transform .3s ease;}
.chl:hover{box-shadow:var(--shadow-h);transform:translateY(-2px);}
.chl .cn{font-family:'IBM Plex Mono',monospace;font-size:11px;color:var(--sig);}
.chl b{font-size:16.5px;letter-spacing:-.01em;}
.chl .ct{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);}
.chl .ar{color:var(--mut);transition:transform .25s ease,color .25s ease;}
.chl:hover .ar{transform:translateX(4px);color:var(--fg);}
@media(max-width:700px){.chl{grid-template-columns:34px 1fr 24px;}.chl .ct{display:none;}}

/* FAQ accordion, rounded */
.faq{display:flex;flex-direction:column;gap:12px;margin-top:8px;}
.fq{padding:0;overflow:hidden;}
.fq summary{padding:22px 26px;font-size:16px;font-weight:600;letter-spacing:-.01em;cursor:pointer;list-style:none;display:flex;justify-content:space-between;align-items:center;gap:18px;}
.fq summary::-webkit-details-marker{display:none;}
.fq summary::after{content:"+";font-family:'IBM Plex Mono',monospace;font-weight:400;font-size:18px;color:var(--mut);transition:transform .3s ease;}
.fq[open] summary::after{transform:rotate(45deg);}
.fq .fq-a{padding:0 26px 24px;}
.fq .fq-a p{font-size:14.5px;line-height:1.75;color:var(--mut);max-width:72ch;}

/* closing band */
/* The closing statement is part of the document, not a panel dropped into it: no
   gradient, no bloom, no inversion, and the same left edge as every section above.
   Those were the only gradient and the only blurred glow on the site, which is why
   the block read as imported from somewhere else. */
.closing{border-top:1.5px solid var(--line);padding:72px 0 40px;text-align:center;}
.closing h2{font-family:var(--fam-display);font-size:clamp(30px,4.2vw,52px);letter-spacing:-.03em;line-height:1.16;margin:0 auto;max-width:26ch;}
.closing .joinwrap{text-align:center;margin-top:46px;}

/* home flow: centered single column, generous rhythm */
.home-flow{max-width:1240px;margin:0 auto;}
.home-flow .sec{margin-top:120px;}
.home-flow .sec:first-child{margin-top:96px;}
@media(max-width:640px){.home-flow .sec{margin-top:80px;}}

/* stack panel padding now that it is a card */
.stack{padding:26px;margin-top:36px;}


/* ============ v18: inline manifesto accordion, comparison, big footer, per-element reveals ============ */

/* per-element reveals own all motion now; sections render immediately */
.rv{opacity:1;transform:none;transition:none;}
.rv.in .card,.rv.in .pf,.rv.in .step,.rv.in .chl,.rv.in .fq{animation:none;}
.ru{opacity:0;transform:translateY(24px);transition:opacity .65s cubic-bezier(.2,.7,.2,1),transform .65s cubic-bezier(.2,.7,.2,1);}
.ru.in{opacity:1;transform:none;}
@media(prefers-reduced-motion:reduce){.ru{opacity:1;transform:none;transition:none;}}

/* record + bench inside rounded cards: soft internal rules, breathing room */
.record{padding:6px 28px;}
.record .r{border-bottom:1px solid var(--hair);padding:16px 0;}
.record .r:last-child{border-bottom:none;}
.bench{padding:26px 28px;}
.b-row{border-bottom:none;}

/* mission: wider single column */
.home-flow .mch-body p{max-width:74ch;font-size:17px;}

/* top bar CTA */
.tb-cta{margin-left:4px;}
@media(max-width:640px){.tb-cta{display:none;}}

/* manifesto accordion */
.chx{border-radius:18px;border:1px solid var(--hair);background:var(--paper);box-shadow:var(--shadow);overflow:hidden;}
.chx summary{display:grid;grid-template-columns:44px minmax(0,220px) 1fr 30px;gap:18px;align-items:center;padding:22px 26px;cursor:pointer;list-style:none;}
.chx summary::-webkit-details-marker{display:none;}
.chx .cn{font-family:'IBM Plex Mono',monospace;font-size:11px;color:var(--sig);}
.chx b{font-size:16.5px;letter-spacing:-.01em;}
.chx .ct{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);}
.chx .pl{font-family:'IBM Plex Mono',monospace;font-size:18px;color:var(--mut);transition:transform .3s ease;text-align:center;}
.chx[open] .pl{transform:rotate(45deg);}
.chx:hover{box-shadow:var(--shadow-h);}
.chx-b{padding:2px 26px 28px;max-width:76ch;}
.chx-b p{font-size:16px;line-height:1.8;margin-bottom:16px;}
.chx-b .record{margin-top:10px;}
.chx-b .joinwrap{text-align:left;margin-top:18px;}
@media(max-width:700px){.chx summary{grid-template-columns:34px 1fr 24px;}.chx .ct{display:none;}}

/* comparison */
.diff{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-top:40px;}
@media(max-width:820px){.diff{grid-template-columns:1fr;}}
.dcol{border-radius:20px;border:1px solid var(--hair);background:var(--paper);box-shadow:var(--shadow);padding:30px 30px 16px;}
.dcol.di{border-color:rgba(31,58,255,.35);box-shadow:var(--shadow),0 0 0 1px rgba(31,58,255,.18);}
.dh{font-family:var(--fam-display);font-weight:700;font-size:19px;letter-spacing:-.01em;margin-bottom:12px;}
.dcol.di .dh{color:var(--sig);}
.dr{font-size:14.5px;line-height:1.6;padding:13px 0 13px 26px;border-bottom:1px solid var(--hair);position:relative;color:var(--fg);}
.dcol .dr:last-child{border-bottom:none;}
.dr::before{position:absolute;left:0;font-family:'IBM Plex Mono',monospace;font-size:13px;}
.dcol.di .dr::before{content:"+";color:var(--sig);}
.dcol.do .dr::before{content:"\2013";color:var(--mut);}
.dcol.do .dr{color:var(--mut);}

/* big footer */
footer.bigfoot{margin-top:120px;border-top:1.5px solid var(--line);padding:56px 0 28px;display:block;}
.bf-grid{display:grid;grid-template-columns:minmax(0,1.6fr) repeat(3,minmax(0,1fr));gap:40px;}
@media(max-width:820px){.bf-grid{grid-template-columns:1fr 1fr;}}
@media(max-width:520px){.bf-grid{grid-template-columns:1fr;}}
.bf-mark{font-family:var(--fam-display);font-weight:700;font-size:20px;letter-spacing:-.01em;}
.bf-mark .v{color:var(--sig);}
.bf-brand p{font-family:'IBM Plex Mono',monospace;font-size:11px;line-height:1.8;color:var(--mut);margin-top:14px;max-width:34ch;}
.bf-h{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--mut);margin-bottom:16px;}
.bf-col{display:flex;flex-direction:column;gap:11px;}
.bf-col a{font-size:14px;color:var(--fg);text-decoration:none;width:fit-content;}
.bf-col a:hover{color:var(--sig);}
.bf-legal{display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-top:52px;padding-top:22px;border-top:1px solid var(--hair);font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.08em;color:var(--mut);text-transform:uppercase;}

/* ============ v19: pinned builds restored on the rounded system ============ */
.faq{margin-top:36px;}
.ru{opacity:1;transform:none;transition:none;}

/* ============ v20: spacing & alignment pass ============ */
/* FAQ: cards keep their internal padding; more air under the // FAQ line and under each opened answer */
.home-flow .faq{margin-top:48px;gap:14px;}
.fq summary{padding:24px 28px;}
.fq .fq-a{padding:2px 28px 26px;}
.fq .fq-a p{margin-top:0;}
/* Stack research questions: legible and tucked close under their heading */
#stack .exp{font-size:13.5px;line-height:1.95;letter-spacing:0;}
#stack .exp li{margin-top:7px;}
#stack .exp li:first-child{margin-top:0;}
/* uniform breathing room beneath each section-head rule in the home flow */
.home-flow .sec-head{margin-bottom:38px;}

/* ============ v21: section decks (descriptor as readable subtitle, not a mono eyebrow) ============ */
.sec-deck{font-family:var(--fam-display);font-size:clamp(15px,1.7vw,19px);line-height:1.42;letter-spacing:-.015em;color:var(--mut);max-width:54ch;margin:-22px 0 34px;}
@media(max-width:640px){.sec-deck{margin-top:-8px;}}

/* ============ v22: audit pass — P1 (rhythm+borders), cut floating +, P2.E stack, P3 ============ */

/* P2.D — remove decorative section crosshair "+" marks */
.sec::after{display:none;}

/* P1-B — drop the harsh full-contrast rule above the FAQ; soften the section-head hairline */
.faq{border-top:none;}
.sec-head::before{background:linear-gradient(90deg,var(--hair) 0%,var(--hair) 60%,transparent 100%);}

/* P1-C — FAQ: kill the void above the cards, calm the card height */
.home-flow .faq{margin-top:6px;gap:10px;}
.fq summary{padding:20px 28px;}
.fq .fq-a{padding:2px 28px 24px;}

/* P1-A — one rhythm: uniform gap between a section's lead text and its first component */
.home-flow .record,.home-flow .chlist,.home-flow .steps{margin-top:30px!important;}

/* P2.E — Stack: research questions as a distinct, legible divided list */
#stack .exp{font-size:14px;line-height:1.55;letter-spacing:0;color:var(--fg);}
#stack .exp li{margin-top:0;padding:11px 0;border-bottom:1px solid var(--hair);}
#stack .exp li:last-child{border-bottom:none;}
#stack .exp li::before{content:"+  ";color:var(--sig);}

/* P3.F — inner-page hero ledes share one calm voice */
.hero.inner .hero-lede{font-family:var(--fam-display);font-size:16px;line-height:1.6;color:var(--mut);letter-spacing:0;}

/* P3.G — hairlines a touch more present for definition */
:root{--hair:rgba(20,23,28,.22);}
[data-theme="dark"]{--hair:rgba(233,235,238,.20);}

/* P4.A — full-contrast emphasis: home hero + base-layer research question.
   var(--fg) flips per theme (#e9ebee dark / #14171c light), so this reads as
   maximum contrast in both rather than a hard-coded white. */
.hero-c h1 .acc{color:var(--fg);font-weight:700;}
.hero-c .hero-lede{color:var(--fg);font-weight:700;}
.lead-em{color:var(--fg);font-weight:700;}

/* ============ manifesto as a single read ============
   The manifesto page drops the chapter numerals, the pinned scenes, and the
   index rail. One centred column, subheads, continuous scroll: it should feel
   like a long essay rather than a spec with sections. Title and body share the
   same measure, so the page reads as one column with even margins either side
   rather than text pinned to the left edge.
   Everything inside sits on the same measure: kicker, headline, lede, subheads,
   prose, the record block, the closing button, and the footer rule all start and
   end on the same two vertical lines. Anything that keeps its own max-width has
   to be released explicitly, or its right edge steps in and breaks the column. */
.reading{max-width:82ch;margin:0 auto;}
.reading .hero{text-align:left;padding-top:76px;}
.reading .hero h1{max-width:none;font-size:clamp(34px,4.4vw,52px);}
.reading .hero-lede{max-width:none;}
.reading ~ footer{max-width:82ch;margin-left:auto;margin-right:auto;}
.essay{margin-top:8px;}
/* The closing button is the one thing that sits centred: it ends the read rather
   than continuing it, so it reads as a full stop instead of another line of text. */
.essay .joinwrap{text-align:center;margin-top:48px;}
.mvt{margin-top:56px;scroll-margin-top:96px;}
.mvt:first-child{margin-top:64px;}
.mvt-h{font-family:var(--fam-display);font-size:clamp(23px,2.6vw,32px);line-height:1.15;letter-spacing:-.025em;font-weight:700;color:var(--fg);margin:0 0 18px;}
/* Newspaper setting: both edges of the column run flush. Hyphenation is what keeps
   justification honest, otherwise the browser pays for the flush right edge with
   rivers of white space between words. The limits stop it hyphenating short words
   or leaving one or two stranded letters at a line break. */
.essay .mch-body p{max-width:none;font-size:17px;line-height:1.82;margin:0 0 22px;text-align:justify;-webkit-hyphens:auto;hyphens:auto;-webkit-hyphenate-limit-before:4;-webkit-hyphenate-limit-after:4;hyphenate-limit-chars:8 4 4;}
/* A narrow column cannot absorb justification: gaps get wide enough to read as
   holes, so phones fall back to a ragged right edge. */
@media(max-width:640px){.mvt{margin-top:44px;}.reading .hero{padding-top:56px;}
.essay .mch-body p{font-size:16px;text-align:left;-webkit-hyphens:none;hyphens:none;}}

/* ============ the Join step opens the concierge ============
   The whole card is the hit area. It carries the signal border and an explicit
   link line at rest, so it reads as clickable without needing a hover. The card
   itself is a div (a button cannot wrap headings and paragraphs); the button is
   the CTA line, stretched over the card by its ::after, so every click on the
   card lands on the button. */
.step-link{position:relative;display:block;text-decoration:none;color:inherit;cursor:pointer;border-color:var(--sig);transition:box-shadow .25s ease,transform .25s ease;}
.step-link:hover,.step-link:focus-within{box-shadow:0 0 0 1.5px var(--sig),var(--shadow);transform:translateY(-2px);}
.step-cta{display:inline-flex;align-items:center;gap:8px;margin-top:16px;padding:0;border:0;background:none;cursor:pointer;font-family:'IBM Plex Mono',monospace;font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--sig-text);text-decoration:underline;text-underline-offset:4px;}
.step-cta::after{content:"";position:absolute;inset:0;}
.step-link:hover .lk-arrow{transform:translateX(3px);}
.lk-arrow{transition:transform .25s ease;}
@media(prefers-reduced-motion:reduce){.step-link,.lk-arrow{transition:none;}.step-link:hover{transform:none;}}

/* ============ the home hero owns the first screen ============
   The index rail belongs to the scrolling body, so it used to peek into the
   bottom left corner of the hero: one element hanging off to one side of an
   otherwise centred, symmetrical composition. Giving the hero a screen of its
   own puts the rail below the fold, where it appears the moment it becomes
   useful. 100svh rather than 100vh so mobile browser chrome cannot push the
   button off screen. */
.hero.hero-c{min-height:calc(100svh - 72px);box-sizing:border-box;display:flex;flex-direction:column;justify-content:center;padding:56px 0 72px;}
.hero-c h1{max-width:34ch;font-size:clamp(32px,4.2vw,56px);}
@media(max-width:860px){.hero.hero-c{min-height:0;padding:56px 0 24px;}}

/* ============ no orphaned words ============
   text-wrap:pretty tells the browser to look ahead at the last few lines of a
   block and rebalance them rather than leaving one word alone on the final line.
   Headings get balance instead, which evens out every line rather than just the
   end. Short words stranded mid-paragraph are handled in the generator, which
   binds them to the following word with a non-breaking space. */
p,li,figcaption,.hero-lede,.sec-deck,.v,.dr{text-wrap:pretty;}
h1,h2,h3,.mvt-h,.tp-name,.dh{text-wrap:balance;}

/* ============ inner-page heroes use a wider measure ============
   15ch of headline and 56ch of intro stopped around the halfway mark, so the
   right half of every inner page read as empty rather than as breathing room. */
.hero.inner h1{max-width:22ch;}
.hero.inner .hero-lede{max-width:72ch;}
/* the manifesto is a centred column and governs its own hero measure */
.reading .hero.inner h1,.reading .hero.inner .hero-lede{max-width:none;}

/* ============ the footer follows the column it sits under ============
   On indexed pages the body occupies the right-hand column of the rail grid, so a
   full-width footer rule crosses the page at a different left edge from the closing
   rule directly above it: two parallel lines that do not start in the same place.
   200px rail plus the 64px grid gap. Below 900px the rail stacks and the offset goes. */
.railgrid ~ footer{margin-left:264px;}
@media(max-width:900px){.railgrid ~ footer{margin-left:0;}}

/* ============ inner pages arrive whole ============
   The hero staggers its parts in: headline at .45s, intro at .6s. On the home page
   that is a designed entrance. On a tab you clicked deliberately it just means the
   intro drops in after the headline has settled, which reads as a glitch rather
   than as motion. Inner pages load complete. */
.hero.inner .kicker,.hero.inner .themes,.hero.inner h1,.hero.inner .hero-lede,.hero.inner .joinwrap,.hero.inner .joinnote{animation:none;opacity:1;transform:none;}

/* ============ blog placeholder cards ============
   Marked as placeholders so a reviewer cannot mistake one for a published post: a
   dashed edge rather than a solid one, no shadow, and the date slot says so. */
.bcard-ph{border-style:dashed;box-shadow:none;cursor:default;}
.bcard-ph .bc-img.ph{border-style:dashed;background:var(--bg);}
.bcard-ph h3,.bcard-ph p{color:var(--mut);}

/* ============ why-section diagram placeholders ============
   Same contract as the blog placeholder cards above: a dashed edge and muted
   ink, so a reviewer cannot mistake the frame for a finished figure. The box
   text describes what the final figure must illustrate; the designer draws it
   (copy/add-why). */
.figph{border:1.5px dashed var(--mut);margin:34px 0;padding:34px 30px;}
.figph .figph-tag{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--mut);}
.figph p{margin:10px 0 0;font-size:14.5px;line-height:1.65;color:var(--mut);max-width:56ch;}

/* ============ citation links + the thesis claim (copy/add-why) ============
   a.ref keeps citation links quiet: inherited ink, a muted underline, no new
   colour on the surface; the small angled arrow marks the link as external.
   The thesis claim carries the page's one set-ultramarine underline (--sig-text
   is the text-safe signal), with italic doing the emphasis bold used to do. */
a.ref{color:inherit;text-decoration:underline;text-decoration-color:var(--mut);text-underline-offset:3px;}
a.ref:hover{text-decoration-color:var(--fg);}
a.ref .ext{width:10.8px;height:10.8px;margin-left:2px;color:var(--mut);position:relative;top:-1px;}
a.ref:hover .ext{color:var(--fg);}
em.claim{font-style:italic;text-decoration:underline;text-decoration-color:var(--sig-text);text-decoration-thickness:1.5px;text-underline-offset:4px;}

/* ============ home hero lede ============
   The centred home lede was set at 700 further up. It now carries a long,
   deliberately open-ended paragraph, and at that length the bold reads as
   shouting rather than emphasis. Regular weight, same colour. */
.hero-c .hero-lede{font-weight:400;}

/* ============ mobile ============
   Read from the stylesheet rather than from a device, so these are conservative:
   nothing changes above 700px, and every rule below is additive.

   1. The top bar. RETIRED 2026-08-27. This block turned the nav into a sheet
      below 700px only; the v29 block at the end of this file does that at every
      width, and its rules sit later in the file at the same specificity, so they
      won here anyway. Left as a marker rather than a live rule set. What is kept
      below is the part that is still purely about small screens: the tighter bar
      padding and the 44px targets. */
@media(max-width:700px){
  .topbar{flex-wrap:nowrap;padding:12px 20px;}
  .tb-right{gap:4px;}
  .toggle{width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;}
}

/* 2. Card padding. .dcol and the accordion kept desktop padding at every width. On a
      375px screen .doc takes 48px and .dcol took another 60px, leaving 267px of
      measure, the tightest column on the site. */
@media(max-width:560px){
  .dcol{padding:20px 20px 12px;}
  .chx summary{padding:18px 18px;gap:12px;}
  .chx-b{padding:2px 18px 22px;}
  .chx-b p{font-size:15.5px;line-height:1.75;}
  /* the section number and slash eat ~60px before the title starts */
  .sec-head .sl{display:none;}
  .sec-head .n{min-width:0;}
  .sec-head{gap:10px;}
}

/* 3. The section index rail (Home and Library). Above 900px it is a sticky vertical
      rail beside the content, which works. Below 900px the grid collapses to one
      column and the rail turns into a horizontally scrolling strip of [01] [02] …
      pinned above the page, boxed in top and bottom borders. It reads as a stray
      toolbar, duplicates a scroll the reader is about to do anyway, and is the first
      thing under the header. Hidden instead.
      .railgrid ~ footer{margin-left:0} is already set at this breakpoint, so there
      is no leftover indent once the column is gone. */
@media(max-width:900px){
  .railgrid .pagenav{display:none;}
}

/* ── Home hero: type scale and measure ───────────────────────────────────────
   The centred home hero was the only hero on the site taking the weak lede
   treatment. .hero-c re-overrode the house lede (Space Grotesk 19px at --fg,
   set earlier in this file) back down to IBM Plex Mono 13px at --mut, capped
   at 66ch. That put the page's most important paragraph at the smallest size
   on the site, in the lowest-contrast colour, in a monospace face, across the
   widest measure. Monospace is fine for the record plates and terminal blocks;
   it is the wrong tool for a running paragraph this long.

   Against a 56px h1, a 13px lede is a 4.3:1 step. Convention for this format
   is 2.5:1 to 4:1 (h1 48-72px, lede 18-22px). The values below land at 3.4:1.

   The h1 measure drops 34ch -> 20ch so the headline breaks deliberately into
   two balanced lines rather than overflowing one. text-wrap:balance is already
   set on .hero h1 and does the work once the measure lets it; the &nbsp; binds
   that were briefly on these headlines fought it and have been removed. */
.hero-c h1{
  max-width:20ch;
  font-size:clamp(36px,4.6vw,64px);
  line-height:1.08;
}
.hero-c .hero-lede{
  font-family:var(--fam-display);
  font-size:clamp(16.5px,2.2vw,19px);
  line-height:1.6;
  letter-spacing:0;
  font-weight:400;
  color:var(--fg);
  max-width:54ch;
  margin-top:28px;
}
@media(max-width:640px){
  .hero-c h1{max-width:16ch;font-size:clamp(34px,8.4vw,46px);}
  .hero-c .hero-lede{max-width:46ch;margin-top:22px;}
}

/* ============================================================================
   v23 · Research-press pass
   ----------------------------------------------------------------------------
   Four changes, agreed 2026-08-16, in response to "it looks like AI built it".

   1. TYPE. Space Grotesk plus mono plus electric blue is the default costume of
      every protocol landing page since about 2021, and the display face was the
      loudest single element. Running prose moves to Source Serif 4, headings to
      Archivo, and IBM Plex Mono keeps the data plates. IOVI publishes papers and
      models itself on the Linux Foundation and RISC-V, so a journal register is
      the honest one. The wordmark is SVG paths, so it is untouched.
   2. SURFACES. 18px radius plus two-layer drop shadows made every block read as
      a dashboard card. Radius drops to 4px and the shadows go; hairlines carry
      the definition, and hover moves the border rather than lifting the box.
   3. ACCENT. Ultramarine was doing brand, state, ornament and emphasis all at
      once, which is why the page looked busy and why eleven elements failed
      contrast in dark mode. It is now restricted to two roles: the primary
      action, and the current/active state. Ornament goes to --mut.
   4. COMPARISON. The two tinted + / - cards become a plain table. Markup change
      lives in gen_alt.py; the styling is at the end of this block.

   No copy was changed anywhere in this pass.
   ============================================================================ */

/* --- 1. type ------------------------------------------------------------- */
/* The v23 faces, Source Serif 4 for prose and Archivo for display, were
   superseded by v29 near the end of this file, which sets both to Inter. Custom
   properties resolve from the final cascade rather than in source order, so the
   later :root won everywhere and these two lines had no effect on anything.
   Their @font-face rules and their seven woff2 files were removed 2026-09-15.
   Nothing matched the families, so no browser was ever downloading them; they
   only made the type story take three reads to follow.

   --fam-mono stays. It is the ONLY definition of that token in the codebase and
   eight rules consume it, so deleting this block wholesale kills all mono type. */

:root{
  --fam-mono:'IBM Plex Mono',ui-monospace,Menlo,monospace;
}

/* Serif at a given pixel size reads smaller than a grotesque, so the text scale
   moves up about a point and the leading opens slightly. Headings tighten,
   because Archivo carries a lower cap-to-x ratio than Space Grotesk did. */
body{font-family:var(--fam-text);font-size:17px;line-height:1.6;}

h1,h2,h3,h4,.sec-head b,.sec-head h2,.mvt-h,.dh,.tp-name,.sd-name,.bf-mark,
.card h3,.step h3,.chx b,.chl b,.layer b,.sb-layer b,.b-l,.as-name,
.closing h2,.hero h1{
  font-family:var(--fam-display);
  font-weight:700;
  letter-spacing:-.022em;
}
.tr-item{font-family:var(--fam-display);}

/* Running prose. Everything here was Space Grotesk and is now serif; the sizes
   are nudged to hold the same physical measure at the same character count. */
.mch-body p,.chx-b p,.fq .fq-a p,.prose p,.cols p,.card p,.step p,
.tp-desc,.tp-body p,.sd-desc,.sb-desc,.blog-empty,.lead{
  font-family:var(--fam-text);
}
.home-flow .mch-body p{font-size:18px;line-height:1.68;max-width:70ch;}
.chx-b p{font-size:17.5px;line-height:1.7;}
.fq .fq-a p{font-size:16.5px;line-height:1.7;}
.step p{font-size:16px;line-height:1.6;}
.card p{font-size:15.5px;line-height:1.6;}
.essay .mch-body p{font-size:18.5px;line-height:1.72;}

/* One paragraph class replaces ten copies of the same inline style, which had
   drifted to two different measures and made adjacent sections step in and out. */
.lead{font-size:17px;line-height:1.68;max-width:70ch;margin:0;}
.lead.mut{color:var(--mut);}
.lead-h{font-family:var(--fam-display);font-size:20px;font-weight:600;color:var(--fg);
        letter-spacing:-.018em;line-height:1.35;max-width:70ch;margin:36px 0 12px;}

/* The lede and the section decks stay serif: they are prose, not labels. */
.hero-lede,.hero-c .hero-lede,.hero.inner .hero-lede,.sec-deck{font-family:var(--fam-text);}
.hero-c .hero-lede{font-size:clamp(17.5px,2.2vw,20px);line-height:1.55;letter-spacing:0;}
.sec-deck{font-size:clamp(16px,1.7vw,19px);line-height:1.45;letter-spacing:-.006em;font-style:italic;}
.hero h1,.hero-c h1{letter-spacing:-.028em;}
.closing h2{letter-spacing:-.026em;}

/* Mono stays mono, but the accordion gloss was 10px uppercase with .14em tracking,
   which is the least readable setting on the site applied to some of its most
   useful writing. Sentence case at 12px; the source strings are already lowercase. */
.chx .ct,.chl .ct{font-size:12px;letter-spacing:.01em;text-transform:none;color:var(--mut);}

/* --- 2. surfaces --------------------------------------------------------- */
/* Kept as a valid shadow value rather than `none`, because .step-link:hover
   composes var(--shadow) into a comma list and `none` inside a list is invalid
   CSS, which would drop the whole declaration. */
:root,[data-theme="dark"]{--shadow:0 0 0 0 transparent;--shadow-h:0 0 0 0 transparent;}

.card,.pf,.fq,.step,.chl,.chx,.stack,.record,.bench,.dcol,
.stack-detail,.track-viewer,.specb,.layer,.sb-layer,.tr-item,
.blog-empty,.b-pending,.modal-card,.bcard .bc-img,.type-sample{border-radius:4px;}
.sk,.toggle,.yours{border-radius:3px;}
.joinbtn{border-radius:3px;}
.sw,.b-track{border-radius:2px;}

/* Hover moves the edge instead of lifting the box. */
a.card:hover,.card:hover,a.pf:hover,.chl:hover,.chx:hover,.step-link:hover{
  transform:none;box-shadow:none;border-color:var(--line);
}
.step-link:hover,.step-link:focus-within{box-shadow:inset 0 0 0 1px var(--sig);transform:none;}
.dcol.di{box-shadow:none;}

/* --- 3. accent discipline ------------------------------------------------ */
/* Ultramarine now means "the action" and "you are here". Everything that was
   using it as ornament moves to --mut, which also clears the dark-mode contrast
   failures at the source rather than by lightening the blue. */
.sec-head .n,.chx .cn,.chl .cn,.step .sn,.card .tag,.pf .tag{color:var(--mut);}
#stack .exp li::before{color:var(--mut);}
.fq[open] summary::after{color:var(--fg);}
.bf-col a:hover{color:var(--fg);}

/* What stays blue is state, and it gets the lightened token in dark mode so it
   clears 4.5:1. The v14 rule aimed at .steps .stn, a class the generator never
   emits (it writes .step .sn), which is why these went unfixed for two weeks. */
.railgrid .pagenav a.on .rn,.tr-item.active .trn,.tr-item.match .trn,
.step-cta,.term .tp1{color:var(--sig-text);}
.tr-item.match::after{background:var(--sig-text);}

/* --- 4. the comparison table --------------------------------------------- */
.dtable{width:100%;border-collapse:collapse;margin-top:34px;table-layout:fixed;}
.dtable th{
  font-family:var(--fam-display);font-size:15px;font-weight:700;letter-spacing:-.01em;
  text-align:left;padding:0 28px 12px 0;border-bottom:1.5px solid var(--line);color:var(--fg);
}
.dtable th:last-child,.dtable td:last-child{padding-right:0;color:var(--mut);}
.dtable td{
  font-family:var(--fam-text);font-size:16.5px;line-height:1.5;vertical-align:top;
  padding:16px 28px 16px 0;border-bottom:1px solid var(--hair);
}
.dtable tr:last-child td{border-bottom:none;}
@media(max-width:640px){
  /* Two 16px columns will not fit a phone. Each row becomes a stacked pair, with
     the column headings repeated inline so the pairing survives the reflow. */
  .dtable,.dtable tbody,.dtable tr,.dtable td{display:block;width:100%;}
  .dtable thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);}
  .dtable tr{padding:14px 0;border-bottom:1px solid var(--hair);}
  .dtable tr:last-child{border-bottom:none;}
  .dtable td{padding:0;border:none;}
  .dtable td+td{margin-top:6px;}
  .dtable td::before{
    content:attr(data-l);display:block;font-family:var(--fam-mono);font-size:9.5px;
    letter-spacing:.16em;text-transform:uppercase;color:var(--faint,var(--mut));margin-bottom:3px;
  }
}

/* --- 5. layout corrections ----------------------------------------------- */
/* A two-card row stretched the shorter card to match its neighbour, leaving
   about 80px of dead space inside it. */
.cards,.cards.two{align-items:start;}
/* The skill chips were centred inside an otherwise left-aligned section. */
.skills{justify-content:flex-start;}
/* One card in the blog row should not stretch across a third of the page. */
.bloggrid.bg-1{grid-template-columns:minmax(0,380px);}
.bloggrid.bg-2{grid-template-columns:repeat(2,minmax(0,1fr));max-width:780px;}
/* The library page no longer sits in a rail grid, so its footer loses the indent. */

/* The skip control sat at 10px in .3-alpha grey on black, which is hard to find at
   the moment you most want it. Full contrast, 11px, and a real hit target. */
.iv-skip{font-size:11px;color:#e9ebee;border-color:rgba(233,235,238,.55);padding:11px 18px;}
.iv-skip:hover{background:rgba(233,235,238,.10);}
/* The veil is a dark moment in a light-default site; its type follows suit. */
.iv-type{font-family:var(--fam-mono);}
.iv-line{font-family:var(--fam-mono);}

/* Two tracks do not earn a 240px vertical rail: the column sat empty for most of
   the panel's height. The rail becomes a row above the viewer, and stays a rail
   only if the group list ever grows past four. */
.trackgrid{grid-template-columns:minmax(0,1fr);gap:16px;}
.track-rail{position:static;flex-direction:row;flex-wrap:wrap;gap:8px;}
.tr-item{flex:0 0 auto;}
/* The signal border on the onboarding card is the exact ultramarine, which all but
   disappears against the dark ground. State colour follows --sig-text in dark. */
.step-link{border-color:var(--sig-text);}
.step-link:hover,.step-link:focus-within{box-shadow:inset 0 0 0 1px var(--sig-text);}

/* ============================================================================
   v24 · Corrections from review, 2026-08-16
   ============================================================================ */

/* --- manifesto measure: keep the original wide column ---------------------
   NOTE: never write a literal comment terminator inside a comment here. Doing
   exactly that is what silently killed .reading for two weeks, because CSS
   error recovery swallows the following rule along with the stray text.

   The v23 pass closed that orphaned terminator, which made .reading apply for
   the first time and narrowed the essay to 82ch. Cara's call: the wide measure
   is the intended one and 82ch reads too narrow. The rule stays valid but
   releases the width,
   so the essay sits at the full document measure as it always did. The real
   defect was never the width: it was that .reading ~ footer DID parse, so the
   footer sat on a different left edge from the text above it. Both now run to
   the same two vertical lines. */
.reading{max-width:none;margin:0;}
.reading ~ footer{max-width:none;margin-left:0;margin-right:0;}

/* --- the // eyebrow is gone from the markup; retire its style ------------- */
.sec-head .sl{display:none;}

/* --- vertical rhythm ------------------------------------------------------
   The old spacing was tuned around Space Grotesk at 15-17px and a mono deck.
   Serif text sits on a different baseline and three sections lost their deck
   entirely, so the section head had a negative-margin hack compensating for a
   gap that no longer exists. One rhythm, stated once, in multiples of 2px:

     section rule    -> 20px -> heading -> 14px -> deck -> 30px -> body
     last body block -> 84px -> next section rule
*/
.home-flow .sec{margin-top:84px;}
.home-flow .sec:first-child{margin-top:72px;}
.sec-head{padding-top:20px;margin-bottom:0;gap:14px;}
.sec-deck{margin:14px 0 0;}
/* One uniform gap between a section's head (or deck) and whatever follows,
   whichever of the two comes last. Replaces the v22 !important patch and the
   inline margin-top values that were stripped out of the generator. */
.home-flow .sec-head + *,
.home-flow .sec-deck + *,
.sec-head + *,
.sec-deck + *{margin-top:30px;}
/* .record/.chlist/.steps carried margin-top:30px!important from v22, which
   would otherwise beat the rule above in the cases where 30px is not wanted. */
.home-flow .record,.home-flow .chlist,.home-flow .steps{margin-top:30px!important;}
.home-flow .faq{margin-top:30px;gap:10px;}
.home-flow .mch-body p:last-child{margin-bottom:0;}
/* Body blocks that follow prose rather than a heading. */
.mch-body + .record,.mch-body + .chlist,.mch-body + .diff,.mch-body + .dtable,
.lead + .record,.lead + .dtable,.lead + .exp{margin-top:30px;}
.lead-h{margin:34px 0 10px;}
#stack .exp{margin-top:0;}
.dtable{margin-top:30px;}
.steps{margin-top:30px;}
.callout{margin-top:30px;}
.closing{padding:64px 0 36px;}
@media(max-width:640px){
  .home-flow .sec{margin-top:60px;}
  .sec-head{padding-top:16px;}
}

/* --- scroll motion retired ------------------------------------------------
   Companion to the guards in v8-extra.js and iovi.js. The scene engine wrote
   inline opacity and transform onto every unit and onto the hero, so these use
   !important: an author !important declaration beats a non-important inline
   style, which means a stale cached script cannot leave content invisible.
   Sections are now as tall as their content, and everything renders at once. */
.scene .scn-wrap{height:auto!important;}
.scene .scn-stage{position:static!important;top:auto!important;min-height:0;padding-bottom:0;}
.scn-u,.epi,.band{opacity:1!important;transform:none!important;}
.hero,.hero:not(.inner){opacity:1!important;transform:none!important;visibility:visible!important;}
.reveal,.rv,.ru{opacity:1!important;transform:none!important;transition:none!important;}

/* --- rhythm, corrected specificity ---------------------------------------
   The v24 block above stated the rhythm but lost two fights: v20's
   `.home-flow .sec-head{margin-bottom:38px}` outranked a bare `.sec-head`,
   and `.sec-head + *` matched the deck itself, handing it the 30px body gap
   instead of its own 14px. Both are pinned here, and the deck is excluded
   from the body-gap rule by name.

     rule -> 20px -> heading -> 14px -> deck -> 30px -> first body block
     rule -> 20px -> heading -> 30px -> first body block   (no deck)
*/
.sec-head,.home-flow .sec-head{margin-bottom:0;}
.sec-head + .sec-deck,.home-flow .sec-head + .sec-deck{margin-top:14px;}
.sec-head + :not(.sec-deck),
.home-flow .sec-head + :not(.sec-deck),
.sec-deck + *,
.home-flow .sec-deck + *{margin-top:30px;}
/* A section head whose deck was deleted must not keep the deck's old offset. */
.sec-deck{margin-bottom:0;}

/* ============================================================================
   v25 · Review round two, 2026-08-16
   ============================================================================ */

/* --- alignment ------------------------------------------------------------
   The section numeral sat in the head with min-width:36px and a 14px gap, so
   every h2 started ~50px right of the deck and body beneath it: three different
   left edges inside one section. Numeral and anchor are out of the markup; this
   clears their styles and puts the head on the same edge as everything else. */
.sec-head{display:block;padding-top:16px;margin:0;}
.sec-head .n,.sec-head .sl,.anch{display:none;}
.sec-head h2,.sec-head b{margin:0;}

/* --- the corner brackets --------------------------------------------------
   Four L-shaped marks pinned to the viewport corners. It is a real device with
   a real history (crop and registration marks on press sheets) but it has been
   the house style of technical and crypto landing pages for years, so it now
   reads as costume rather than as craft. Removed; the class stays so it can be
   restored by deleting one line. */
.frame{display:none;}

/* --- spacing: one tighter scale ------------------------------------------
   "assume all spacing is tighter, it looks poorly made". Every gap below is a
   multiple of 4px and steps down from the previous pass.

     rule -> 16 -> heading -> 24 -> body -> 60 -> next rule
*/
.home-flow .sec,.sec{margin-top:60px;}
.home-flow .sec:first-child{margin-top:52px;}
.sec-head + .sec-deck,.home-flow .sec-head + .sec-deck{margin-top:10px;}
.sec-head + :not(.sec-deck),
.home-flow .sec-head + :not(.sec-deck),
.sec-deck + *,
.home-flow .sec-deck + *{margin-top:24px;}
.home-flow .record,.home-flow .chlist,.home-flow .steps{margin-top:24px!important;}
.home-flow .faq{margin-top:24px;gap:8px;}
.chlist{gap:8px;}
.dtable,.steps,.callout,.diff{margin-top:24px;}
.mch-body + .record,.mch-body + .chlist,.mch-body + .dtable,
.lead + .record,.lead + .dtable,.lead + .exp{margin-top:24px;}
.lead-h{margin:28px 0 8px;}
.cards,.proof,.skills,.trackgrid{margin-top:24px;}
.mch-body p{margin-bottom:14px;}

/* the closing call to action and the footer beneath it */
.closing{padding:48px 0 28px;}
.closing .joinwrap{margin-top:32px;}
footer,footer.bigfoot{margin-top:56px;padding-top:20px;}

@media(max-width:640px){
  .home-flow .sec,.sec{margin-top:44px;}
  .sec-head{padding-top:14px;}
}

/* --- hero: one group, not four stranded objects ---------------------------
   The mark, headline, lede and button were spread down a full viewport with
   ~170px of air between the mark and the headline and a long six-line lede,
   so the block read as scattered rather than composed. The measure narrows so
   the lede sets in four lines, the internal gaps tighten, and the hero no
   longer reserves a whole screen it cannot fill. */
.hero.hero-c{min-height:0;padding:64px 0 56px;display:block;}
.hero-c .hero-art{margin:0 auto 20px;}
.hero-c .hero-art svg{height:56px;}
.hero-c h1{margin:0 auto;max-width:18ch;font-size:clamp(38px,5.2vw,68px);line-height:1.04;}
.hero-c .hero-lede{margin:20px auto 0;max-width:46ch;font-size:clamp(16px,1.9vw,18px);line-height:1.55;}
.hero-c .joinwrap{margin-top:28px;}
@media(max-width:860px){.hero.hero-c{padding:44px 0 40px;}}
@media(max-width:640px){.hero-c h1{max-width:15ch;}.hero-c .hero-lede{max-width:38ch;}}

/* ============================================================================
   v29 · Inter carries the site, IBM Plex Mono carries the data
   ----------------------------------------------------------------------------
   This is the two-line change the v26 pass below was holding open. v26 withdrew
   the research-press serif pairing and set the target: one sans carrying
   everything with IBM Plex Mono for data, just not a face that announces itself
   as the default choice. Space Grotesk stayed live in the meantime so the
   structural work could be judged with the type held constant.

   Inter is the winner, chosen 2026-08-27 against two references Cara supplied:
   ludwig.ai runs Inter with Geist Mono and JetBrains Mono, and beeai.dev runs
   IBM Plex Sans with IBM Plex Mono. Both are one sans plus one mono and neither
   uses a serif, which is the same structure v26 asked for. Space Grotesk with
   IBM Plex Mono is the most recognisable crypto and AI startup template pairing
   there is, and that was the thing reading as generic.

   Superseded 2026-09-15: those blocks are gone. The Archivo and Source Serif 4
   @font-face rules and their seven woff2 files were deleted, and Space Grotesk's
   @font-face rules went with them. Two things this paragraph used to get wrong.

   First, an unused @font-face costs a visitor nothing. A browser fetches a face
   only when a family actually matches rendered text, and gen_alt.py preloads
   only inter-400, inter-700 and ibm-plex-mono-500, so none of these was ever
   being downloaded by anyone.

   Second, gen_og.py does not touch Archivo. It sets the OG card headline in
   space-grotesk-latin-700-normal, reading the woff2 straight from
   dist/assets/fonts rather than through this stylesheet. That is why all four
   Space Grotesk files are still on disk while their CSS rules are not, and why
   deleting them breaks the build.

   The other generators share iovi.css, not this file.
   ============================================================================ */
@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(fonts/inter-latin-400-normal.woff2) format('woff2');}
@font-face{font-family:'Inter';font-style:normal;font-weight:500;font-display:swap;src:url(fonts/inter-latin-500-normal.woff2) format('woff2');}
@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(fonts/inter-latin-600-normal.woff2) format('woff2');}
@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url(fonts/inter-latin-700-normal.woff2) format('woff2');}
/* Space Grotesk's @font-face rules were removed 2026-09-15. No element on this
   site matches the family: the v26 tokens that used it were superseded by Inter,
   and the one remaining reference is PARTNERS in gen_alt.py, a mock partner
   marquee that nothing emits.

   The woff2 FILES stay in static/assets/fonts deliberately. gen_og.py sets the
   OG card headline in space-grotesk-latin-700-normal, reading it straight from
   dist/assets/fonts rather than through this stylesheet, and build.sh runs that
   generator on every build. Deleting those files breaks the build. */

:root{
  --fam-display:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif;
  --fam-text:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif;
}

/* A grotesque sets larger on the body than a serif at the same pixel size, so
   the v23 scale (tuned up for Source Serif) comes back down to the values the
   live site was already using. */
body{font-family:var(--fam-text);font-size:16px;line-height:1.55;}
/* Tabular figures wherever numbers sit in a column or want to hold position. */
.record .v,.rn,.sn,.cn,.stamp,.fd-cap{font-variant-numeric:tabular-nums;}
.home-flow .mch-body p{font-size:17px;line-height:1.7;max-width:74ch;}
.chx-b p{font-size:16px;line-height:1.75;}
.fq .fq-a p{font-size:14.5px;line-height:1.75;}
.step p{font-size:14.5px;line-height:1.7;}
.card p{font-size:13.5px;line-height:1.65;}
.essay .mch-body p{font-size:17px;line-height:1.82;}
.lead{font-size:15px;line-height:1.7;max-width:74ch;}
.lead-h{font-size:19px;font-weight:600;letter-spacing:-.015em;line-height:1.4;}
.dtable td{font-family:var(--fam-text);font-size:14.5px;line-height:1.6;}
.dtable th{font-size:15px;}
.hero-c .hero-lede{font-family:var(--fam-text);letter-spacing:0;}
/* The italic deck was an editorial-serif gesture. Back to upright, muted. */
.sec-deck{font-style:normal;font-size:clamp(15px,1.6vw,17px);line-height:1.45;}
h1,h2,h3,.sec-head h2,.closing h2,.hero h1{letter-spacing:-.03em;}

/* Hero lede measure. Narrowing it to 46ch was the wrong direction: the string is
   ~310 characters, so a tighter measure pushed it from five lines to six and made
   the lede block taller than the two-line headline above it. Widening lets it set
   in four, which balances against the headline instead of competing with it. */
.hero-c .hero-lede{max-width:68ch;}
@media(max-width:640px){.hero-c .hero-lede{max-width:42ch;}}

/* --- v27 ------------------------------------------------------------------
   Two cards of very different length sat in one row. v23 set align-items:start
   to kill ~80px of dead space inside the shorter one, but that was my note from
   the audit, not a complaint, and the cure was worse: the two boxes ended at
   different heights and the row read as broken rather than as breathing. Boxes
   in a row match; the air inside the shorter one is the cost of using boxes. */
.cards,.cards.two,.steps{align-items:stretch;}

/* Dark is the default theme now, so the light palette is the alternate. Nothing
   about the tokens changes: both themes were already defined token-level, and
   the toggle still wins in either direction. */

/* --- v28: index rail tap targets -----------------------------------------
   Found by the post-deploy audit. The rail links were 15.5px tall, under the
   24px minimum (WCAG 2.5.8). Padding goes on, the flex gap comes off by the
   same amount, so each row still sits 26.5px from the next and the rail looks
   identical: only the hit area changes. */
.railgrid .pagenav{gap:1px;}
.railgrid .pagenav a{padding:5px 0;}

/* ==================== Fig 01, event-state dissociation ====================
   Replaces the two-row Base / Semantic layer plate in the Stack section.
   Unboxed and full width since 2026-08-27. It used to sit in a --paper card beside
   a 300px readout panel, which left the drawing 488px wide inside an 888px column:
   the 9.5px labels in a 800-unit viewBox came out near 5.8px on screen, too small to
   read. The panel moved under the figure (where the mobile layout already put it) and
   the card chrome came off, so the drawing takes the full 888px and its labels land
   around 10.5px. The card was not carrying contrast: ink on --paper is 15.9:1 in light
   and 14.3:1 in dark, against 14.2:1 and 15.5:1 on the plain --bg, so removing it costs
   nothing in light and gains a little in dark. Both pass AA either way.
   All colour comes from the theme tokens so the figure inverts with the page;
   the BASE LAYER label uses --sig-text because raw ultramarine fails AA on the
   dark canvas. Class and pattern ids are fd-prefixed: .flow and .sel are taken. */
/* Readout back beside the drawing, 2026-08-27. Full width put it under the figure
   and the eye had to travel down to read what it was hovering. The drawing is only
   affordable at this width because the viewBox was cropped to the artwork first:
   it was carrying 15% empty margin horizontally and 28% vertically, so cropping
   buys back most of what the 260px column costs. 260px holds the longest readout
   in about twelve lines, which stays inside the figure's own height, so the row
   never grows taller than the drawing. */
.fdis{display:grid;grid-template-columns:1fr 260px;gap:32px;align-items:center;margin-top:34px;}
.fdis svg{width:100%;height:auto;display:block;}
.fd-hot{cursor:pointer;outline:none;}
.fdis .fd-hot text{font-family:var(--fam-display),'Space Grotesk',sans-serif;}
.fdis .fd-flow text{font-family:var(--fam-mono),'IBM Plex Mono',monospace;}
.fd-hot polygon,.fd-hot text,.fd-hot path{transition:opacity .28s ease;}
.fd-box.on .fd-hot{opacity:.36;}
.fd-box.on .fd-hot.fd-sel{opacity:1;}
.fd-hot:focus-visible polygon{stroke-width:2.6;}
.fd-flow line,.fd-flow path,.fd-flow text{transition:opacity .28s ease;}
.fd-box.on .fd-flow{opacity:.3;}
.fd-cap{font-family:var(--fam-mono),'IBM Plex Mono',monospace;font-size:10px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--mut);margin-top:16px;}
/* 160px is the measured height of the tallest readout (the boundary entry) at the
   74ch measure, so hovering a layer never nudges what follows the figure. */
.fd-panel{min-height:200px;border-left:1.5px solid var(--hair);padding-left:24px;}
.fd-panel .fd-b,.fd-panel .fd-idle{max-width:none;}
.fd-k{font-family:var(--fam-mono),'IBM Plex Mono',monospace;font-size:10px;
  letter-spacing:.2em;text-transform:uppercase;color:var(--mut);}
.fd-t{font-family:var(--fam-display);font-size:23px;font-weight:700;
  letter-spacing:-.02em;margin:12px 0 10px;line-height:1.15;color:var(--fg);}
.fd-b{font-size:14px;line-height:1.65;color:var(--fg);opacity:.88;}
.fd-idle{font-size:14px;line-height:1.65;color:var(--mut);}
@media(max-width:820px){
  .fdis{grid-template-columns:1fr;gap:20px;}
  .fd-panel{min-height:0;border-left:none;border-top:1.5px solid var(--hair);padding:18px 0 0;}
}
@media(prefers-reduced-motion:reduce){
  .fd-hot polygon,.fd-hot text,.fd-hot path,
  .fd-flow line,.fd-flow path,.fd-flow text{transition:none;}
}

/* ── Index rail, nested (Home) ────────────────────────────────────────────────
   Base layer and Stack belong to the Thesis, and Process belongs to Status, so
   the rail sets them as children rather than as four more peers in a flat run of
   seven. The numeral stays in the margin and the child label starts on the same
   left edge as its parent's label, which makes it a hanging indent: the numerals
   read as a column of their own and everything under one numeral is visibly one
   part of the page. The indent is a hidden [00] in the child's own markup rather
   than a padding value: the link is a flex row of numeral + gap + label, so an
   identical hidden box is the only indent that cannot drift out of alignment when
   the mono advance, the letter-spacing or the flex gap changes.
   Rail-only: below 900px .railgrid .pagenav is hidden outright, and .pagenav
   without .railgrid (the "On this page" nav) never gets sub entries. */
.railgrid .pagenav a.sub .rn{visibility:hidden;}

/* ── v29 · Nav collapses to a hamburger at every width ────────────────────────
   The four links used to sit inline on desktop and behind a button only below
   700px. They are behind the button at all widths now: hover opens it with a
   pointer, tap opens it on touch.

   The button and the panel share .tb-menuwrap so :hover stays true while the
   pointer travels from the icon down into the panel. :hover follows the DOM, not
   the layout, so an absolutely positioned panel still counts as inside its
   wrapper. The wrapper is stretched over the topbar's vertical padding with a
   negative margin and matching padding, which changes nothing about the layout
   but puts its bottom edge flush with the topbar's border. Without that the
   panel would open 18px below the wrapper and the pointer would cross dead space
   on the way down, closing it.

   The hover rule is gated on (hover:hover) and (pointer:fine). A touch browser
   that reports a lingering :hover would otherwise open the panel on the tap that
   is also toggling .nav-open, and the two would fight.

   Click still pins the panel open through .nav-open, so hover previews and click
   holds. Both paths are keyboard-reachable: :focus-within opens it on tab. */
.tb-menuwrap{position:relative;display:flex;align-items:center;align-self:stretch;
  margin-block:-18px;padding-block:18px;}
.tb-menu{display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;background:none;border:0;padding:0;color:var(--fg);cursor:pointer;}
.tb-menu .ic-x{display:none;}
.topbar.nav-open .tb-menu .ic-x{display:block;}
.topbar.nav-open .tb-menu .ic-bars{display:none;}

.tb-nav{position:absolute;top:100%;right:0;left:auto;z-index:30;
  display:none;flex-direction:column;gap:0;width:min(78vw,300px);
  background:var(--bg);border:1.5px solid var(--line);border-top:0;padding:4px 18px;}
.topbar.nav-open .tb-nav{display:flex;}
@media(hover:hover) and (pointer:fine){
  .tb-menuwrap:hover .tb-nav,
  .tb-menuwrap:focus-within .tb-nav{display:flex;}
}
/* 44px minimum touch target (WCAG 2.5.8): 14px of padding on a 16px line. */
.tb-nav a{padding:14px 2px;font-size:12px;border-bottom:1px solid var(--hair);border-left:0;}
.tb-nav a:last-child{border-bottom:0;}
/* Stacked, an aria-current border reads as a divider rather than a marker, so the
   active page is carried by weight and colour alone. */
.tb-nav a[aria-current="page"]{border-bottom-color:var(--hair);}
.tb-nav a:last-child[aria-current="page"]{border-bottom-color:transparent;}
@media(max-width:860px){.topbar{flex-wrap:nowrap;padding:14px 20px;}
  .tb-menuwrap{margin-block:-14px;padding-block:14px;}}
/* The bar tightens again at 700px, so the wrapper's stretch has to follow it or
   the panel reopens a gap under the icon. 44px targets on touch. */
@media(max-width:700px){.tb-menuwrap{margin-block:-12px;padding-block:12px;}
  .tb-menu{width:44px;height:44px;}}

/* ── v29 · Wordmark ──────────────────────────────────────────────────────────
   22px left the mark smaller than the theme toggle beside it. 26px reads as the
   primary element in the bar without crowding the 1.5px rule under it. */
.tb-mark svg{height:26px;}
@media(max-width:640px){.tb-mark svg{height:22px;}}

/* ── v29 · Closing headline ──────────────────────────────────────────────────
   26ch broke the line into three, which set the sentence as a stack rather than
   a statement. The measure opens to 40ch and the size comes down, so it lands in
   two. The non-breaking spaces inside "neutral value" and "coordination
   substrate" stay: they are what stops either pair splitting across the break
   and stranding a word, per the house rule on orphans. */
.closing h2{font-size:clamp(26px,3.1vw,40px);max-width:40ch;}
@media(max-width:640px){.closing h2{max-width:24ch;}}

/* ── v29 · Hero is static ─────────────────────────────────────────────────────
   iovi.css:56 starts the kicker, headline, lede and CTA at opacity:0 and fades
   each one up on a stagger, and the headline additionally rises word by word.
   Cara wants the hero to be there when the page is. Neutralised here rather than
   in iovi.css because that file is shared with gen_v3.py, gen_site3.py and
   gen_asm.py, which still build their own versions of this site.

   This is the same declaration set the prefers-reduced-motion block in iovi.css
   already applies, promoted to apply always. */
.hero .kicker,.hero .themes,.hero h1,.hero .hero-lede,
.hero .joinwrap,.hero .joinnote{animation:none!important;opacity:1;transform:none;}
.hero h1 .hw{opacity:1;transform:none;animation:none!important;}

/* ── v29 · Mono is for accents only ───────────────────────────────────────────
   Audited every text-bearing element on Home for its computed family. Two were
   setting full sentences in IBM Plex Mono rather than labelling anything:

     .record .v   the record plate's values, e.g. "A base layer that orders
                  economic events and keeps them retrievable, without reading
                  them". Its label, .record .k, is a real accent and stays mono.
     .exp li      the Stack section's open questions, which are whole sentences
                  ending in question marks.

   Both are running text, so both move to Inter. Everything still on mono is a
   label, a numeral or a kicker: the index rail and its [01] figures, the nav,
   record keys, step and accordion numerals, the figure's annotations and its
   readout kicker, and the footer stamp. Set in iovi.css, overridden here because
   that file is shared with three other generators. */
.record .v{font-family:var(--fam-text);font-size:14px;line-height:1.6;letter-spacing:0;}
.exp{font-family:var(--fam-text);letter-spacing:0;}
#stack .exp,#stack .exp li{font-family:var(--fam-text);letter-spacing:0;}

/* ── v31 · Plain Inter, set the way luigi-project.io sets it ──────────────────
   "I just want normal Inter." Previous passes kept inherited display settings on
   top of it: 700 weight, negative tracking, and 1.04-1.12 leading, all of which
   were tuned for Space Grotesk and none of which Inter asks for. Stacked up they
   stop reading as Inter at all.

   Luigi, the reference, sets headings at font-weight:normal, line-height:1.4 and
   no letter-spacing whatsoever. That is what this is. Every negative tracking
   value on a heading is gone rather than reduced, weight drops to 400, and the
   leading opens. Display sizes keep a little less leading than 1.4 only because
   they are three to four times Luigi's heading sizes, where 1.4 would open the
   lines past the point of reading as one headline.

   Reverting to bold is one line: change the font-weight below. */
h1,h2,h3,h4,.hero h1,.hero-c h1,.sec-head h2,.sec-head b,.closing h2,.mvt-h,.dh{
  font-weight:400;letter-spacing:normal;}
.hero h1,.hero-c h1{line-height:1.16;}
.sec-head h2,.sec-head b,.mvt-h,.dh{line-height:1.2;}
.closing h2{line-height:1.28;}
h3,.step h3,.card h3,.fd-t,.sd-name,.tp-name{font-weight:600;letter-spacing:normal;line-height:1.35;}
p,li,.lead,.lead-h,.lead-em,.sec-deck,.hero-lede,.mch-body p,
.fd-b,.fd-idle,.record .v,.exp,.exp li{letter-spacing:normal;}

/* ── v30 · The closing CTA is not a fenced enclosure ──────────────────────────
   .closing carried border-top:1.5px solid var(--line) and <footer> carries the
   same, so the CTA sat between two full-width rules at full ink weight: --line
   is the foreground colour, not a hairline. Two hard rules that close ranks
   around one sentence and a button read as a boxed-off panel. The band's own
   rule comes off and the footer's drops to --hair, which still divides the
   footer from the page without fencing the CTA in. */
.closing{border-top:0;padding-top:40px;}
footer{border-top:1.5px solid var(--hair);}

/* ── v30 · Topbar: two links inline, the rest in the menu ─────────────────────
   Contribute / Participate and Research sit inline because they are what the
   site is asking of a reader. Home, Blog and the socials live in the menu. The
   inline pair inherits .tb-nav's type but not its panel layout, so it is styled
   from scratch rather than by unpicking the panel rules. Hidden below 700px,
   where the bar has no room for them and the menu already holds everything. */
.tb-inline{display:flex;align-items:center;gap:22px;
  font-family:var(--fam-mono),'IBM Plex Mono',monospace;font-size:11px;
  letter-spacing:.14em;text-transform:uppercase;}
.tb-inline a{color:var(--mut);text-decoration:none;padding:2px 0;
  border-bottom:1.5px solid transparent;transition:color .25s ease;}
.tb-inline a:hover{color:var(--fg);}
.tb-inline a[aria-current="page"]{color:var(--fg);font-weight:600;border-bottom-color:var(--line);}
@media(max-width:700px){.tb-inline{display:none;}}

/* Socials close the menu panel, under the same hairline the nav links use so the
   row reads as part of the list rather than as a separate tray. The icons are
   the footer's, from the one SOCIALS constant, so the two can never drift. */
.tb-social{display:flex;align-items:center;gap:16px;padding:14px 2px 4px;}
.tb-social a{color:var(--mut);display:inline-flex;padding:0;border:0;
  transition:color .25s ease;}
.tb-social a:hover{color:var(--fg);}

/* ── v31 · Bar is chrome, content is a centred column ────────────────────────
   Two things Cara asked for at once, and they pull in opposite directions until
   the roles are separated. The prose was running hard against the left with all
   the slack piling up on the right, because the column was the full 1152px while
   the paragraphs capped at 70ch, about 794px. And the wordmark and the menu were
   sitting too far in from the window edges.

   So the bar stops sharing the content's grid. It spans the window on a small
   fixed inset, which is what puts the wordmark and menu near the edges, and the
   content becomes a column sized to the measure it actually sets and centred, so
   the slack falls evenly on both sides instead of all to the right. The earlier
   pass had done the opposite, pinning the bar to the column, which was right
   when the column was full width and is wrong now.

   .doc is 968px so its content box is 880px, which is the paragraph measure plus
   a little. Everything in the column shares that width: headings, rules, cards,
   the figure and the FAQ, so nothing is left hanging into empty space. */
:root{--gutter:44px;}
@media(max-width:860px){:root{--gutter:24px;}}
.doc{max-width:968px;padding-left:var(--gutter);padding-right:var(--gutter);}
.topbar{padding-left:28px;padding-right:28px;}
@media(max-width:860px){.topbar{padding-left:16px;padding-right:16px;}}

/* The prose caps come off inside the column: the column is the measure now, so a
   second, narrower cap would reintroduce the same right-hand gap one level in. */
.home-flow{max-width:none;margin:0;}
.home-flow .mch-body p,.lead,.fd-b,.fd-idle,.record .v{max-width:none;}

/* ── v31 · Theme toggle ───────────────────────────────────────────────────────
   It was a pill-bordered box, the only outlined control in the bar, sitting to
   the left of the menu. Bare icon now, same 40px hit area as the menu button so
   both still clear the 24px minimum, and last in the row. */
.toggle{border:0;background:transparent;border-radius:0;padding:0;
  width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center;
  color:var(--mut);}
.toggle:hover{color:var(--fg);border-color:transparent;}
@media(max-width:700px){.toggle{width:44px;height:44px;}}

/* ── v32 · research concierge, terminal pane ─────────────────────────────────
   Replaces the sign-up form as the primary way in. Built from the existing
   system only: Canvas field, Ink text, IBM Plex Mono, the same 18px corner
   brackets as .frame, 4px radius, no shadow.

   The parked .chat rules further up this file are a different thing: a
   bottom-right bubble with a 999px pill and a 50px drop shadow, from before the
   surfaces were flattened. That shell stays for gen_v3.py; nothing below
   touches it, and the two never appear on the same page. */
.rc-veil{position:fixed;inset:0;z-index:60;background:var(--bg);display:none;}
.rc-veil.open{display:block;}
.rc-frame{position:absolute;inset:16px;pointer-events:none;}
.rc-frame i{position:absolute;width:18px;height:18px;border:1.5px solid var(--line);}
.rc-frame .tl{top:0;left:0;border-right:none;border-bottom:none;}
.rc-frame .tr{top:0;right:0;border-left:none;border-bottom:none;}
.rc-frame .bl{bottom:0;left:0;border-right:none;border-top:none;}
.rc-frame .br{bottom:0;right:0;border-left:none;border-top:none;}

.rc-pane{position:absolute;inset:16px;display:flex;flex-direction:column;padding:38px 34px 26px;}
@media(max-width:700px){.rc-pane{padding:34px 22px 18px;}}

.rc-head{display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--mut);padding-bottom:14px;border-bottom:1.5px solid var(--hair);}
.rc-close{background:none;border:none;color:var(--mut);font-family:'IBM Plex Mono',monospace;
  font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;cursor:pointer;padding:0;}
.rc-close:hover{color:var(--fg);}

.rc-log{flex:1;overflow-y:auto;padding:22px 0;display:flex;flex-direction:column;gap:18px;
  font-family:'IBM Plex Mono',monospace;font-size:13px;line-height:1.7;
  max-width:78ch;width:100%;margin:0 auto;scrollbar-width:thin;}
.rc-turn{display:grid;grid-template-columns:auto 1fr;gap:12px;align-items:start;}
.rc-turn .rc-sig{color:var(--mut);user-select:none;white-space:nowrap;}
.rc-turn.me .rc-body{color:var(--fg);}
.rc-turn.iov .rc-body{color:var(--mut);}
.rc-body{white-space:pre-wrap;word-break:break-word;}
.rc-body p{margin:0 0 .7em;}
.rc-body p:last-child{margin-bottom:0;}
.rc-err{color:var(--fg);border-left:1.5px solid var(--line);padding-left:12px;}

/* The one Ultramarine element on this surface. Not the send action, which stays
   a plain mono label, because the cursor is what carries the REPL reading. */
.rc-cursor{display:inline-block;width:.58em;height:1.05em;background:var(--sig);
  vertical-align:-.16em;margin-left:2px;animation:rc-blink 1.05s steps(1) infinite;}
@keyframes rc-blink{0%,49%{opacity:1;}50%,100%{opacity:0;}}
@media(prefers-reduced-motion:reduce){.rc-cursor{animation:none;}}

/* The mono context is set here as well as on .rc-log because `ch` resolves
   against each element's own font. Without it .rc-foot measures 78ch of Inter
   and the input column lands on a different left edge from the transcript. */
.rc-foot{max-width:78ch;width:100%;margin:0 auto;padding-top:14px;border-top:1.5px solid var(--hair);
  font-family:'IBM Plex Mono',monospace;font-size:13px;}
.rc-in{display:flex;align-items:baseline;gap:12px;font-family:'IBM Plex Mono',monospace;font-size:13px;}
.rc-in .rc-sig{color:var(--mut);user-select:none;}
.rc-in textarea{flex:1;border:none;background:transparent;color:var(--fg);outline:none;resize:none;
  font-family:'IBM Plex Mono',monospace;font-size:13px;line-height:1.7;padding:0;max-height:7.5em;overflow-y:auto;}
.rc-in textarea::placeholder{color:var(--mut);opacity:.75;}
.rc-send{background:none;border:none;color:var(--fg);font-family:'IBM Plex Mono',monospace;
  font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;cursor:pointer;padding:0;white-space:nowrap;}
.rc-send[disabled]{color:var(--mut);cursor:default;}

.rc-alt{margin-top:12px;font-family:'IBM Plex Mono',monospace;font-size:10.5px;
  letter-spacing:.1em;color:var(--mut);display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;}
.rc-alt a{color:var(--mut);text-decoration:none;border-bottom:1px solid var(--hair);}
.rc-alt a:hover{color:var(--fg);}

/* Email capture: same row grammar as a turn, so it reads as part of the session
   rather than a form dropped into it. */
.rc-capture{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;
  font-family:'IBM Plex Mono',monospace;font-size:13px;}
.rc-capture input[type=email]{flex:1;min-width:210px;border:none;border-bottom:1.5px solid var(--hair);
  background:transparent;color:var(--fg);outline:none;font-family:'IBM Plex Mono',monospace;
  font-size:13px;padding:2px 0;}
.rc-capture input[type=email]:focus{border-bottom-color:var(--line);}
.rc-consent{display:flex;align-items:flex-start;gap:8px;font-family:'IBM Plex Mono',monospace;
  font-size:10.5px;line-height:1.6;color:var(--mut);margin-top:10px;max-width:62ch;}
.rc-consent input{margin:2px 0 0;accent-color:var(--sig);}

/* Turnstile sits in the capture block. Cloudflare renders it in an iframe whose
   chrome we cannot restyle, so it is given room rather than fought with. */
.rc-turnstile{margin-top:12px;min-height:0;}
.rc-turnstile:empty{margin-top:0;}
.rc-turnstile iframe{max-width:100%;}

/* ── v33 · concierge, embedded ───────────────────────────────────────────────
   The Contribute page hosts the pane in flow instead of over the page. Same
   .rc-* parts as the overlay so the two cannot drift; only the container and a
   few sizes change. The corner brackets are dropped here because the page frame
   already draws them, and a second set inside the first reads as a box in a box. */
.rc-host{border:1.5px solid var(--line);border-radius:4px;background:var(--paper);
  margin:0 0 12px;overflow:hidden;}
.rc-host .rc-pane{position:static;inset:auto;padding:0;display:block;}
.rc-host .rc-head{padding:14px 20px;margin:0;border-bottom:1.5px solid var(--hair);}
.rc-host .rc-log{max-width:none;margin:0;padding:20px;
  /* Tall enough to read as a session rather than a search box, short enough that
     the sections under it stay in reach on a laptop. */
  min-height:260px;max-height:min(52vh,460px);}
.rc-host .rc-foot{max-width:none;margin:0;padding:14px 20px 16px;
  border-top:1.5px solid var(--hair);background:var(--bg);}
.rc-host .rc-turn{grid-template-columns:auto 1fr;}
@media(max-width:700px){
  .rc-host .rc-log{padding:16px;min-height:220px;max-height:60vh;}
  .rc-host .rc-head,.rc-host .rc-foot{padding-left:16px;padding-right:16px;}
}

/* The hero above it loses its call to action, so the two need to read as one
   move: question, then the place to answer it. */
.hero.hero-ask{padding-bottom:22px;}
.hero.hero-ask .hero-lede{margin-bottom:0;}

/* The hero and the pane below it are one move: the question, then the place to
   answer it. They were breaking on three different right edges, because .hero.inner
   caps the headline at 22ch and the lede at 72ch while the pane fills the column.
   Capped text beside an uncapped box reads as text that stopped early rather than
   as a column. Inside .hero-ask both run the full measure, so all three share an
   edge. The 880px column is itself the paragraph measure, so nothing is over-set. */
.hero.hero-ask h1,
.hero.hero-ask .hero-lede{max-width:none;}

/* ── v34 · depth, softness, motion ───────────────────────────────────────────
   The brief, from Cara after the Da call: the site should read as an AI-first
   company, and Da's "90s internet" note about the form probably applies wider.

   What is deliberately NOT changing: the wordmark, ultramarine, Inter, IBM Plex
   Mono, the copy, the layout and the information architecture. Da approved the
   logo, the colour scheme and the overall feel on 2026-08-31, and none of that is
   what reads dated. What reads dated is the execution underneath it.

   Three diagnoses, three fixes:

   1. Nothing has depth. The v29 pass set --shadow to a transparent value and
      flattened every radius to 4px, so every card, plate and chip sits on exactly
      the same plane behind a hard full-ink border. With no elevation the eye gets
      no hierarchy and the page reads as a wireframe. Depth comes back here, but
      as a top highlight plus a wide soft drop rather than the 2015 grey blur:
      one crisp inset line where light would catch the top edge, and a shadow that
      is nearly all spread and almost no opacity.

   2. Every border is full-strength ink. --line IS the foreground colour, which is
      already recorded as a trap further up this file. A 1.5px --line border is a
      paragraph of text drawn as a rectangle. Structural borders move to --edge,
      which is a real hairline, and ink is kept for things that are genuinely
      ink.

   3. There is no motion. The reveal machinery is all here and all switched off,
      twice over: v17 built .rv, v18 disabled it in favour of .ru, and nothing
      emits .ru. Rather than untangle that, v34 brings its own class, .mv, driven
      by assets/iovi-motion.js, which gen_alt.py loads and the other generators
      never see. */

:root{
  /* Nearly all spread, almost no opacity. Depth you read before you notice. */
  --shadow:0 1px 2px rgba(20,23,28,.045),0 14px 32px -16px rgba(20,23,28,.20);
  --shadow-h:0 2px 6px rgba(20,23,28,.06),0 26px 56px -20px rgba(20,23,28,.28);
  /* The line light would catch along a lifted top edge. */
  --sheen:rgba(255,255,255,.55);
  /* A hairline that is actually hair. --hair is .17 alpha ink; --edge is lighter
     still, because at 14px radius the radius carries the shape and the border
     only has to separate the surface from the ground. */
  --edge:rgba(20,23,28,.11);
  --edge-hi:rgba(20,23,28,.24);
  --glow:0 10px 34px -8px rgba(31,58,255,.42);
  --glow-hi:0 14px 44px -6px rgba(31,58,255,.58);
  --r-card:14px;
  --r-btn:10px;
  --ease:cubic-bezier(.2,.7,.2,1);
}
[data-theme="dark"]{
  --shadow:0 1px 2px rgba(0,0,0,.5),0 18px 40px -20px rgba(0,0,0,.9);
  --shadow-h:0 2px 8px rgba(0,0,0,.55),0 30px 64px -22px rgba(0,0,0,1);
  --sheen:rgba(255,255,255,.06);
  --edge:rgba(233,235,238,.10);
  --edge-hi:rgba(233,235,238,.24);
  --glow:0 10px 38px -8px rgba(31,58,255,.55);
  --glow-hi:0 14px 48px -6px rgba(31,58,255,.72);
}

/* --- surfaces ------------------------------------------------------------- */
.card,.pf,.fq,.step,.chl,.chx,.stack,.record,.bench,.dcol,
.stack-detail,.track-viewer,.specb,.layer,.sb-layer,
.blog-empty,.b-pending,.modal-card,.bcard .bc-img,.type-sample{border-radius:var(--r-card);}
.tr-item,.sk,.yours,.toggle,.joinbtn{border-radius:var(--r-btn);}

.card,.pf,.step,.chl,.chx,.stack,.record,.bench,.track-viewer,.dcol{
  border:1px solid var(--edge);
  box-shadow:inset 0 1px 0 var(--sheen),var(--shadow);
  transition:border-color .22s var(--ease),box-shadow .3s var(--ease),transform .3s var(--ease);
}

/* v29 replaced the lift with a hard ink edge on hover. The lift comes back, and
   the edge it moves to is --edge-hi rather than full ink. */
a.card:hover,.card:hover,a.pf:hover,.chl:hover,.chx:hover{
  transform:translateY(-2px);
  box-shadow:inset 0 1px 0 var(--sheen),var(--shadow-h);
  border-color:var(--edge-hi);
}

/* --- rules ---------------------------------------------------------------- */
/* Section heads were a 1.5px full-ink bar fading out at 55%. Same gesture, drawn
   at hairline weight so it separates sections instead of fencing them. */
.sec-head::before{
  height:1px;
  background:linear-gradient(90deg,var(--edge-hi) 0%,var(--edge-hi) 42%,transparent 100%);
}
.record{border-top:none;}
.record .r{border-bottom:1px solid var(--edge);}
.record .r:last-child{border-bottom:none;}
.fq{border-bottom:1px solid var(--edge);}
.footer,.foot{border-top-color:var(--edge);}

/* --- the one blue thing --------------------------------------------------- */
/* Brand rule is one ultramarine per surface, and on the home page that one is the
   primary action. So the CTA is where the light goes: it is the only element that
   is allowed to emit any. The hero figure below it stays monochrome for exactly
   this reason. */
/* .ghost is the outlined variant: the Substack subscribe button and the working
   group "Ask about this group" button. They were carrying three inline style
   declarations each to unpaint themselves; that moved to this class so the glow
   below can be scoped away from them. A blue halo under a button that is not blue
   was the first thing wrong with the v34 button pass. */
.joinbtn.ghost{background:transparent;color:var(--fg);border-color:var(--line);}
.joinbtn.ghost:hover{border-color:var(--fg);background:transparent;}

.joinbtn:not(.ghost){
  transition:box-shadow .3s var(--ease),transform .22s var(--ease);
}
.joinbtn:not(.ghost):hover{opacity:1;transform:translateY(-1px);}
.joinbtn{transition:border-color .22s var(--ease),transform .22s var(--ease),box-shadow .3s var(--ease);}
.joinbtn:hover{transform:translateY(-1px);}
.joinbtn:active{transform:translateY(0);}
.joinbtn:focus-visible{outline:2px solid var(--sig-text);outline-offset:3px;}

/* --- track chips ---------------------------------------------------------- */
/* The working group picker on Contribute is the most form-like thing left on the
   site, which matters because it sits directly under the concierge that replaced
   the form. Same treatment as a card so the two read as one system. */
.tr-item{
  border:1px solid var(--edge);
  box-shadow:inset 0 1px 0 var(--sheen),var(--shadow);
  padding:14px 18px;
  transition:border-color .22s var(--ease),box-shadow .3s var(--ease),transform .3s var(--ease);
}
.tr-item:hover{border-color:var(--edge-hi);transform:translateY(-1px);box-shadow:inset 0 1px 0 var(--sheen),var(--shadow-h);}
.tr-item.active{border-color:var(--edge-hi);box-shadow:inset 0 0 0 1px var(--sig-text),var(--shadow-h);}

/* --- motion --------------------------------------------------------------- */
/* .mv is v34's own reveal class, added by iovi-motion.js and switched on by an
   IntersectionObserver. It exists because .rv and .ru are both dead ends: v17
   built .rv and v18 turned it off in favour of .ru, which no generator emits.
   Short travel and a short duration on purpose. Long fade-ups on every block are
   themselves a dated tell; this should read as the page settling, not performing. */
.mv{opacity:0;transform:translateY(14px);}
.mv.mv-in{opacity:1;transform:none;
  transition:opacity .5s var(--ease),transform .5s var(--ease);}
/* Children of a revealed block stagger behind it, capped at six so a long list
   never turns into a slow queue. */
.mv-in>.mv-c{animation:mv-up .5s var(--ease) backwards;}
.mv-in>.mv-c:nth-child(2){animation-delay:.06s;}
.mv-in>.mv-c:nth-child(3){animation-delay:.12s;}
.mv-in>.mv-c:nth-child(4){animation-delay:.18s;}
.mv-in>.mv-c:nth-child(5){animation-delay:.24s;}
.mv-in>.mv-c:nth-child(n+6){animation-delay:.3s;}
@keyframes mv-up{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}
/* No-JS and reduced-motion both land on "already there" rather than "invisible".
   The observer adds .mv-on to <html> so the initial hidden state only ever
   applies when something is going to come along and reveal it. */
html:not(.mv-on) .mv{opacity:1;transform:none;}
@media(prefers-reduced-motion:reduce){
  .mv,.mv.mv-in{opacity:1;transform:none;transition:none;}
  .mv-in>.mv-c{animation:none;}
}

/* ── v34 · the concierge ─────────────────────────────────────────────────────
   This is the surface Da's "90s internet" note was actually about, and the one
   that has to carry "AI-first". As it shipped it does not, for reasons that are
   about behaviour rather than style:

   - It opens as a 260px empty box under one line of grey mono. There is nothing
     to click, nothing to read, and no sign anything is alive. A blank rectangle
     asking to be typed into is the form experience it replaced, minus the fields.
   - The composer is 13px placeholder text and a 10.5px uppercase SEND label that
     is visually quieter than the fine print beneath it. The primary action on the
     page is its least prominent element.
   - The only state it can express is a blinking caret. No thinking, no streaming
     cue, no sense of a system doing work.

   So: openers to click, a composer that looks like the main event, a live badge,
   a surface that lifts off the page, and a log that sizes to what is in it. */

.rc-host{
  border:1px solid var(--edge);
  border-radius:16px;
  box-shadow:inset 0 1px 0 var(--sheen),var(--shadow);
  background:var(--paper);
  margin:0 0 14px;
  transition:box-shadow .3s var(--ease),border-color .22s var(--ease);
}
/* The whole pane brightens while the composer has focus, so the thing you are
   typing into is visibly the active object on the page. */
.rc-host:focus-within{border-color:var(--edge-hi);box-shadow:inset 0 1px 0 var(--sheen),var(--shadow-h);}

.rc-head{border-bottom:1px solid var(--edge);}
.rc-host .rc-head{padding:15px 22px;}
.rc-host .rc-foot{border-top:1px solid var(--edge);background:transparent;padding:16px 22px 18px;}
/* The 260px min-height was the void. It sizes to its content now and only starts
   scrolling once there is a real conversation in it. */
.rc-host .rc-log{padding:22px;min-height:0;max-height:min(56vh,520px);}

/* A live badge, because a chat that cannot say whether it is running reads as a
   picture of a chat. The dot is --mut, not ultramarine: this surface spends its
   one blue on the composer. */
.rc-stat{display:inline-flex;align-items:center;gap:7px;}
.rc-dot{width:5px;height:5px;border-radius:50%;background:var(--mut);flex:0 0 auto;
  animation:rc-pulse 2.6s var(--ease) infinite;}
@keyframes rc-pulse{0%,100%{opacity:.35;}50%{opacity:1;}}
@media(prefers-reduced-motion:reduce){.rc-dot{animation:none;opacity:.9;}}

/* --- openers -------------------------------------------------------------- */
/* The single biggest change. An empty box gives a visitor nothing to do; four
   real questions give them a way in and, just as usefully, tell them what this
   thing is competent to answer before they spend a question finding out.

   They seed the composer rather than sending, which is the rule the existing
   [data-chat-seed] buttons already follow: a button that fires a message on
   someone's behalf reads as the machine talking to itself. */
.rc-openers{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;}
.rc-op{
  font-family:var(--fam-mono),'IBM Plex Mono',monospace;font-size:11.5px;
  line-height:1.4;color:var(--mut);background:transparent;
  border:1px solid var(--edge);border-radius:999px;padding:7px 14px;
  cursor:pointer;text-align:left;
  transition:color .2s var(--ease),border-color .2s var(--ease),transform .2s var(--ease);
}
.rc-op:hover{color:var(--fg);border-color:var(--edge-hi);transform:translateY(-1px);}
.rc-op:focus-visible{outline:2px solid var(--sig-text);outline-offset:2px;}
/* Scaffolding: once the conversation is real they go, and they do not come back.
   Re-offering canned questions mid-conversation is what makes a support widget
   feel like a support widget. */
.rc-openers.gone{display:none;}

/* --- composer ------------------------------------------------------------- */
/* Was a bare 13px line with a `>` in front of it. Now a field: an edge, a ground,
   room to breathe, and an ultramarine focus ring. That ring and the caret are this
   surface's one blue, and they are the same signal, which is why the Send button
   next to them is deliberately not blue as well. */
.rc-in{
  align-items:flex-end;gap:12px;
  border:1px solid var(--edge);border-radius:12px;
  background:var(--bg);padding:12px 12px 12px 14px;
  transition:border-color .2s var(--ease),box-shadow .2s var(--ease);
}
/* A single crisp ink edge. The first version added a 1px ring on top of the border,
   which came to 2px of full-strength ink around the field and read as a warning. */
.rc-in:focus-within{border-color:var(--fg);}
.rc-in .rc-sig{color:var(--mut);align-self:flex-start;padding-top:3px;}
.rc-in textarea{font-size:14px;line-height:1.6;max-height:9em;}
.rc-in textarea::placeholder{color:var(--mut);opacity:.65;}

/* The primary action, sized like one. Ink fill rather than ultramarine: the blue
   on this surface is already spent on the composer's focus state, and the brand
   allows one signal per surface, not one per control. */
.rc-send{
  background:var(--sig);color:var(--onsig);
  border-radius:8px;padding:10px 17px;font-size:11px;font-weight:600;
  letter-spacing:.1em;line-height:1;flex:0 0 auto;
  transition:opacity .2s var(--ease),transform .2s var(--ease),box-shadow .3s var(--ease);
}
.rc-send:hover:not([disabled]){transform:translateY(-1px);opacity:.9;}
.rc-send[disabled]{background:var(--mut);color:var(--bg);opacity:.5;transform:none;box-shadow:none;}
.rc-send:focus-visible{outline:2px solid var(--sig-text);outline-offset:2px;}
/* The email submit inside a turn borrows .rc-send and must not become a slab. */
.rc-capture .rc-send{padding:8px 14px;}

.rc-alt{margin-top:14px;font-size:11px;opacity:.85;}

/* --- thinking ------------------------------------------------------------- */
/* Between pressing Send and the first token there was nothing but a "..." on the
   button, at the far side of the pane. Three dots on the transcript line the
   answer is about to appear on, so the wait happens where the eye already is. */
.rc-think{display:inline-flex;align-items:center;gap:5px;}
.rc-think i{width:4px;height:4px;border-radius:50%;background:var(--mut);display:block;
  animation:rc-think 1.3s var(--ease) infinite;}
.rc-think i:nth-child(2){animation-delay:.16s;}
.rc-think i:nth-child(3){animation-delay:.32s;}
@keyframes rc-think{0%,60%,100%{opacity:.25;transform:translateY(0);}30%{opacity:1;transform:translateY(-3px);}}
@media(prefers-reduced-motion:reduce){.rc-think i{animation:none;opacity:.6;}}

/* A turn arriving should land, not blink into existence. */
.rc-turn{animation:rc-turn-in .34s var(--ease) backwards;}
@keyframes rc-turn-in{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}
@media(prefers-reduced-motion:reduce){.rc-turn{animation:none;}}

/* Reading answers at 13px mono over 78ch was hard work. The transcript keeps the
   mono voice for the sigils and for the visitor's own lines, and sets the
   concierge's prose in the text face, which is what it is. */
.rc-turn.iov .rc-body{font-family:var(--fam-text);font-size:14.5px;line-height:1.65;color:var(--fg);}
.rc-turn.me .rc-body{font-size:13.5px;color:var(--mut);}
.rc-log{font-size:13.5px;}

/* --- overlay host --------------------------------------------------------- */
.rc-pane{padding:44px 34px 30px;}
.rc-veil .rc-foot{max-width:78ch;}
@media(max-width:700px){
  .rc-host .rc-log{padding:18px;max-height:60vh;}
  .rc-host .rc-head,.rc-host .rc-foot{padding-left:18px;padding-right:18px;}
  .rc-op{font-size:11px;padding:6px 12px;}
  .rc-stat{max-width:100%;}
}

/* ── v35 · Contribute opens on the pane and nothing else ─────────────────────
   Cara's call: the concierge should be the whole first screen. .askfold wraps the
   headline and the pane in one flex column tall enough to fill the viewport, the
   hero takes the height it needs, and the pane takes everything left over. Using
   min-height rather than height means a short window or a long headline pushes the
   block taller instead of crushing the pane. */
.askfold{
  /* The topbar is sticky and sits above this block, so its height comes off the
     viewport. Measured, not guessed: 77px, from the bar's own 18px vertical
     padding, dropping to 14px at the 860px breakpoint. Keep these two in step with
     .topbar's padding, the same way .tb-menuwrap has to be. */
  --bar:77px;
  min-height:calc(100dvh - var(--bar));
  display:flex;flex-direction:column;
}
@media(max-width:860px){.askfold{--bar:69px;}}
/* dvh is the correct unit here because a phone's toolbars change the viewport as
   you scroll, but it is recent enough to want a floor under it. */
@supports not (height: 100dvh){.askfold{min-height:calc(100vh - var(--bar));}}

.askfold .hero{flex:0 0 auto;}
/* min-height:0 at every level of the chain. Without it a flex item refuses to
   shrink below its content, the log never scrolls, and the composer is pushed off
   the bottom of the screen instead of sitting on it. */
.askfold .rc-host{flex:1 1 auto;display:flex;flex-direction:column;min-height:0;}
.askfold .rc-host .rc-pane{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;}
.askfold .rc-head,.askfold .rc-foot{flex:0 0 auto;}
.askfold .rc-log{flex:1 1 auto;min-height:0;max-height:none;}

/* Once there is a conversation the transcript sits at the bottom of the pane,
   against the composer, the way a terminal session or any chat does.

   margin-top:auto on the first turn rather than justify-content:flex-end on the
   log: an auto margin only absorbs positive free space, so it collapses to zero
   the moment the transcript is long enough to scroll. flex-end has a long history
   of making the earliest messages unreachable once the container overflows. */
/* Targeted through the mark, not with :first-of-type. Both .rc-mark and .rc-turn
   are divs, so `.rc-turn:first-of-type` asks for an element that is both the first
   div and a turn, and the first div is always the mark. It matched nothing, and
   the transcript sat at the top of a tall pane with 300px of nothing under it.
   The mark is always the log's first child, so the turn straight after it is the
   first turn. */
.askfold .rc-log:not(.rc-empty)>.rc-mark+.rc-turn{margin-top:auto;}

/* A floor, so a short laptop window degrades into scrolling the page rather than
   into a two-line pane. */
.askfold .rc-log{min-height:180px;}
@media(max-height:620px){.askfold{min-height:0;}.askfold .rc-log{min-height:220px;}}

/* One line, which is what it is asking. It was running at 56px and needed 972px
   inside an 880px column, so it broke after "you". 49px measures about 850px and
   clears the column with room to spare; below roughly 570px it wraps again, which
   is correct on a phone and is not what this rule is for. */
.hero.hero-ask h1{font-size:clamp(30px,4.4vw,49px);}

/* The --glow tokens are unused now that the CTAs are flat again. Left defined
   rather than deleted: they are the only record of what the value was, and a
   custom property nothing references costs nothing. */

/* ── v35 · the home hero's distribution ──────────────────────────────────────
   Three centred blocks, each breaking on its own logic, so the silhouette came
   out ragged and the eye had no shape to follow. Not a copy problem: the words
   are signed off and none of them change here.

   What was actually wrong:
   - The first lede is 94 characters against a measure of about 890px, so it broke
     three words onto a second line. "to build out." sitting alone under a full
     line is the orphan rule, and it is the first thing under the headline.
   - The thesis paragraph ran the full 880px column, which made it the widest
     block on screen and left it competing with the headline instead of sitting
     under it.

   The fix is a taper: headline widest, lede under it, thesis narrower still, so
   the three read as one descending block. text-wrap does the rest. balance evens
   the lines of the short blocks, which is exactly the job it exists for; pretty
   on the long one stops the last line coming up short. Both degrade to normal
   wrapping where unsupported, which is the current behaviour. */
.hero-c h1{text-wrap:balance;}
.hero-c .hero-lede{margin-left:auto;margin-right:auto;text-wrap:pretty;}

/* The tagline runs on one line. Measured at the shipped 18px it needs 784px inside
   an 880px column, so it fits with room to spare and only wraps below about 860px,
   where the column finally drops under it.

   It is set with pretty rather than balance, and that is the whole lesson of this
   rule. balance evens the line lengths and, on this sentence, evened them by
   breaking after "Open", splitting "Open to record" across two lines. Even lines
   are not the goal; not breaking a phrase in half is. pretty leaves the greedy
   break alone and only intervenes to stop a short last line. */
.hero-c .hero-lede:first-of-type{max-width:none;}

/* The thesis is a paragraph, not a standfirst, so it gets prose treatment and the
   tightest measure of the three. */
.hero-c .hero-lede+.hero-lede{max-width:54ch;}
/* A little more air before the action than between the two paragraphs, so the CTA
   reads as the end of the block rather than a fourth line of it. */
.hero-c .joinwrap{margin-top:40px;}

/* ── v36 · the resting state is a composition, not a gap ─────────────────────
   Making the pane fill the screen created a new problem: at rest the log is about
   480px tall and holds one sentence. Pinned to the bottom, that reads as a
   rendering fault, and pinning it to the top reads the same way with the gap
   underneath instead of above.

   Neither position fixes it, because position was never the problem. An empty
   state needs something to be, so it gets one: the mark, the opening line set as
   a statement rather than a transcript turn, and the whole thing centred. The
   openers sit underneath in the footer where they already were, so the pane at
   rest reads top to bottom as mark, invitation, four ways in, composer.

   It is a state, so it is a class, and it ends the moment a real conversation
   starts. `.rc-empty` comes off in send() before the first turn is drawn, and the
   greeting drops back into the transcript it was always part of. */
.rc-mark{display:none;color:var(--mut);opacity:.45;}
.rc-mark svg{width:38px;height:auto;display:block;margin:0 auto;}

.rc-log.rc-empty{
  justify-content:center;align-items:center;text-align:center;gap:24px;
  /* Safe here in a way it would not be on a transcript: the empty state holds one
     short line by definition, so there is never overflow for flex-end-style
     centring to make unreachable. The class is gone before anything can grow. */
}
.rc-log.rc-empty .rc-mark{display:block;}

/* The greeting stops being a turn and becomes the invitation. No sigil, because
   there is no exchange yet for it to be one side of. */
/* 54ch broke the invitation onto a stubby third line. 62ch gives it two, which is
   the shape a centred standfirst wants. Still pretty rather than balance: see the
   note on the home tagline for why evening the lines is not the goal. */
.rc-log.rc-empty .rc-turn{grid-template-columns:1fr;max-width:62ch;animation:none;}
.rc-log.rc-empty .rc-sig{display:none;}
.rc-log.rc-empty .rc-turn.iov .rc-body{
  font-size:18px;line-height:1.55;color:var(--fg);text-wrap:pretty;
}

/* Breathing room at the bottom. The form fallback was sitting about 30px off the
   pane's own edge, and the pane's edge was sitting on the fold. */
.rc-alt{margin-top:18px;}
.rc-host .rc-foot{padding:18px 22px 24px;}
.askfold .rc-host{margin-bottom:30px;}
@media(max-width:700px){
  .rc-host .rc-foot{padding-left:18px;padding-right:18px;padding-bottom:20px;}
  .rc-log.rc-empty .rc-turn.iov .rc-body{font-size:16px;}
  .rc-mark svg{width:28px;}
}

/* Shorter viewports. At a 713px viewport, which is an ordinary 800px browser
   window, the fold overran by about 12px: enough that the pane's bottom edge sat
   just under the fold and the page asked to be scrolled for no reward. The log's
   floor and the gap under the pane both give a little back before the
   max-height:620px rule hands the page its normal flow. */
@media(max-height:780px){
  .askfold .rc-log{min-height:150px;}
  .askfold .rc-host{margin-bottom:20px;}
  .rc-host .rc-foot{padding-bottom:18px;}
}

/* ── v37 · no box until there is a conversation ──────────────────────────────
   Two passes were spent arranging things inside an empty rectangle before the
   rectangle turned out to be the problem. A border drawn around nothing does not
   read as restraint, it reads as a component that failed to load, and no amount of
   centring its contents changes that, because it is the border doing the talking.

   "Make the box longer so it is the only thing you see" was taken too literally.
   The goal was for the concierge to own the first screen, and .askfold already
   guarantees that by keeping everything else below the fold. The box did not need
   to be 700px tall to achieve it, and being 700px tall is what broke it.

   So at rest there is no box. The mark, the invitation and the composer sit
   directly on the page, centred in the fold, and the pane, its head bar and its
   scrolling transcript appear on the first send. An empty transcript container
   becomes structurally impossible rather than something to decorate.

   rc-idle is the host's class, rc-empty the log's. Two classes because they do two
   jobs: rc-empty composes the log's contents, rc-idle takes the chrome off the
   pane around it. */
.rc-host.rc-idle{
  border-color:transparent;background:transparent;box-shadow:none;margin-bottom:0;
}
.rc-host.rc-idle:focus-within{border-color:transparent;box-shadow:none;}
.rc-host.rc-idle .rc-head{display:none;}
.rc-host.rc-idle .rc-log{
  border:0;padding:0;min-height:0;max-height:none;overflow:visible;flex:0 0 auto;
}
.rc-host.rc-idle .rc-foot{
  border-top:0;background:transparent;padding:0;flex:0 0 auto;
  display:flex;flex-direction:column;
}
/* The composer comes first at rest and the openers sit under it, which is the
   order the eye wants when the input is the main object rather than the last row
   of a panel. In the transcript state the source order takes over again. */
.rc-host.rc-idle .rc-in{order:1;}
.rc-host.rc-idle .rc-openers{order:2;}
.rc-host.rc-idle .rc-alt{order:3;}

/* Centred in the fold, under the headline. */
.askfold .rc-host.rc-idle .rc-pane{justify-content:center;gap:34px;}

/* The composer is the object now, so it is sized like one: its own surface, a
   real edge, and the same lift every other raised thing on the site has. */
.rc-host.rc-idle .rc-in{
  background:var(--paper);border:1px solid var(--edge-hi);border-radius:14px;
  box-shadow:inset 0 1px 0 var(--sheen),var(--shadow);
  padding:17px 17px 17px 19px;max-width:660px;width:100%;margin:0 auto;
}
.rc-host.rc-idle .rc-in:focus-within{border-color:var(--fg);}
.rc-host.rc-idle .rc-in textarea{font-size:15px;}

.rc-host.rc-idle .rc-openers{justify-content:center;margin:18px auto 0;max-width:660px;}
.rc-host.rc-idle .rc-alt{justify-content:center;text-align:center;margin-top:20px;}

/* The invitation carries the page at rest, so it is set a size up from the
   transcript version and given room under the mark. */
.rc-host.rc-idle .rc-log.rc-empty{gap:26px;}
.rc-host.rc-idle .rc-log.rc-empty .rc-turn.iov .rc-body{font-size:19px;line-height:1.5;}
.rc-host.rc-idle .rc-mark{opacity:.4;}
.rc-host.rc-idle .rc-mark svg{width:42px;}

@media(max-width:700px){
  .rc-host.rc-idle .rc-in{padding:15px 15px 15px 16px;}
  .rc-host.rc-idle .rc-log.rc-empty .rc-turn.iov .rc-body{font-size:17px;}
  .rc-host.rc-idle .rc-mark svg{width:34px;}
  .askfold .rc-host.rc-idle .rc-pane{gap:26px;}
}

/* The fold is one centred composition, headline included. A centred island of
   mark, invitation and composer sitting under a left-aligned headline was the
   remaining mismatch: two alignments on one screen, neither of them wrong on its
   own. Centring the hero settles it, and it matches the home page, which has been
   centred all along. The sections below the fold keep their left alignment, so the
   change is contained to the screen the concierge owns. */
.hero.hero-ask{text-align:center;}
.hero.hero-ask .hero-lede{max-width:74ch;margin-left:auto;margin-right:auto;text-wrap:pretty;}

/* The composer was capped at 660px, which read as narrow against an 880px column.
   720 gives it presence without letting the line length run past comfortable. */
.rc-host.rc-idle .rc-in{max-width:720px;}
/* The openers were capped to the composer's width, which broke four chips as three
   and one. Stranding the fourth on its own row is the orphan rule applied to
   interface: they get the full column and sit on one line. */
.rc-host.rc-idle .rc-openers{max-width:none;}

/* The fold is centred as one stack, not two. The hero sat at the top and the
   composition was centred in whatever was left, which opened a 180px hole between
   the lede and the mark and pushed the composer low. Centring the column instead
   distributes the air above the headline and below the openers, and the gap
   between the two parts becomes a spacing decision rather than a leftover.

   justify-content can be unconditional: in the transcript state the pane is
   flex:1 and fills the column, so there is no free space for it to distribute. */
/* Top-aligned, with a fixed gap doing the work centring used to do.

   Centring the stack cost 110px of layout shift on the first send: at rest the
   pane is flex:0 and the column has free space to distribute, and the moment a
   conversation starts the pane goes flex:1, fills the column, and the headline
   snaps upward. There is no way to keep a centred block still while the block
   below it grows to fill the screen.

   A gap does not move. The headline sits in the same place in both states, the
   pane grows downward into air that was already empty, and the interface reads as
   filling in rather than reflowing. 96px is tuned so the resting composition sits
   off the top without floating: it is the one number to change if the balance
   looks wrong. */
.askfold{justify-content:flex-start;gap:96px;}
.askfold .hero.hero-ask{flex:0 0 auto;padding-bottom:0;}
.askfold .rc-host.rc-idle{flex:0 0 auto;}
.askfold .rc-host.rc-idle .rc-pane{justify-content:flex-start;gap:30px;}
@media(max-width:700px){.askfold{gap:52px;}}
@media(max-height:780px){.askfold{gap:60px;}}


/* The Slack link at the end of the joining script. It is the one thing in the
   transcript the visitor is meant to act on, so it is the one thing that carries the
   signal colour. Sized as a target rather than as a run of text: this is the end of
   the flow, and a link that reads as prose here gets missed. */
.rc-join{display:inline-block;margin-top:4px;padding:11px 18px;border-radius:var(--r-card,4px);
  background:var(--sig);color:var(--onsig);text-decoration:none;
  font-family:'IBM Plex Mono',monospace;font-size:12px;letter-spacing:.02em;}
.rc-join:hover{filter:brightness(1.08);}
.rc-join:focus-visible{outline:2px solid var(--fg);outline-offset:2px;}

/* ---- Scripted onboarding -------------------------------------------------
   While the join script is running there is no composer. A question at the top of
   the pane and a box at the bottom are two places to look, and the visitor should
   be typing where they are already reading. Hiding .rc-foot takes the field, the
   Send button and the rule above them in one go, since the rule is its border-top.
   The composer comes back at handBack, when the model takes the conversation. */
/* !important because .rc-host.rc-idle .rc-foot sets display:flex at the same
   specificity, and it won on the inline host: the composer, the openers and the way
   in all stayed on screen through the script. The overlay was unaffected, since that
   idle rule is scoped to .rc-host, which is why this only ever showed on Contribute.
   This is a state, and while the script is running the composer is not there. */
.rc-scripted .rc-foot{display:none !important;}

/* The field is not dressed as a field. No box, no rule, no button. The only mark
   that it is one is the caret, and caret-color paints that in the signal blue.
   A native caret rather than a drawn one: it blinks on its own, it survives an IME
   and a phone keyboard, and it sits where the text actually is. */
.rc-field{
  width:100%;box-sizing:border-box;
  border:0;background:transparent;outline:none;padding:0;margin:0;
  color:var(--fg);caret-color:var(--sig);
  /* Inputs inherit neither font nor alignment. Both have to be asked for, or the
     answer is typed at 13px left-aligned under a centred 19px question. */
  font:inherit;letter-spacing:inherit;text-align:inherit;
}
.rc-field::placeholder{color:var(--mut);opacity:.5;}

.rc-hint{
  margin-top:12px;font-family:'IBM Plex Mono',monospace;font-size:10.5px;
  letter-spacing:.09em;text-transform:uppercase;color:var(--mut);opacity:.55;
}

/* The way past the Slack link for someone who does not want it yet. Deliberately
   quiet: the link is the action, this is the exit, and the two should not read as a
   pair of equal choices. */
.rc-skip{
  display:block;margin-top:14px;border:0;background:none;padding:0;cursor:pointer;
  font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--mut);opacity:.55;
}
.rc-skip:hover{opacity:.9;}
.rc-skip:focus-visible{outline:2px solid var(--fg);outline-offset:3px;}

/* The way in, sitting under the composer. It spends the signal colour because it is
   the one action on this surface that is not a question, and it has to read as
   different in kind from the openers above it rather than as a fifth one. Text, not
   a filled button: the composer's Send is already filled and two solid blues on one
   surface would argue with each other. */
.rc-joinline{
  display:inline-flex;align-items:center;gap:7px;
  border:0;background:none;padding:0;cursor:pointer;
  font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--sig);
}
.rc-joinline .lk-arrow{transition:transform .18s ease;}
.rc-joinline:hover .lk-arrow{transform:translateX(3px);}
.rc-joinline:focus-visible{outline:2px solid var(--sig);outline-offset:4px;border-radius:2px;}
@media(prefers-reduced-motion:reduce){.rc-joinline .lk-arrow{transition:none;}}

/* Gone once the details are in. Offering the way in to someone who has just walked
   through it is the same fault as re-offering the openers mid-conversation. */
.rc-joined .rc-alt{display:none;}

/* The script running inline has to own the fold the way the overlay owns the screen.
   Without a height of its own it renders as a couple of paragraphs appearing in the
   middle of a page, under a headline that is still arguing for attention, and the
   moment reads as a page update rather than as a handover. */
.rc-host.rc-fold .rc-pane{min-height:72vh;justify-content:center;}
@media(max-width:700px){.rc-host.rc-fold .rc-pane{min-height:66vh;}}

/* A callout standing above the steps is a lead-in to them, so it measures against
   them: at 44ch it broke to three lines and stopped well short of the grid's right
   edge, which read as a ragged column rather than as an introduction.
   Scoped with :has so the callouts that sit above prose keep their reading measure,
   where a full-width line at 23px would be far too long to track. */
.callout:has(+ .steps){max-width:none;}

/* ── The canvas entry (2026-09-17, reworked 2026-09-19) ─────────────────────
   Neu leads: its line types at the top and the sections print beneath it one
   line at a time, the way a terminal answers, then open whole and scroll. The
   node graph and its connecting lines were cut on review for reading as a web3
   homepage; the motion is borrowed from terminal output instead, a line at a
   time and a blinking caret, which is what an AI surface looks like.

   Still an enhancement over an ordinary document. Every section is in the
   markup at full length and all of this is built from them at runtime, so with
   no JavaScript `html.nx` is never set and the page reads top to bottom. Do not
   move the content into JavaScript. */

html{--nx-w:1080px;}
html:not(.nx) .nxc,html:not(.nx) .nx-neu,html:not(.nx) .nx-read{display:none;}
html.nx, html.nx body{height:100%;overflow:hidden;overscroll-behavior:none;}
html.nx .nx-flow{display:none;}

/* Neu, at the top. One voice, set large, with the prompt under it. */
.nx-neu{position:fixed;left:0;right:0;top:calc(64px + 9vh);z-index:40;padding:0 24px;
  pointer-events:none;transition:opacity .3s ease;}
.nx-neu-in{max-width:var(--nx-w);margin:0 auto;pointer-events:auto;}
.nx-neu-line{font-size:clamp(22px,3.1vw,34px);line-height:1.3;margin:0 0 30px;
  letter-spacing:-.01em;text-wrap:pretty;}
.nx-neu-k{font-family:var(--fam-mono),'IBM Plex Mono',monospace;font-size:.42em;
  letter-spacing:.08em;color:var(--mut);vertical-align:.45em;margin-right:.9em;}
.nx-caret{display:inline-block;width:.5em;height:1em;margin-left:.12em;vertical-align:-.12em;
  background:var(--sig);animation:nx-blink 1.06s steps(1) infinite;}
@keyframes nx-blink{50%{opacity:0}}
.nx-ask{display:flex;gap:12px;align-items:center;
  border:1.5px solid var(--hair);border-radius:12px;padding:13px 13px 13px 20px;
  background:color-mix(in srgb,var(--bg) 86%,transparent);backdrop-filter:blur(8px);
  transition:border-color .2s ease;}
.nx-ask:focus-within{border-color:var(--fg);}
.nx-ask .nx-slash{font-family:var(--fam-mono),'IBM Plex Mono',monospace;font-size:14px;color:var(--mut);}
.nx-ask input{flex:1;min-width:0;font:inherit;font-size:18px;color:var(--fg);background:none;border:0;padding:6px 0;}
.nx-ask input::placeholder{color:var(--mut);}
.nx-ask input:focus{outline:none;}
.nx-chip{font:inherit;font-size:13.5px;color:var(--fg);background:none;cursor:pointer;
  border:1.5px solid var(--hair);border-radius:999px;padding:7px 14px;white-space:nowrap;
  transition:border-color .2s ease,color .2s ease;}
.nx-chip:hover,.nx-chip:focus-visible{border-color:var(--fg);}
.nx-join{border-color:var(--fg);}

/* Neu's answer, streamed with the same caret. */
.nx-ans{margin:0 0 14px;max-height:34vh;overflow:auto;font-size:15.5px;line-height:1.6;
  animation:nx-line-in .3s ease both;}
.nx-ans-q{margin:0 0 8px;color:var(--mut);font-family:var(--fam-mono),'IBM Plex Mono',monospace;font-size:13px;}
.nx-ans-q::before{content:"> ";}
.nx-ans-a{margin:0;white-space:pre-wrap;}
.nx-ans{position:relative;}
.nx-ans-x{position:absolute;top:8px;right:8px;width:28px;height:28px;border-radius:999px;border:0;
  background:none;color:var(--mut);font-size:18px;line-height:1;cursor:pointer;}
.nx-ans-x:hover,.nx-ans-x:focus-visible{color:var(--fg);}
.nx-ans-q{padding-right:32px;}
/* Where the answer points. Sits under it, in the section's own words. */
.nx-ans-go{margin-top:14px;}

/* The hint: there for anyone looking for it, invisible to everyone else. */
.nx-hint{max-width:var(--nx-w);margin:26px auto 0;font-family:var(--fam-mono),'IBM Plex Mono',monospace;
  font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);opacity:0;
  transition:opacity .5s ease;}
.nx-hint.in{opacity:.6;}
@media(max-width:760px){.nx-hint{display:none;}}

/* The sections, printed a line at a time. */
.nxc{position:fixed;inset:0;z-index:5;overflow:auto;padding:0 24px 72px;background:var(--bg);}
/* Multi-column rather than a grid: a grid lays rows out across the columns, so
   the numbers ran 01, 02 side by side. This fills the left column first, which
   is how a list is read, and it does not care how many sections there are. */
.nx-index{max-width:var(--nx-w);margin:0 auto;column-count:2;column-gap:44px;}
.nx-row{break-inside:avoid;}
.nx-row{display:flex;align-items:baseline;gap:16px;width:100%;text-align:left;font:inherit;color:var(--fg);
  background:none;border:0;border-top:1px solid var(--hair);padding:21px 4px;cursor:pointer;
  opacity:0;transform:translateY(6px);transition:opacity .35s ease,transform .35s ease,color .2s ease;}
.nx-row.in{opacity:1;transform:none;}
.nx-row .nxn-k{font-family:var(--fam-mono),'IBM Plex Mono',monospace;font-size:12px;color:var(--mut);
  letter-spacing:.06em;flex:0 0 auto;}
.nx-row .nxn-t{font-size:21px;line-height:1.35;flex:1;}
.nx-row .nxn-go{color:var(--mut);opacity:0;transform:translateX(-6px);transition:opacity .2s ease,transform .2s ease;}
.nx-row:hover .nxn-go,.nx-row:focus-visible .nxn-go{opacity:1;transform:none;}
.nx-row:focus-visible{outline:1.5px solid var(--fg);outline-offset:2px;border-radius:4px;}
@keyframes nx-line-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}

/* A section, whole. Neu docks at its foot. */
.nx-read{position:fixed;inset:0;z-index:30;display:none;background:var(--bg);}
.nx-read.nx-on{display:block;animation:nx-line-in .35s ease both;}
.nx-read-bar{position:absolute;left:0;right:0;top:0;z-index:2;display:flex;align-items:center;gap:16px;
  padding:14px 24px;background:color-mix(in srgb,var(--bg) 90%,transparent);backdrop-filter:blur(8px);
  border-bottom:1px solid var(--hair);}
.nx-read-k{font-size:14px;color:var(--mut);}
.nx-read-k #nxReadK{font-family:var(--fam-mono),'IBM Plex Mono',monospace;font-size:11px;margin-right:6px;}
/* A column, so on a short section the foot sits just above Neu's dock rather
   than under it: the body takes the slack and the dock's height is the padding. */
.nx-read-scroll{position:absolute;inset:0;overflow:auto;padding:88px 24px 160px;
  display:flex;flex-direction:column;}
.nx-read-body{flex:1 0 auto;width:100%;max-width:var(--nx-w);margin:0 auto;}
.nx-read-body > .sec{padding-top:0;margin-top:0;}
/* Full column in a section, flush with the rules above and below it (Cara,
   2026-09-19). The long page's reading measures cap prose at 530 to 770px,
   which left a ragged gap to the right of every section here. */
.nx-read-body :is(p,h2,h3,.chx-b,.fq-a,.tp-desc,.lead-h,.sec-deck){max-width:none;}
/* The closing ask is a full-screen scene on the long page. Opened here it sat at
   the top over an empty screen, so as the last tab it is centred in the view as
   the finale, at the size of the other section headings. */
.nx-read-body:has(> #join){display:flex;flex-direction:column;justify-content:center;}
.nx-read-body > #join .closing{padding:0;margin:0 auto;min-height:0;max-width:none;}
/* The finale's own button is the ask; the dock's copy of it would repeat it. */
html[data-nx-on="join"] .nx-join{display:none;}
.nx-read-body > #join h2{font-size:clamp(30px,4vw,52px);line-height:1.15;margin:0 0 34px;}
.nx-rv{opacity:0;transform:translateY(12px);transition:opacity .5s ease,transform .5s ease;}
.nx-rv.in{opacity:1;transform:none;}
.nx-read-foot{width:100%;max-width:var(--nx-w);margin:56px auto 0;display:flex;gap:12px;align-items:center;
  border-top:1px solid var(--hair);padding-top:22px;}
.nx-read-foot .nx-spacer{margin-left:auto;}
.nx-read-foot .nx-chip[hidden]{display:none;}

html.nx-reading .nx-neu{top:auto;bottom:34px;}
html.nx-reading .nx-neu-in{max-width:760px;}
html.nx-reading .nx-neu-line{display:none;}
html.nx-reading .nx-ans{background:color-mix(in srgb,var(--bg) 92%,transparent);backdrop-filter:blur(8px);
  border:1.5px solid var(--hair);border-radius:10px;padding:14px 16px;}

/* The thin rail: always on screen, never in the way. */
.nx-rail{position:fixed;left:0;right:0;bottom:0;z-index:45;display:flex;align-items:center;
  gap:14px;padding:5px 24px;font-family:var(--fam-mono),'IBM Plex Mono',monospace;
  font-size:9.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--mut);
  background:var(--bg);border-top:1px solid var(--hair);}
.nx-rail a{color:var(--mut);}
.nx-rail a:hover{color:var(--fg);}
.nx-rail .nx-rail-soc{display:flex;gap:10px;align-items:center;margin-left:auto;}
.nx-rail .nx-rail-soc svg{width:12px;height:12px;display:block;}

/* The writing node. */
.nx-posts > .nx-post{display:block;margin-bottom:32px;}
.nx-post .nx-post-k{font-family:var(--fam-mono),'IBM Plex Mono',monospace;font-size:11px;
  color:var(--mut);margin-bottom:12px;}
.nx-post h3{font-size:clamp(22px,3vw,30px);font-weight:400;line-height:1.22;margin:0 0 14px;}
.nx-post p{margin:0 0 12px;}

/* One line on anything wider than a phone: sized off the viewport so it fits
   the 880px column instead of wrapping to a stranded half-line. */
@media(min-width:761px){
  .nx-neu-line{white-space:nowrap;font-size:min(33px,2.9vw);}
}
@media(prefers-reduced-motion:reduce){
  .nx-caret{animation:none;}
  .nx-row,.nx-rv{opacity:1;transform:none;transition:none;}
  .nx-read.nx-on,.nx-ans{animation:none;}
}
@media(max-width:760px){
  .nx-neu{top:calc(60px + 4vh);padding:0 16px;}
  .nxc{padding:0 16px 72px;}
  .nx-index{column-count:1;}
  .nx-ask{flex-wrap:wrap;}
  .nx-ask input{flex-basis:70%;}
  .nx-join{width:100%;}
  html.nx-reading .nx-neu{bottom:28px;}
  html.nx-reading .nx-join{display:none;}
  .nx-read-scroll{padding:80px 16px 150px;}
  .nx-read-bar{padding:12px 16px;}
  .nx-rail{gap:9px;padding:5px 14px;font-size:9px;}
}

/* ── Memo register, sandbox 2026-09-23 ─────────────────────────────────────
   The "researcher to researcher" hybrid: Gautam's Draft 00 memo forms, in IOV
   type and tokens. Ultramarine stays with the thesis claim and the chart's one
   data mark, so the note and the pull rule in ink. */
.memo-note{font-family:'IBM Plex Mono',monospace;font-size:13.5px;line-height:1.7;color:var(--fg);
  background:var(--paper);border-left:3px solid var(--line);border-radius:4px;padding:14px 18px;margin:0 0 30px;max-width:74ch;}
.memo-pull{font-size:clamp(19px,2.3vw,24px);line-height:1.45;letter-spacing:-.01em;color:var(--fg);
  border-left:3px solid var(--line);padding:4px 0 4px 20px;margin:4px 0 34px;max-width:46ch;}
ol.memo-list{list-style:none;counter-reset:memo;padding:0;margin:8px 0 0;max-width:74ch;}
ol.memo-list li{counter-increment:memo;position:relative;padding-left:2.6em;font-size:16.5px;line-height:1.7;margin:0 0 12px;}
ol.memo-list li::before{content:counter(memo) ".";position:absolute;left:0;top:.2em;font-family:'IBM Plex Mono',monospace;font-size:13px;color:var(--mut);}
ol.memo-list li b{font-weight:600;}

/* Trust against value. Lines draw on when the section is shown (a CSS
   animation restarts when display changes, which is how the reader opens a
   section), and appear finished under reduced motion. */
figure.tv{margin:0 0 8px;max-width:680px;}
figure.tv svg{display:block;width:100%;height:auto;overflow:visible;}
figure.tv figcaption{margin-top:12px;font-size:13px;line-height:1.6;color:var(--mut);max-width:62ch;}
.tv-ax{stroke:var(--fg);stroke-width:1;fill:none;}
.tv-now{stroke:var(--mut);stroke-width:1;stroke-dasharray:3 4;}
.tv-gap{stroke:var(--fg);stroke-width:1;}
.tv-ln{fill:none;stroke-width:2.2;stroke-dasharray:1;stroke-dashoffset:0;}
.tv-trust{stroke:#7b8595;}
.tv-value{stroke:var(--sig);}
.tv-m{fill:var(--mut);}
.tv-f{fill:var(--fg);}
.tv-s{fill:var(--sig-text);}
@media (prefers-reduced-motion:no-preference){
  .tv-ln{animation:tvDraw 2.2s cubic-bezier(.4,0,.2,1) .2s both;}
  .tv-late{animation:tvFade .8s ease 2s both;}
}
@keyframes tvDraw{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}
@keyframes tvFade{from{opacity:0}to{opacity:1}}

/* ── Gautam's Draft 00 memo, in IOV type and colour (memo.html) ────────────
   His markup and class names, restyled: Inter for prose, Plex Mono for the
   document furniture (header, section numbers, tables, figure), IOV greys,
   ultramarine only for the primary action and the shared sequence. Every
   rule is scoped to .gd because his class names are generic. */
body:has(.gd) main.doc{padding:0;max-width:none;}
.gd{padding:0 16px 96px;}
.gd .page{max-width:720px;margin:0 auto;font-size:16px;line-height:1.72;}
.gd p{margin:0;max-width:none;}
.gd a{color:inherit;}
.gd .hdr{display:grid;grid-template-columns:1fr auto;gap:4px 24px;padding:28px 0 22px;border-bottom:1px solid var(--hair);
  font-family:'IBM Plex Mono',monospace;font-size:12.5px;line-height:1.6;color:var(--mut);}
.gd .hdr b{color:var(--fg);font-weight:500;}
.gd .hdr .r{text-align:right;}
.gd .hdr-cta{display:inline-block;margin-bottom:10px;padding:7px 12px;border-radius:4px;background:var(--sig);color:#fff;text-decoration:none;
  font-family:inherit;font-size:12px;font-weight:500;letter-spacing:.02em;}
.gd .hdr-cta:hover{filter:brightness(1.12);}
.gd .title{padding:56px 0 40px;text-align:center;}
.gd .title h1{font-size:clamp(26px,3.6vw,34px);line-height:1.25;font-weight:500;letter-spacing:-.01em;margin:0;}
.gd .title .sub{margin:16px auto 0;max-width:60ch;font-size:16.5px;line-height:1.6;color:var(--mut);}
.gd section{padding:26px 0 6px;margin:0;border:0;min-height:0;}
.gd section h2{display:flex;gap:16px;margin:0 0 14px;font-family:'IBM Plex Mono',monospace;font-size:13.5px;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;line-height:1.4;}
.gd section h2 .n{min-width:3ch;color:var(--mut);font-weight:400;}
.gd .body{padding-left:calc(3ch + 16px);display:grid;gap:16px;}
.gd .memo{font-family:'IBM Plex Mono',monospace;font-size:13.5px;line-height:1.7;background:var(--paper);border-left:3px solid var(--line);
  border-radius:4px;padding:14px 18px;}
.gd mark{background:none;color:inherit;text-decoration:underline;text-decoration-color:var(--sig-text);text-decoration-thickness:1.5px;text-underline-offset:3px;}
.gd em.claim{font-style:normal;font-weight:600;}
.gd .pull{font-size:19px;line-height:1.5;border-left:3px solid var(--line);padding:2px 0 2px 18px;}
.gd figure.tv{max-width:none;margin:6px 0 0;}
.gd .q{display:grid;grid-template-columns:4.5ch 1fr;gap:10px 0;}
.gd .q .k{font-family:'IBM Plex Mono',monospace;font-size:13px;color:var(--mut);padding-top:3px;}
.gd .q b{font-weight:600;}
.gd .tbl{border-top:1px solid var(--hair);}
.gd .tbl div{display:grid;grid-template-columns:minmax(12ch,18ch) 1fr;gap:0 2ch;padding:9px 0;border-bottom:1px solid var(--hair);font-size:15px;}
.gd .tbl div span:first-child{color:var(--mut);}
.gd .cta{margin-top:24px;padding:20px 22px;border:1px solid var(--line);border-radius:4px;display:flex;justify-content:space-between;gap:16px;align-items:center;flex-wrap:wrap;}
.gd .cta a.b{background:var(--sig);color:#fff;padding:10px 16px;border-radius:4px;font-weight:500;text-decoration:none;font-size:15px;}
.gd .cta a.b:hover{filter:brightness(1.12);}
.gd .foot{margin-top:48px;padding-top:14px;border-top:1px solid var(--hair);display:flex;justify-content:space-between;flex-wrap:wrap;gap:8px 16px;
  font-family:'IBM Plex Mono',monospace;font-size:12px;color:var(--mut);}
.gd .foot-r{display:flex;align-items:center;gap:16px;flex-wrap:wrap;}
.gd .foot a{color:var(--mut);text-decoration:none;}
.gd .foot a:hover{color:var(--fg);}
.gd .foot-soc{display:inline-flex;gap:10px;}
/* Figure 1 */
.gd .fig-live{margin:0 0 20px;}
.gd .stage{position:relative;min-height:400px;border:1px solid var(--hair);border-radius:4px;background:var(--bg);overflow:hidden;}
.gd .stage canvas{position:absolute;inset:0;width:100%;height:100%;display:block;}
.gd .hud{position:absolute;left:12px;top:10px;display:flex;gap:14px;font-family:'IBM Plex Mono',monospace;font-size:11.5px;color:var(--mut);}
.gd .hud b{color:var(--fg);font-weight:500;}
.gd .figbar{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-top:10px;
  font-family:'IBM Plex Mono',monospace;font-size:12.5px;line-height:1.55;color:var(--mut);}
.gd .figbar > span:first-child{flex:1 1 320px;}
.gd .seg{display:inline-flex;border:1px solid var(--hair);border-radius:4px;overflow:hidden;}
.gd .seg button{appearance:none;border:0;background:transparent;color:var(--mut);font:inherit;font-size:11.5px;letter-spacing:.1em;text-transform:uppercase;padding:6px 10px;cursor:pointer;}
.gd .seg button[aria-pressed="true"]{background:var(--fg);color:var(--bg);}
.gd .seg button:focus-visible{outline:2px solid var(--sig);outline-offset:-2px;}
@media (max-width:600px){
  .gd .hdr{grid-template-columns:1fr;}
  .gd .hdr .r{text-align:left;}
  .gd .body{padding-left:0;}
  .gd .tbl div{grid-template-columns:1fr;gap:2px;}
}
/* Blog: three follow buttons in a row (2026-09-23); post pages' back line. */
.blog-follow{display:flex;flex-wrap:wrap;gap:12px;}
.post-hero{padding-bottom:0;}
.post-meta{font-family:'IBM Plex Mono',monospace;font-size:12.5px;color:var(--mut);}
.post-meta a{color:var(--mut);}
/* Andreas's pass (2026-09-24): no header block, Join top right, the opening
   sentence is the h1. Consistency pass on Gautam's note: Inter for every
   sentence, Plex Mono only for labels (section numbers and heads, figure
   furniture, footer), one body size, and the pull quote at body weight. */
.gd .topcta{display:flex;justify-content:flex-end;padding:24px 0 0;}
.gd .hdr-cta{margin:0;}
.gd .title{padding:40px 0 36px;text-align:left;border-bottom:1px solid var(--hair);margin-bottom:28px;}
.gd .title h1.lead{font-size:clamp(20px,2.4vw,24px);line-height:1.5;font-weight:400;letter-spacing:-.005em;margin:0;}
.gd .pull{font-size:18px;line-height:1.6;}
.gd .q span:not(.k){font-size:16px;}
.gd .figbar{font-family:'IBM Plex Mono',monospace;}
/* 2026-09-24, second look: with the header block gone the opening is the
   page's title, so it is centred like Gautam's title block, with balanced
   lines; Join the research sits in the page's own top-right corner rather
   than at the edge of the text column. */
.gd{position:relative;}
.gd .topcta{position:absolute;top:20px;right:24px;padding:0;}
.gd .title{padding:88px 0 44px;text-align:center;}
.gd .title h1.lead{max-width:36ch;margin:0 auto;font-size:clamp(21px,2.6vw,26px);line-height:1.45;text-wrap:balance;}
@media (max-width:600px){
  .gd .topcta{right:16px;top:16px;}
  .gd .title{padding-top:76px;}
}
/* Top bar (2026-09-24): Join the research at the far right of every page.
   The memo page uses the site bar now instead of its own corner button. */
.tb-join{display:inline-flex;align-items:center;padding:8px 14px;border-radius:4px;background:var(--sig);color:#fff;text-decoration:none;
  font-size:13px;font-weight:500;letter-spacing:.01em;white-space:nowrap;}
.tb-join:hover{filter:brightness(1.12);}
.gd .title{padding:56px 0 44px;}
/* 2026-09-24, third look (Cara: "shouldn't be as tight", title back above):
   a real title, and the sentence as a wide, airy deck under it. */
.gd .title{padding:72px 0 48px;text-align:center;}
.gd .title h1{font-size:clamp(34px,5vw,52px);line-height:1.1;font-weight:500;letter-spacing:-.02em;margin:0;}
.gd .title .deck{max-width:62ch;margin:24px auto 0;font-size:18px;line-height:1.75;color:var(--mut);text-wrap:pretty;display:grid;gap:10px;justify-items:center;}
.gd .title .deck .deck-part{display:block;width:100%;max-width:60ch;margin:0 auto;text-align:center;}
.gd .title .deck .deck-main{text-wrap:balance;}
@media (max-width:600px){.gd .title{padding:44px 0 36px;}.gd .title .deck{font-size:16.5px;gap:10px;}}

/* Board page (2026-09-29, d2). Vertical and narrow, after the memo home
   page: a 720px reading column, centered title with an airy deck, and one
   hairline-separated row per member (name, role, bio, email). The portrait
   is a monogram block until real photos land, then an <img> takes the same
   box; the empty seat keeps the box with a dashed hairline. */
.bm-page,.pg-page{max-width:720px;margin:0 auto;padding:0 16px;}
.bm-title,.pg-title{padding:72px 0 48px;text-align:center;}
.bm-title h1,.pg-title h1{font-size:clamp(34px,5vw,52px);line-height:1.1;font-weight:500;letter-spacing:-.02em;margin:0;}
.bm-deck,.pg-deck{max-width:56ch;margin:20px auto 0;font-size:17px;line-height:1.7;color:var(--mut);text-wrap:pretty;}
.bm-row{display:grid;grid-template-columns:96px 1fr;gap:24px;padding:30px 0;border-bottom:1px solid var(--hair);align-items:start;}
.bm-row:first-child{padding-top:8px;}
.bm-row:last-child{border-bottom:0;}
.bm-photo{width:96px;height:96px;border-radius:8px;background:var(--bg);border:1.5px solid var(--hair);
  display:flex;align-items:center;justify-content:center;overflow:hidden;}
.bm-photo span{font-family:'IBM Plex Mono',monospace;font-size:19px;letter-spacing:.12em;color:var(--mut);text-indent:.12em;}
.bm-photo.bm-empty{border-style:dashed;}
.bm-photo img{width:100%;height:100%;object-fit:cover;display:block;}
.bm-role{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--sig);margin-bottom:8px;}
.bm-info h3{font-size:20px;font-weight:700;letter-spacing:-.01em;margin-bottom:10px;}
.bm-info p{font-size:15px;line-height:1.7;color:var(--mut);max-width:52ch;}
.bm-mail{font-family:'IBM Plex Mono',monospace;font-size:12px;color:var(--sig);text-decoration:none;display:inline-block;margin-top:14px;}
.bm-mail:hover{text-decoration:underline;}
@media(max-width:600px){
  .bm-title,.pg-title{padding:44px 0 36px;}
  .bm-row{grid-template-columns:72px 1fr;gap:18px;padding:24px 0;}
  .bm-photo{width:72px;height:72px;}
}

/* Contribute page (2026-09-29). Same narrow reading column as the board
   page (pg-* above); cb-* is the numbered mono section heads, the research
   questions definition list, and the single GitHub CTA (placeholder href
   until the org repo exists). Caps on heads and terms come from CSS, so the
   markdown mirrors read clean title case. */
.cb-sec{padding:30px 0 6px;}
.cb-sec h2{margin:0 0 14px;font-family:'IBM Plex Mono',monospace;font-size:12.5px;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;line-height:1.4;}
.cb-sec h2 .n{color:var(--mut);font-weight:400;}
.cb-sec p{font-size:16px;line-height:1.75;max-width:60ch;margin:0;}
.cb-sec p+p{margin-top:12px;}
.cb-tbl{border-top:1px solid var(--hair);}
.cb-tbl>div{display:grid;grid-template-columns:180px 1fr;gap:2px 18px;padding:13px 0;border-bottom:1px solid var(--hair);}
.cb-tbl span:first-child{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.1em;text-transform:uppercase;padding-top:3px;white-space:nowrap;}
.cb-tbl span:last-child{color:var(--mut);font-size:14px;line-height:1.6;}
.cb-cta{margin-top:20px;}
.cb-list{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:1fr 1fr;gap:8px 24px;max-width:34em;}
.cb-list li{position:relative;padding-left:16px;font-size:15px;line-height:1.6;color:var(--mut);}
.cb-list li::before{content:"";position:absolute;left:0;top:.67em;width:5px;height:5px;background:var(--mut);}
@media(max-width:600px){
  .cb-tbl>div{grid-template-columns:1fr;gap:4px;}
  .cb-list{grid-template-columns:1fr;}
}

/* External-link arrow (2026-09-29): on every CTA that leaves the site
   (Slack, GitHub). Nudges up-right on hover, still under reduced motion. */
.lk-ext{margin-left:6px;vertical-align:-1px;transition:transform .15s ease;}
a:hover .lk-ext,button:hover .lk-ext{transform:translate(1.5px,-1.5px);}
@media(prefers-reduced-motion:reduce){
  .lk-ext{transition:none;}
  a:hover .lk-ext,button:hover .lk-ext{transform:none;}
}

/* Swiss association registration line (placeholder, 2026-09-29): its own line
   under the wordmark in the site footer. */
footer .reg{display:block;margin-top:8px;}

/* The memo page uses the site footer now (2026-09-30). body:has(.gd) strips
   main.doc's padding and max-width, so the footer needs the .doc content box
   back: 968 - 2*44 = 880 wide, 44px insets, and the 72px of space after it
   that .doc's bottom padding gives every other page. */
body:has(.gd) main.doc{padding:0 0 72px;}
body:has(.gd) main.doc footer{width:calc(100% - 88px);max-width:880px;margin:0 auto;padding:24px 0 0;}
@media(max-width:640px){
  body:has(.gd) main.doc{padding-bottom:56px;}
  body:has(.gd) main.doc footer{width:calc(100% - 48px);}
}

/* Inline links in contribute body copy: fg with the signal underline, same
   treatment as the memo's <mark>. */
.cb-sec p a{color:var(--fg);text-decoration:underline;text-decoration-color:var(--sig-text);
  text-decoration-thickness:1.5px;text-underline-offset:3px;}
.cb-sec p a:hover{text-decoration-color:var(--fg);}

/* ============ 2026-09-30: manifesto page, featured blog card, card thumbnails ============ */

/* The manifesto card: same size as every other card, distinguished only by
   the signal border, the Manifesto tag (already signal) in place of a date,
   and the signal mark in its placeholder thumb — a quiet pop, not a banner. */
.bcard-mani{border:2px solid var(--sig);}
.bcard-mani .bc-img.bc-ph::after{background:var(--sig);opacity:.9;}

/* The manifesto is a single centred reading column, set larger and looser than
   body copy. No cards, no grid: the text is the page. */
.mani{max-width:640px;margin:64px auto 0;font-size:17px;line-height:1.85;color:var(--fg);}
.mani p{margin:0 0 1.7em;}
.mani-lede{font-family:var(--fam-display);font-size:clamp(19px,2vw,23px);line-height:1.55;letter-spacing:-.01em;}
/* The one pull line, and the page's one use of ultramarine. */
.mani-pull{font-family:var(--fam-display);font-size:clamp(24px,3vw,32px);font-weight:700;letter-spacing:-.02em;line-height:1.2;color:var(--sig);margin:2.4em 0;}
.mani a{color:inherit;text-decoration:underline;text-decoration-color:var(--sig);text-decoration-thickness:1.5px;text-underline-offset:3px;}
.mani a:hover{text-decoration-color:var(--fg);}
@media(max-width:640px){.mani{margin-top:44px;font-size:16px;}}

/* Placeholder card thumbnail (bc-ph): a quiet diagonal hatch with the mark
   centred, until real art exists. Distinct from .bcard-ph .bc-img.ph, which
   belongs to the retired dashed placeholder CARD. */
.bcard .bc-img.bc-ph{background:var(--bg);background-image:repeating-linear-gradient(-45deg,transparent 0 9px,var(--hair) 9px,transparent 10px);position:relative;}
.bcard .bc-img.bc-ph::after{content:"";position:absolute;left:50%;top:50%;width:30px;height:26px;transform:translate(-50%,-50%);background:var(--mut);opacity:.5;clip-path:polygon(8% 12%,92% 12%,50% 88%);}

/* Open Value lockup (2026-09-30) is 2.4x the width of the IOV one. Below 440px it
   pushed the theme toggle and the Join button onto their own rows; a shorter mark
   keeps the bar on one line. */
@media(max-width:440px){.tb-mark svg{height:16px;}}
@media(max-width:380px){.tb-mark svg{height:13px;}}
/* Under 357px even that wraps: show the mark alone. The lockup SVG is xMinYMid slice,
   so a narrow box crops it to the mark instead of shrinking the whole lockup. */
@media(max-width:356px){.tb-mark svg{height:22px;width:27px;}}
