/* ==========================================================================
   cyberlop.com — the homepage experience.
   Six chapters, six layouts: an editorial open, a telemetry dashboard, an
   instrument HUD, a horizontal case file, a code editor that rewrites itself,
   and the seal. Loaded only by index.html, on top of site.css.
   Palette: Brand Kit v1.0 — Sentinel action, Cipher brand, Mint verified,
   Coral critical, Vault Ink ground.
   ========================================================================== */

body{background:var(--ink-900); overflow-x:hidden}
body::before{display:none}   /* the grain film costs a full-screen repaint */
body header.nav{background:transparent; border-bottom:0}
/* the reference keeps its chrome to a logo, one action and a menu — so does this */
body .menu .mi,
body .menu .signin,
body .menu > a:not(.btn){display:none}
body .menu{margin-left:auto; gap:10px}
body .burger{display:flex; margin-left:12px}
body .nav-in{height:64px}
body header.nav.stuck{background:rgb(5 8 11 / .6)}


/* ---------- preloader: the mark draws itself as the page loads ---------- */
.pre{position:fixed; inset:0; z-index:200; background:var(--ink-900); display:grid; place-items:center;
  transition:opacity .8s ease, visibility .8s}
.pre.gone{opacity:0; visibility:hidden}
.pre-in{width:min(320px,74vw); display:grid; justify-items:center}
.pre-mark{width:132px; height:132px; display:block; margin-bottom:30px; overflow:visible}
.p-ring{fill:none; stroke:url(#pg); stroke-width:9; stroke-linecap:round;
  stroke-dasharray:180; stroke-dashoffset:180}
.p-seal{fill:none; stroke:var(--mint-500); stroke-width:9; stroke-linecap:round;
  stroke-dasharray:72; stroke-dashoffset:72}
.p-core{fill:var(--mint-500); opacity:0; transform-origin:50px 50px}
.pre-row{display:flex; align-items:baseline; justify-content:space-between; width:100%;
  font-family:var(--mono); font-size:10.5px; letter-spacing:.24em; text-transform:uppercase;
  color:var(--muted); margin-bottom:12px}
.pre-row b{font-family:var(--display); font-size:34px; font-weight:700; letter-spacing:-.04em;
  color:var(--paper-100)}
.pre-track{width:100%; height:1px; background:var(--ink-700); overflow:hidden}
.pre-bar{display:block; height:100%; transform:scaleX(0); transform-origin:0 50%;
  background:linear-gradient(90deg,var(--sentinel-500),var(--cipher-500) 60%,var(--mint-500))}
.pre-t{font-family:var(--mono); font-size:10.5px; letter-spacing:.14em; color:var(--muted);
  margin:14px 0 0; justify-self:start}

/* ---------- stage ------------------------------------------------------- */
.xp-root{position:relative}
/* no transition: xp.js writes this every frame once the page is revealed, and
   a CSS transition would turn each write into its own easing curve */
#gl{position:fixed; inset:0; width:100%; height:100%; z-index:0; pointer-events:none; opacity:0}
.grade{position:fixed; inset:0; z-index:-1; pointer-events:none;
  background:radial-gradient(120% 90% at 50% 8%, var(--g2,#0A1430), var(--g1,#05080B) 68%)}
.flash{position:fixed; inset:0; z-index:5; pointer-events:none; opacity:0;
  background:linear-gradient(90deg, transparent, rgb(31 111 235 / .18), transparent)}
html:not(.has-xp) .grade,html:not(.has-xp) .flash{display:none}
/* The loading screen is a fixed overlay that only script takes down, so it
   must not exist at all unless the experience is genuinely starting up. */
html:not(.has-xp) .pre{display:none}
html:not(.has-xp) #gl{display:none}

/* 220vh per chapter: long enough to read, short enough that a scroll
   always moves the scene. 300vh read as "nothing is happening". */
.ch{position:relative; z-index:2; height:220vh}
.ch-in{position:relative; min-height:100vh; height:100vh;
  min-height:100dvh; height:100dvh; overflow:hidden;
  padding:96px clamp(24px,5vw,80px) 64px}
@media(max-width:820px){.ch{height:180vh} .ch-in{padding:88px 22px 48px}}
/* the chapter rail lives in the right margin — give the copy room for it,
   except in the centred closing chapter, which would tilt off axis */
@media(min-width:1101px){
  html.has-xp .sc-surface .ch-in,
  html.has-xp .sc-patch .ch-in{padding-right:clamp(96px,8vw,150px)}
}

/* With the experience running, a chapter's stage is FIXED to the viewport and
   the section behind it is only scroll length. Nothing in the DOM moves while
   you scroll, so there is nothing to re-rasterise and nothing to judder —
   position:sticky and a smooth-scroll library shake pinned text, because the
   browser snaps sticky offsets to whole pixels while the scroll is fractional. */
html.has-xp .ch-in{
  position:fixed; inset:0; height:100vh; height:100dvh; z-index:2;
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .62s cubic-bezier(.4,0,.2,1), visibility .62s;
}
html.has-xp .ch.on .ch-in{opacity:1; visibility:visible; pointer-events:auto}
html.has-xp .ch.off .ch-in{opacity:0; visibility:hidden}
/* the footer is the one thing that scrolls over the fixed stage */
body .foot{position:relative; z-index:6; background:var(--ink-900)}
html.has-xp .scroll-cue{position:fixed; opacity:0; transition:opacity .4s ease}
html.has-xp .ch.on .scroll-cue{opacity:1}

html:not(.has-xp) .ch{height:auto}
html:not(.has-xp) .ch-in{position:static; height:auto; min-height:0; overflow:visible;
  max-width:var(--maxw); margin:0 auto; padding:70px 28px}

.ch-n{font-family:var(--mono); font-size:10.5px; letter-spacing:.26em; text-transform:uppercase;
  color:var(--sentinel-100); margin:0 0 16px; display:flex; align-items:center; gap:11px}
.ch-n::before{content:""; width:26px; height:1px; background:var(--action)}
/* Every word here sits on top of a moving scene, so type carries its own
   separation: a soft ink shadow under headings and body, and no 500-step
   greys as text. Contrast is checked against the darkest frame of each grade. */
.ch h2{font-size:clamp(30px,4.1vw,58px); line-height:1.04; letter-spacing:-.035em;
  text-shadow:0 3px 26px rgb(5 8 11 / .9)}
.ch .lede{color:var(--paper-300); max-width:52ch; text-shadow:0 2px 18px rgb(5 8 11 / .9)}
.ch-n{text-shadow:0 1px 3px rgb(5 8 11 / .95), 0 2px 16px rgb(5 8 11 / .9)}
.ch-more{margin:22px 0 0; font-family:var(--mono); font-size:12.5px}
.ch-more a{color:var(--action-text); text-decoration:none; border-bottom:1px solid rgb(76 141 255 / .35);
  padding-bottom:2px; transition:color .2s, border-color .2s}
.ch-more a:hover{color:#fff; border-bottom-color:#fff}
.ch-more .arw{display:inline-block; transition:transform .2s}
.ch-more a:hover .arw{transform:translateX(3px)}

/* the scene sits behind the copy, so each layout carries its own scrim */
.ch-in::before{content:""; position:absolute; inset:0; z-index:-1; pointer-events:none}
.sc-open .ch-in::before{background:linear-gradient(180deg, rgb(5 8 11 / .3) 6%, transparent 28%, rgb(5 8 11 / .62) 72%, rgb(5 8 11 / .94))}
.sc-surface .ch-in::before{background:linear-gradient(90deg, rgb(5 8 11 / .95) 0%, rgb(5 8 11 / .72) 38%, rgb(5 8 11 / .26) 68%, transparent 86%)}
.sc-matrix .ch-in::before{background:linear-gradient(90deg, rgb(5 8 11 / .96) 0%, rgb(5 8 11 / .8) 26%, rgb(5 8 11 / .3) 48%, transparent 66%)}
.sc-patch .ch-in::before{background:linear-gradient(90deg, rgb(5 8 11 / .94) 0%, rgb(5 8 11 / .68) 32%, rgb(5 8 11 / .22) 58%, transparent 76%)}
.sc-close .ch-in::before{background:radial-gradient(46% 34% at 50% 48%, rgb(5 8 11 / .88), rgb(5 8 11 / .45) 52%, transparent 78%)}
.sc-open .ch-in::after{content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  opacity:var(--veil,0);
  background:radial-gradient(62% 48% at 20% 80%, rgb(5 8 11 / .93), rgb(5 8 11 / .55) 52%, transparent 80%)}
html:not(.has-xp) .ch-in::before,
html:not(.has-xp) .sc-open .ch-in::after{display:none}

/* ============ 01 · OPEN — the mark is the hero ========================== */
/* Nothing but the mark at rest: no tag row, no read-out. The four points are
   the only words, and they arrive one at a time during the opener. */
.sc-open .ch-in{display:grid; grid-template-rows:1fr auto; gap:16px;
  justify-items:start; text-align:left}

/* The WebGL mark fills the middle of the stage, so the four points hang off
   the edges of the chapter rather than off a box — they annotate the frame.
   The insets are keyed to viewport HEIGHT: fixed pixels were tuned on a tall
   window and put 01 on the tag row and 03 through the lede on a short one. */
/* The copy sits bottom-left at full size, so the four points frame the UPPER
   part of the stage — a wide, short rectangle — rather than the whole of it.
   A frame reaching the bottom put 02 straight through the headline. */
.stage4{position:absolute; z-index:2; pointer-events:none;
  top:clamp(104px,13vh,150px); bottom:clamp(400px,52vh,560px);
  left:clamp(24px,5vw,80px); right:clamp(24px,5vw,80px)}
/* the rail lives in the right margin, so 01 and 04 stop short of it */
@media(min-width:1101px){.stage4{right:clamp(96px,12vw,172px)}}
/* on a short window the sub-lines are what push the labels into the copy */
@media(max-height:780px){.an em{display:none}}
/* text-align:left explicitly: the chapter centres its copy now, and without
   this the labels centre themselves over their own sub-lines */
.an{position:absolute; display:flex; align-items:flex-start; gap:11px; width:max-content;
  max-width:min(300px,21vw); opacity:.78; transition:opacity .5s ease; text-align:left}
.an.on{opacity:1}
.an b{display:grid; place-items:center; flex:none; width:38px; height:38px;
  border:1px solid var(--border); border-radius:10px; background:rgb(11 15 20 / .85);
  color:var(--paper-300); transition:color .4s, border-color .4s, background .4s}

/* Each pointer carries the mark itself, playing the beat it names — the same
   four moments the big mark works through in the middle of the stage. The ring
   takes the tile's colour, so it lights with the stage; the core stays mint,
   because that is the one part of the mark that never changes. */
.mk{width:26px; height:26px; display:block; overflow:hidden}
.mk path,.mk circle{fill:none; stroke-width:8; stroke-linecap:round; stroke-linejoin:round}
.mk .mk-ring{stroke:currentColor; opacity:.6}
.mk .mk-core{fill:var(--mint-500); stroke:none}

/* 01 — a reading head runs the loop */
.mk .mk-scan{stroke:currentColor; stroke-dasharray:20 160; stroke-dashoffset:180;
  animation:mk-scan 3.2s linear infinite}
@keyframes mk-scan{from{stroke-dashoffset:180} to{stroke-dashoffset:0}}

/* 02 — the exploit goes in through the mouth of the ring */
.mk .mk-shot{fill:currentColor; stroke:none; opacity:0;
  transform-box:view-box; transform-origin:50px 50px;
  animation:mk-shot 3.4s cubic-bezier(.4,0,.2,1) infinite}
@keyframes mk-shot{
  0%{transform:translateX(42px); opacity:0}
  16%{opacity:1}
  58%{transform:translateX(0); opacity:1}
  74%,100%{transform:translateX(0); opacity:0}
}

/* 03 — the patch closes onto the core */
.mk .mk-patch{stroke:currentColor; stroke-width:6; opacity:0;
  transform-box:view-box; transform-origin:50px 50px;
  animation:mk-patch 3.6s ease-in-out infinite}
@keyframes mk-patch{
  0%{transform:scale(1.55); opacity:0}
  26%{opacity:.95}
  66%{transform:scale(1); opacity:.95}
  100%{transform:scale(1); opacity:0}
}

/* 04 — the seal draws and the loop is closed */
.mk .mk-seal{stroke:var(--mint-500); stroke-dasharray:72; stroke-dashoffset:72;
  animation:mk-seal 3.8s ease-in-out infinite}
@keyframes mk-seal{
  0%{stroke-dashoffset:72} 42%,80%{stroke-dashoffset:0} 100%{stroke-dashoffset:72}
}

/* nothing animates until the opener has handed that pointer out */
.mk .mk-scan,.mk .mk-shot,.mk .mk-patch,.mk .mk-seal{animation-play-state:paused}
.an.lit .mk-scan,.an.lit .mk-shot,.an.lit .mk-patch,.an.lit .mk-seal{animation-play-state:running}
.an span{font-family:var(--display); font-size:16.5px; font-weight:600; color:var(--paper-100);
  line-height:1.25; text-shadow:0 2px 18px rgb(5 8 11 / .95)}
.an span i{display:block; font-style:normal; font-family:var(--mono); font-size:9.5px;
  font-weight:400; letter-spacing:.22em; color:var(--muted); margin-bottom:3px}
.an em{display:block; font-style:normal; font-family:var(--mono); font-size:10.5px;
  letter-spacing:.03em; color:var(--paper-300); margin-top:4px; font-weight:400; line-height:1.5;
  text-shadow:0 2px 14px rgb(5 8 11 / .95)}
.an::after{content:""; position:absolute; top:19px; width:52px; height:1px;
  background:linear-gradient(90deg,var(--border),transparent); transition:background .4s}
/* the four corners of the stage, reading down the left then down the right */
.an1{left:0; top:0}          .an1::after{right:-58px}
.an2{left:0; bottom:0}       .an2::after{right:-58px}
.an3{right:0; bottom:0; flex-direction:row-reverse; text-align:right}
.an3::after{left:-58px; background:linear-gradient(270deg,var(--border),transparent)}
.an4{right:0; top:0; flex-direction:row-reverse; text-align:right}
.an4::after{left:-58px; background:linear-gradient(270deg,var(--border),transparent)}
@media(max-width:1100px){.an{max-width:27vw} .an::after{display:none}}
/* A phone has no corners to spare: the four points come out of the frame and
   sit as a 2x2 block under the mark, which still animates behind them. */
@media(max-width:820px){
  /* minmax(0,1fr), not 1fr: a bare 1fr takes its minimum from the item's
     max-content, so the longest label pushes the column off the screen */
  .stage4{position:static; margin-top:auto; display:grid;
    grid-template-columns:repeat(2, minmax(0, 1fr)); gap:10px 14px; margin-bottom:16px}
  .an{position:static; width:auto; max-width:100%; min-width:0; gap:8px;
    align-items:center; opacity:.8}
  .an span{min-width:0}
  .an3,.an4{flex-direction:row; text-align:left}
  .an span{font-size:13.5px; line-height:1.2}
  .an em{display:none}
  .an::after{display:none}
}
/* Hidden until the opener's reading head passes them. .lit is only ever added,
   never removed, so it survives the stage cycle that runs afterwards. */
html.has-xp .stage4 .an{opacity:0; translate:0 12px;
  transition:opacity .7s ease, translate .7s cubic-bezier(.16,1,.3,1)}
html.has-xp .stage4 .an.lit{opacity:.8; translate:0 0}
html.has-xp .stage4 .an.lit.on{opacity:1}
html.has-xp .an::after{width:0; transition:width .65s cubic-bezier(.16,1,.3,1), background .4s}
html.has-xp .an.lit::after{width:52px}

/* each stage lights in its own colour */
.an1.on b{color:var(--sentinel-300); border-color:rgb(76 141 255 / .5); background:rgb(31 111 235 / .14)}
.an2.on b{color:var(--coral-500); border-color:rgb(255 59 92 / .5); background:rgb(255 59 92 / .12)}
.an3.on b{color:var(--data-2); border-color:rgb(154 128 255 / .5); background:rgb(124 92 255 / .14)}
.an4.on b{color:var(--mint-500); border-color:rgb(0 229 160 / .5); background:rgb(0 229 160 / .12)}
.an1.on::after{background:linear-gradient(90deg,var(--sentinel-300),transparent)}
.an2.on::after{background:linear-gradient(90deg,var(--coral-500),transparent)}
.an3.on::after{background:linear-gradient(270deg,var(--data-2),transparent)}
.an4.on::after{background:linear-gradient(270deg,var(--mint-500),transparent)}

/* Bottom left, at full size — one stack, not two columns, so there is a single
   left edge for the headline, the sentence and the buttons to line up on. */
.open-say{align-self:end; display:grid; justify-items:start;
  gap:clamp(12px,2vh,20px); max-width:min(760px,60vw); text-align:left}
.mega{margin:0; font-family:var(--display); font-weight:700; letter-spacing:-.04em; line-height:1.02;
  /* The floor was 30px, which forced the second line to wrap on a phone and
     made a two-line headline into three. 24px holds 27 characters inside a
     360px column, and the steeper vw keeps it large from tablet up. */
  font-size:clamp(24px,6.4vw,58px); text-shadow:0 2px 24px rgb(5 8 11 / .7)}
.mw{display:block; overflow:hidden; padding-bottom:.06em}
.mega .w{display:block}
.mega .g{font-style:normal; background:var(--grad); -webkit-background-clip:text;
  background-clip:text; color:transparent}
.mega .m{color:var(--mint-500)}
.open-lede{margin:0; max-width:48ch; font-size:clamp(15px,1.15vw,17.5px); line-height:1.6;
  color:var(--paper-300); text-shadow:0 2px 16px rgb(5 8 11 / .9)}
.open-cta{display:flex; gap:12px; flex-wrap:wrap}
/* the three things a buyer wants to know before they read anything else */
.trust{width:100%; display:flex; flex-wrap:wrap; gap:9px 26px;
  list-style:none; margin:0; padding:14px 0 0; border-top:1px solid var(--rule);
  font-family:var(--mono); font-size:11px; letter-spacing:.04em; color:var(--paper-300)}
.trust li{display:flex; align-items:center; gap:9px}
.trust li::before{content:""; width:5px; height:5px; border-radius:50%; background:var(--mint-500);
  flex:none; box-shadow:0 0 10px rgb(0 229 160 / .7)}
/* A centred hero reads badly in a narrow column, so the phone stacks it left.
   This has to sit AFTER .open-say/.open-cta/.trust above: same specificity, so
   source order is what decides, and a media query does not change that. */
@media(max-width:820px){
  .sc-open .ch-in{display:flex; flex-direction:column; gap:14px; text-align:left}
  .open-say{align-self:auto; justify-items:start; max-width:none; text-align:left}
  .open-cta{justify-content:flex-start}
  .trust{justify-content:flex-start}
}
@media(max-width:600px){.trust li:nth-child(n+3){display:none}}
.boot{display:none}
.scroll-cue{position:absolute; left:50%; translate:-50% 0; bottom:26px; z-index:3;
  pointer-events:none;
  font-family:var(--mono); font-size:10px; letter-spacing:.24em; text-transform:uppercase;
  color:var(--paper-300); display:flex; flex-direction:column; align-items:center; gap:10px}
.scroll-cue::after{content:""; width:1px; height:32px;
  background:linear-gradient(180deg,var(--action),transparent)}
html:not(.has-xp) .scroll-cue{display:none}
/* on a phone the gesture needs no invitation, and the rule runs off the edge */
@media(max-width:820px){.scroll-cue{display:none}}
html:not(.has-xp) .stage4{position:static; inset:auto; display:grid; gap:16px; margin:24px 0}
html:not(.has-xp) .an{position:static; opacity:1; max-width:none; flex-direction:row; text-align:left}
html:not(.has-xp) .an em{display:block}
html:not(.has-xp) .an::after{display:none}
html:not(.has-xp) .sc-open .ch-in{display:block; text-align:left}
html:not(.has-xp) .open-say{align-self:auto; justify-items:start; max-width:none; margin-bottom:26px}
html:not(.has-xp) .open-cta,
html:not(.has-xp) .trust{justify-content:flex-start}

/* ============ 02 · SURFACE — dashboard =================================== */
.sc-surface .ch-in{display:grid; align-content:center}
.dash{display:grid; grid-template-columns:minmax(0,380px) minmax(0,1fr); gap:clamp(24px,5vw,72px);
  align-items:center}
@media(max-width:940px){.dash{grid-template-columns:1fr; gap:26px}}
.tele{border:1px solid var(--border); border-radius:14px; background:rgb(10 13 18 / .93); overflow:hidden; box-shadow:0 40px 80px -40px rgb(0 0 0 / .9)}
.tele-h{display:flex; align-items:center; gap:9px; padding:13px 16px; border-bottom:1px solid var(--rule);
  font-family:var(--mono); font-size:11px; letter-spacing:.08em; color:var(--paper-300); background:rgb(23 29 38 / .7)}
.tele-stats{display:grid; grid-template-columns:1fr 1fr; gap:1px; background:var(--rule)}
.st8{background:rgb(10 13 18 / .9); padding:16px}
.st8 b{display:block; font-family:var(--display); font-size:30px; font-weight:700; letter-spacing:-.03em;
  line-height:1; color:var(--paper-100)}
.st8 span{display:block; font-family:var(--mono); font-size:10.5px; letter-spacing:.09em;
  text-transform:uppercase; color:var(--paper-300); margin-top:7px}
.tele-roles{display:flex; gap:6px; flex-wrap:wrap; padding:14px 16px; border-top:1px solid var(--rule)}
.rchip{font-family:var(--mono); font-size:10.5px; padding:4px 9px; border-radius:5px;
  border:1px solid rgb(31 111 235 / .32); color:var(--sentinel-300); background:rgb(31 111 235 / .1)}
.tele-log{list-style:none; margin:0; padding:10px 16px 16px; border-top:1px solid var(--rule);
  font-family:var(--mono); font-size:12px; color:var(--paper-300)}
.tele-log li{display:flex; gap:9px; padding:5px 0; opacity:0}
.tele-log b{color:var(--sentinel-300); font-weight:500; min-width:46px}
.tele-log em{font-style:normal; color:var(--muted); margin-left:auto; white-space:nowrap}
@media(max-width:520px){.tele-log em{display:none}}
.dash-copy h2{margin-bottom:18px}

/* ============ 03 · MATRIX — HUD ========================================== */
.sc-matrix .ch-in{display:grid; align-content:end}
.mx-copy{position:absolute; top:clamp(96px,14vh,150px); left:clamp(24px,5vw,80px);
  max-width:min(40ch,36vw)}
/* this headline is a question, so it needs room to stay one sentence */
.sc-matrix h2{font-size:clamp(27px,2.9vw,42px)}
.hud{position:absolute; right:clamp(24px,9vw,172px); top:50%; translate:0 -50%; width:300px;
  border:1px solid var(--border); border-radius:12px; background:rgb(10 13 18 / .95); overflow:hidden; box-shadow:0 30px 70px -36px rgb(0 0 0 / .9)}
@media(max-width:940px){.hud{position:static; translate:none; width:auto; margin-top:26px}
  .mx-copy{position:static; max-width:none}}
.hud-h{padding:11px 15px; border-bottom:1px solid var(--rule); background:rgb(23 29 38 / .7);
  font-family:var(--mono); font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--paper-300)}
.hud-r{display:flex; justify-content:space-between; gap:14px; padding:10px 15px;
  font-family:var(--mono); font-size:11.5px; border-bottom:1px solid var(--rule)}
.hud-r span{color:var(--paper-300)} .hud-r b{color:var(--paper-100); font-weight:500}
.hud-v{padding:12px 15px; font-family:var(--mono); font-size:12px; color:var(--mint-500);
  background:rgb(0 229 160 / .07)}
.hud-v.bad{color:var(--coral-500); background:rgb(255 59 92 / .1)}
.mx-key{position:absolute; left:clamp(24px,5vw,80px); bottom:26px; display:flex; gap:18px;
  flex-wrap:wrap; align-items:center; font-family:var(--mono); font-size:11px; color:var(--paper-300)}
.mx-key span{display:flex; align-items:center; gap:7px}
.mx-key i{width:9px; height:9px; border-radius:2px; display:block}
.k-ok{background:var(--mint-500)} .k-no{background:#3C4759} .k-bad{background:var(--coral-500)}
.mx-key em{font-style:normal; letter-spacing:.16em; text-transform:uppercase; opacity:.7}

/* ============ 04 · EVIDENCE — horizontal case file ======================= */
.sc-evidence .ch-in{display:grid; grid-template-rows:auto 1fr; gap:28px; padding-inline:0}
.ev-head{padding-inline:clamp(24px,5vw,80px); position:relative}
.sc-evidence h2{max-width:20ch}
.ev-hint{display:block; margin-top:14px; font-family:var(--mono);
  font-size:10.5px; letter-spacing:.2em; text-transform:uppercase; color:var(--paper-300)}
@media(max-width:820px){.ev-hint{display:none}}
.ev-rail{overflow:hidden; display:flex; align-items:center}
.ev-track{display:flex; gap:22px; padding-inline:clamp(24px,5vw,80px); will-change:transform}
.ev-card{flex:0 0 min(420px,78vw); border:1px solid var(--border); border-radius:14px;
  background:rgb(10 13 18 / .95); overflow:hidden;
  box-shadow:0 40px 90px -46px rgb(0 0 0 / .95); transition:border-color .25s, transform .25s}
.ev-card:hover{border-color:var(--slate-600); transform:translateY(-4px)}
.ev-card header{display:flex; align-items:center; gap:10px; padding:13px 16px;
  border-bottom:1px solid var(--rule); background:rgb(23 29 38 / .7);
  font-family:var(--mono); font-size:11.5px; color:var(--paper-300)}
.ev-card pre{margin:0; padding:20px 16px; font-family:var(--mono); font-size:12.6px; line-height:1.9;
  color:var(--paper-300); white-space:pre-wrap; min-height:240px}
.ev-card footer{padding:12px 16px; border-top:1px solid var(--rule); font-family:var(--mono);
  font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--paper-300)}
.ev-card.final{border-color:rgb(0 229 160 / .4)}
.ev-card.final footer{color:var(--mint-500)}
.shot-mock{display:flex; flex-direction:column; gap:12px; padding:26px 16px; min-height:240px;
  justify-content:center}
.shot-mock span{height:11px; border-radius:3px; background:#3C4759}
.shot-mock span:nth-child(2){width:74%}
.shot-mock span.hot{width:56%; background:rgb(255 59 92 / .55)}
.shot-mock span:nth-child(4){width:64%}
html:not(.has-xp) .ev-rail{overflow-x:auto}
/* these two wait on a GSAP tween that the fallback never runs, so without
   this the crawl log and the patched lines are invisible on a machine with
   no WebGL (prefers-reduced-motion is already covered at the foot of the file) */
html:not(.has-xp) .tele-log li{opacity:1}
html:not(.has-xp) .ide-code .add{height:auto; opacity:1}
/* stop the cards before the rail, or they slide out from under the labels */
@media(min-width:1101px){html.has-xp .sc-evidence .ev-rail{margin-right:clamp(70px,6vw,112px)}}

/* ============ 05 · PATCH — the editor ==================================== */
.sc-patch .ch-in{display:grid; grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(24px,5vw,64px); align-items:center}
@media(max-width:940px){.sc-patch .ch-in{grid-template-columns:1fr; gap:26px}}
.ide{border:1px solid var(--border); border-radius:14px; background:rgb(8 11 16 / .97); overflow:hidden; box-shadow:0 50px 100px -50px rgb(0 0 0 / .95)}
.ide-bar{display:flex; align-items:center; gap:8px; padding:12px 16px; border-bottom:1px solid var(--rule);
  background:rgb(23 29 38 / .8); font-family:var(--mono); font-size:11.5px; color:var(--muted)}
.ide-bar i{width:9px; height:9px; border-radius:50%; background:#3C4759}
.ide-file{margin-left:8px; color:var(--paper-300)}
.ide-patch{margin-left:auto; font-style:normal; color:var(--data-2)}
.ide-code{margin:0; padding:18px 0; font-family:var(--mono); font-size:12.8px; line-height:2;
  color:var(--paper-300); overflow-x:auto}
.ide-code .l{display:block; padding:0 18px; position:relative; white-space:pre}
.ide-code .l i{display:inline-block; width:34px; color:var(--muted); font-style:normal;
  font-size:11px; user-select:none}
.ide-code .k2{color:var(--sentinel-300)}
.ide-code .c{color:var(--muted)}
.ide-code .del{background:rgb(255 59 92 / .09); box-shadow:inset 2px 0 0 var(--coral-500)}
.ide-code .add{background:rgb(0 229 160 / .09); box-shadow:inset 2px 0 0 var(--mint-500);
  height:0; overflow:hidden; opacity:0}
.ide-code .del.gone{height:0; overflow:hidden; opacity:0; padding-block:0}
.ide-code .add.in{height:auto; opacity:1}
.ide-foot{display:flex; align-items:center; gap:12px; padding:13px 16px; border-top:1px solid var(--rule);
  background:rgb(23 29 38 / .6); font-family:var(--mono); font-size:11px; flex-wrap:wrap}
.ide-tag{padding:3px 9px; border-radius:5px; border:1px solid}
.ide-tag.del{color:var(--coral-500); border-color:rgb(255 59 92 / .4); background:rgb(255 59 92 / .08)}
.ide-tag.add{color:var(--mint-500); border-color:rgb(0 229 160 / .4); background:rgb(0 229 160 / .08)}
.ide-note{color:var(--paper-300)}

/* ============ 06 · CLOSED — the seal ===================================== */
/* minmax(0,1fr), not the default auto: the implicit column was sizing itself
   to its widest child — .doors, which auto-fits four 190px tracks — so on a
   phone the column grew past the viewport and justify-items:center pushed
   every other child off the right edge. This pins the column to the
   container and lets the children shrink inside it. */
.sc-close .ch-in{display:grid; grid-template-columns:minmax(0,1fr);
  align-content:center; justify-items:center; text-align:center; gap:18px}
/* this eyebrow lands on the lit part of the ring, where Sentinel 300 on blue
   is unreadable — so it carries its own ground */
.sc-close .ch-n{margin:0; padding:7px 16px; border-radius:999px; color:var(--paper-100);
  border:1px solid var(--border); background:rgb(11 15 20 / .85)}
.sc-close .ch-n::before{display:none}
.flip{position:relative; display:grid; place-items:center; height:clamp(64px,9vw,120px); margin-bottom:6px}
.flip span{position:absolute; font-family:var(--display); font-weight:700; letter-spacing:-.04em;
  font-size:clamp(40px,6vw,92px); line-height:1; white-space:nowrap}
.flip-a{color:var(--coral-500)}
.flip-b{color:var(--mint-500); opacity:0}
.sc-close h2{max-width:20ch}
.sc-close .lede{margin:0 auto; text-align:center}
.doors{display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:12px;
  margin-top:26px; width:100%; max-width:940px}
.door{display:block; text-align:left; text-decoration:none; padding:15px 17px; border-radius:12px;
  border:1px solid var(--border); background:rgb(8 11 16 / .96);
  transition:border-color .22s, translate .22s cubic-bezier(.16,1,.3,1), background .22s}
.door:hover{border-color:var(--mint-500); background:rgb(8 11 16 / .96); translate:0 -3px}
.door b{display:block; font-family:var(--display); font-size:14.5px; font-weight:600; margin-bottom:3px}
.door span{font-family:var(--mono); font-size:11px; color:var(--paper-300)}

/* ---------- chapter rail + cursor --------------------------------------- */
.rail-x{position:fixed; right:22px; top:50%; translate:0 -50%; z-index:40; display:flex;
  flex-direction:column; gap:13px; align-items:flex-end}
@media(max-width:1100px){.rail-x{display:none}}
html:not(.has-xp) .rail-x{display:none}
/* Without the experience the chapters are ordinary stacked sections, so
   anything the fixed stage positioned absolutely has to come back into
   flow — otherwise chapter 3 lands on top of chapter 4. */
html:not(.has-xp) .mx-copy,
html:not(.has-xp) .hud,
html:not(.has-xp) .mx-key{position:static; translate:none; inset:auto; width:auto;
  max-width:none; margin:0 0 24px}
html:not(.has-xp) .sc-matrix .ch-in{display:block}
html:not(.has-xp) .sc-matrix h2{font-size:clamp(28px,3.6vw,44px)}
html:not(.has-xp) .sc-evidence .ch-in{display:block; padding-inline:28px}
html:not(.has-xp) .ev-head{padding-inline:0; margin-bottom:24px}
html:not(.has-xp) .ev-track{padding-inline:0}
/* two prompts that only make sense next to the 3D scene they refer to */
html:not(.has-xp) .mx-key em,
html:not(.has-xp) .ev-hint{display:none}
.rail-i{display:flex; align-items:center; gap:9px; text-decoration:none; font-family:var(--mono);
  font-size:9px; letter-spacing:.18em; text-transform:uppercase; color:var(--muted); transition:color .3s}
.rail-i em{font-style:normal; opacity:0; translate:8px 0; transition:opacity .3s, translate .3s}
.rail-i b{display:block; width:20px; height:2px; border-radius:2px; background:#3C4759;
  transition:background .3s, width .3s cubic-bezier(.16,1,.3,1)}
.rail-i.on{color:var(--paper-100)}
.rail-i.on em{opacity:1; translate:0 0}
.rail-i.on b{width:36px; background:var(--grad)}
.rail-i:last-child.on b{background:linear-gradient(90deg,var(--mint-700),var(--mint-500))}

/* The pointer is the mark. .cur-r is only a transform anchor — xp.js writes the
   lagged position onto it, and the ring inside turns on its own, because the
   two cannot share one transform property. */


/* with no motion the flip has nothing to flip, so show the change inline */
html:not(.has-xp) .flip{height:auto; display:flex; gap:16px; align-items:baseline; justify-content:center}
html:not(.has-xp) .flip span{position:static; opacity:1}
html:not(.has-xp) .flip-a{opacity:.55; text-decoration:line-through}
html:not(.has-xp) .flip-b::before{content:"\2192"; margin-right:16px; color:var(--muted)}

@media(prefers-reduced-motion:reduce){
  .mk .mk-scan,.mk .mk-shot,.mk .mk-patch,.mk .mk-seal,
  .an.lit .mk-scan,.an.lit .mk-shot,.an.lit .mk-patch,.an.lit .mk-seal{animation:none}
  .mk .mk-seal{stroke-dashoffset:0}
  .mega .w,.tele-log li,.flip-b{opacity:1!important; transform:none!important}
  .ide-code .add{height:auto; opacity:1}
}

/* ======================================================================
   After the experience
   The six chapters are fixed to the viewport, so anything that follows has
   to be lifted over the canvas the way the footer is — otherwise the scene
   shows through it. Everything here is an ordinary document again: the
   motion is the site's own .reveal, nothing is pinned, nothing hijacks the
   scroll. After six chapters of it, that restraint is the point.
   ====================================================================== */
.after-xp{position:relative; z-index:6; background:var(--ink-900)}
.after-xp .fact{background:var(--ink-900)}
/* the second line of a stat label — sentence case under the mono caps */
.after-xp .fact span em{display:block; font-style:normal; letter-spacing:.04em;
  text-transform:none; color:var(--slate); margin-top:3px}
.after-xp .card .kicker{margin-bottom:10px}
.after-xp .card h3{margin-bottom:8px}

/* ---------- what is ready ---------------------------------------------
   A status list rather than a feature grid: the status is the message, so
   it leads the row and the eye can run down the column of chips alone. */
.rdy{margin-top:34px; border:1px solid var(--border); border-radius:14px; overflow:hidden}
.rdy-row{display:grid; grid-template-columns:86px minmax(0,1fr) minmax(0,1.5fr);
  gap:18px; align-items:baseline; padding:15px 20px; border-bottom:1px solid var(--border)}
.rdy-row:last-child{border-bottom:0}
.rdy-row:nth-child(odd){background:rgb(255 255 255 / .015)}
.rdy-row b{font-weight:600; color:var(--paper-100)}
.rdy-row em{font-style:normal; color:var(--slate); font-size:14.5px; line-height:1.5}
.rdy-row .chip{justify-self:start; white-space:nowrap}
/* the chip is the one thing that arrives, so it gets the small flare */
.js-motion .rdy-row .chip{opacity:0; scale:.9}
.js-motion .rdy-row.in .chip{animation:rdy-chip .5s cubic-bezier(.16,1,.3,1) forwards}
@keyframes rdy-chip{to{opacity:1; scale:1}}
.rdy-note{margin-top:20px; color:var(--slate); font-size:15px}
.rdy-note a{color:var(--sentinel-300)}

@media(max-width:760px){
  /* the description drops under the label rather than fighting it for width */
  .rdy-row{grid-template-columns:78px minmax(0,1fr); gap:6px 14px; padding:14px 16px}
  .rdy-row em{grid-column:2}
}
@media(prefers-reduced-motion:reduce){
  .js-motion .rdy-row .chip{opacity:1; scale:1; animation:none}
}
/* past the last chapter the rail has nothing left to point at */
html.xp-past .rail-x{opacity:0; pointer-events:none}
.rail-x{transition:opacity .45s ease}
