
:root{
  --ground:#fafaf9; --void:#ffffff; --panel:rgba(0,0,0,.025); --panel2:rgba(0,0,0,.05);
  --line:rgba(0,0,0,.12); --line2:rgba(0,0,0,.22);
  --ink:#0a0a0b; --mute:#5f5e63; --dim:#8b8a90;
  --acc:#0a0a0b; --on-acc:#fafaf9;
  --scan:rgba(0,0,0,.022);
  --page-art:url('/art/page-reaper.png');
  color-scheme:light;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --ground:#0a0a0b; --void:#000000; --panel:rgba(255,255,255,.035); --panel2:rgba(255,255,255,.06);
    --line:rgba(255,255,255,.10); --line2:rgba(255,255,255,.18);
    --ink:#f5f4f2; --mute:#8d8c94; --dim:#5b5a62;
    --acc:#ffffff; --on-acc:#000000;
    --scan:rgba(255,255,255,.018);
    color-scheme:dark;
  }
}
:root[data-theme="dark"]{
  --ground:#0a0a0b; --void:#000000; --panel:rgba(255,255,255,.035); --panel2:rgba(255,255,255,.06);
  --line:rgba(255,255,255,.10); --line2:rgba(255,255,255,.18);
  --ink:#f5f4f2; --mute:#8d8c94; --dim:#5b5a62;
  --acc:#ffffff; --on-acc:#000000;
  --scan:rgba(255,255,255,.018);
  color-scheme:dark;
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{margin:0;background:var(--ground);color:var(--ink);font:15px/1.5 "Archivo",system-ui,sans-serif;min-height:100vh;position:relative}
body::before{content:"";position:fixed;inset:0;pointer-events:none;z-index:0;
  background:repeating-linear-gradient(0deg,var(--scan) 0 1px,transparent 1px 3px)}
/* ── the page's own reaper, behind the entire console ────────────────────────
   🔴 The art is white ink on black, so it can only be screened onto a DARK
   ground. In light mode that would paint a white ghost onto white. The light
   branch therefore inverts the image and multiplies it instead, which turns the
   same white ink into dark ink on a pale ground — one asset, both themes, no
   second file. Kept far quieter than the hero's layers: this is atmosphere the
   eye should find on the second look, not a picture competing with the console. */
body::after{content:"";position:fixed;inset:0;pointer-events:none;z-index:0;
  background:var(--page-art) no-repeat center/cover;
  opacity:.07;filter:invert(1);mix-blend-mode:multiply}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) body::after{opacity:.13;filter:none;mix-blend-mode:screen}
}
:root[data-theme="dark"] body::after{opacity:.13;filter:none;mix-blend-mode:screen}
a{color:inherit;text-decoration:none}
a:hover{opacity:.72}
:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

.mono,.lbl,.chip,.btn,.stat,.brand small,.nav a,.readout .k,code,pre,.kv th,.kv td{
  font-family:"IBM Plex Mono",ui-monospace,Menlo,monospace}
.lbl{font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--mute)}
.disp{font-weight:900;font-style:italic;letter-spacing:-.02em;line-height:.95;text-transform:uppercase}
.wrap{position:relative;z-index:1;max-width:1360px;margin:0 auto;padding:0 20px}

/* top bar — 56 */
.top{height:56px;display:flex;align-items:center;gap:22px;border-bottom:1px solid var(--line)}
.brand{display:flex;align-items:center;gap:10px;font-weight:900;font-style:italic;letter-spacing:-.02em;font-size:17px;text-transform:uppercase}
/* The mark is the generated ink-wash icon, not a drawn glyph — same artwork the
   token carries, so the header and a wallet list show the same thing. It keeps
   its own black ground in both themes (a token icon is composited onto whatever
   a wallet feels like, so the ground is part of the mark) and fills the whole
   26px box, because it was measured to hold at exactly that size and giving it
   inner padding would drop it below where the eye slits survive. */
.brand .mark{width:26px;height:26px;display:block;flex:none;
  border:1px solid var(--line2);object-fit:cover}
.brand small{font-weight:500;font-style:normal;font-size:9.5px;letter-spacing:.14em;color:var(--mute);
  border:1px solid var(--line2);padding:1px 6px;margin-left:4px}
.nav{display:flex;gap:18px;margin-left:8px}
.nav a{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--mute);padding:6px 0;border-bottom:1px solid transparent}
.nav a:hover,.nav a.on{color:var(--ink);border-bottom-color:var(--acc);opacity:1}
.top .right{margin-left:auto;display:flex;align-items:center;gap:10px}
.btn{display:inline-flex;align-items:center;gap:8px;height:34px;padding:0 14px;border:1px solid var(--line2);
  font-size:11px;letter-spacing:.14em;text-transform:uppercase;font-weight:600;background:var(--panel);
  white-space:nowrap;color:var(--ink);cursor:pointer}
.btn:hover{border-color:var(--acc);opacity:1}
.btn.acc{background:var(--acc);border-color:var(--acc);color:var(--on-acc)}
.btn.acc:hover{opacity:.85}
.theme{width:34px;padding:0;justify-content:center;font-size:13px}

/* stage — viewport 8 / rail 4, rows equal */
.stage{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:14px;align-items:stretch;padding:14px 0}
.vp{grid-column:span 8;min-width:0}
.rail{grid-column:span 4;min-width:0;display:flex;flex-direction:column;gap:14px}
/* 🔴 The last rail instrument absorbs the leftover height. Without this the rail
   ends short of the viewport and leaves dead ground beside it, which is the first
   thing the eye reads as un-designed — HUD rule 2, rows are equal height. */
.rail>:last-child{flex:1}
.full{grid-column:span 12;min-width:0}

/* one bezel per instrument, with corner ticks */
.ins{position:relative;border:1px solid var(--line);background:var(--panel);min-width:0;display:flex;flex-direction:column}
.ins::before,.ins::after{content:"";position:absolute;width:10px;height:10px;pointer-events:none;border-color:var(--line2);border-style:solid}
.ins::before{left:-1px;top:-1px;border-width:1px 0 0 1px}
.ins::after{right:-1px;bottom:-1px;border-width:0 1px 1px 0}
.ins>.hd{display:flex;align-items:center;gap:10px;min-height:38px;padding:0 14px;border-bottom:1px solid var(--line)}
.ins>.hd .sp{margin-left:auto}
.ins>.bd{padding:16px 18px;flex:1;min-height:0;display:flex;flex-direction:column;gap:14px}

/* display */
.hero h1{font-size:clamp(34px,5.2vw,68px);margin:4px 0 0}
.lede{color:var(--mute);max-width:58ch;font-size:15px;margin:0}
.lede b{color:var(--ink);font-weight:600}
/* ── the cinema: a live display INSIDE the viewport's bezel ──────────────────
   Four stacked layers, back to front: void → canvas (ash, 3 depth bands) →
   art plate → slash sweep → type. Depth comes from parallax between the bands
   and the plate, driven by pointer, not scroll — the display never leaves the
   frame, so scroll-linked motion would be motion with nothing to tie it to. */
/* 🔴 THE DISPLAY DOES NOT THEME. Its interior is pure black in both light and
   dark, and its type is always light, for two reasons that point the same way:
   a screen is black, which is the honest thing for an instrument's viewport —
   and the art is keyed with mix-blend-mode:screen, which needs a black ground.
   On a light ground screen-blend blows the whole frame out to white. So the
   chrome themes and the display does not, exactly like a real console. */
.cine{position:relative;width:100%;aspect-ratio:16/9;min-height:220px;max-height:min(58vh,520px);
  border:1px solid var(--line2);background:#000;color:#f5f4f2;overflow:hidden;isolation:isolate;
  display:grid;place-items:center}
.cine canvas{position:absolute;inset:0;width:100%;height:100%;display:block}
/* ── three art depths, each moving at its own rate ──────────────────────────
   bg   the scythe arc — furthest, quietest, sets the frame
   mid  a second reaper, off-centre and dim — the layer that sells the depth,
        because parallax needs something BETWEEN the backdrop and the subject
   plate the subject — nearest, moves most
   Each is feathered, because none of them can carry an alpha channel. */
.cine .bg{position:absolute;inset:-4%;background-size:cover;background-position:center;
  opacity:.34;mix-blend-mode:screen;will-change:transform}
.cine .mid{position:absolute;top:-14%;bottom:-14%;right:-6%;width:62%;
  background-size:contain;background-position:right center;background-repeat:no-repeat;
  opacity:.30;mix-blend-mode:screen;will-change:transform;
  -webkit-mask-image:radial-gradient(ellipse 58% 66% at 62% 50%,#000 30%,transparent 84%);
  mask-image:radial-gradient(ellipse 58% 66% at 62% 50%,#000 30%,transparent 84%)}
@media (max-width:900px){.cine .mid{display:none}}
.cine .plate{position:absolute;inset:0;display:grid;place-items:center;will-change:transform}
/* screen-blend keys the art's flat black ground away without an alpha channel —
   which matters because the generator cannot output transparency at all. */
/* 🔴 Feather the plate's edges. The generator cannot emit transparency, so every
   figure arrives as a rectangle of flat black — and screen-blend only hides that
   rectangle while its black is EXACTLY #000. JPEG ringing, a vignette, any lift
   at all, and the box edge shows as a seam. A soft mask kills it for whatever art
   lands here later, instead of re-crushing levels once per image. */
.cine .plate img,.cine .plate video{max-width:min(46%,340px);max-height:92%;object-fit:contain;mix-blend-mode:screen;
  -webkit-mask-image:radial-gradient(ellipse 60% 68% at 50% 48%,#000 38%,transparent 88%);
  mask-image:radial-gradient(ellipse 60% 68% at 50% 48%,#000 38%,transparent 88%)}
/* Placeholder while the art is being drawn — it is a framed void, not a grey box
   with the word "image" in it, because an empty state is still a design. */
.cine .plate .void{width:min(46%,300px);aspect-ratio:3/4;border:1px solid rgba(255,255,255,.18);
  display:grid;place-items:center;color:#5b5a62;font-family:"IBM Plex Mono",monospace;
  font-size:10px;letter-spacing:.2em;text-transform:uppercase;text-align:center;padding:12px}
/* the slash — one diagonal cut of light across the plate, on load and on hover */
.cine .slash{position:absolute;top:-30%;bottom:-30%;width:14%;left:-25%;
  background:linear-gradient(90deg,transparent,var(--acc),transparent);opacity:.22;
  transform:rotate(18deg);filter:blur(6px);animation:slash 2.6s cubic-bezier(.2,.7,.2,1) .35s both}
.cine:hover .slash{animation:slash 1.1s cubic-bezier(.2,.7,.2,1) both}
@keyframes slash{0%{left:-25%;opacity:0}12%{opacity:.34}100%{left:118%;opacity:0}}
/* vignette keeps the type legible over whatever the art turns out to be */
.cine .vig{position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(120% 80% at 50% 42%,transparent 34%,#000 100%);opacity:.8}
/* The strapline sits ABOVE the wordmark, not beside it. Beside it, the italic's
   overhang runs into the mono and the two fight over the same baseline. */
.cine .cap{position:absolute;left:0;right:0;bottom:0;padding:18px;display:flex;
  flex-direction:column;align-items:flex-start;gap:6px}
.cine .cap h1{margin:0;font-size:clamp(30px,5vw,62px);color:#f5f4f2;
  text-shadow:0 2px 24px rgba(0,0,0,.9)}
.cine .cap .sub{font-family:"IBM Plex Mono",monospace;font-size:10.5px;letter-spacing:.18em;
  text-transform:uppercase;color:#a9a8ae}

/* Entrance runs ON LOAD and ends at the resting state — never parked at opacity 0
   waiting for an observer, because that first still frame is what a share card,
   a thumbnail and a skimming reader all get. */
.rise{animation:rise .9s cubic-bezier(.16,1,.3,1) both}
.rise.d1{animation-delay:.10s}.rise.d2{animation-delay:.20s}.rise.d3{animation-delay:.30s}
@keyframes rise{from{opacity:0;transform:translate3d(0,14px,0)}to{opacity:1;transform:none}}

@media (prefers-reduced-motion:reduce){
  .cine .slash{display:none}
  .rise{animation:none}
}

/* readouts — dials in a row inside ONE bezel, hairlines between */
/* Four dials sit 2×2, not 3-then-1 — an auto-fit row that wraps leaves a ragged
   gap where the fourth should be, and a readings panel with a hole in it stops
   reading as one instrument. */
.dials{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}
.readout{padding:12px 14px;min-width:0;border-right:1px solid var(--line);border-bottom:1px solid var(--line)}
.readout:nth-child(2n){border-right:0}
.readout:nth-last-child(-n+2){border-bottom:0}
.readout .k{font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mute);display:block}
.readout .v{font-family:"IBM Plex Mono",monospace;font-size:17px;font-weight:600;margin-top:3px;word-break:break-word}

/* tables */
.tw{overflow-x:auto}
.kv{width:100%;border-collapse:collapse;table-layout:fixed}
.kv th{font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--mute);text-align:left;
  padding:9px 0;font-weight:500;border-bottom:1px solid var(--line);vertical-align:top}
.kv td{padding:9px 0;border-bottom:1px solid var(--line);font-size:12.5px;vertical-align:top;color:var(--ink);text-align:right}
.kv tr:last-child th,.kv tr:last-child td{border-bottom:0}

pre{margin:0;background:var(--void);border:1px solid var(--line);padding:14px 16px;font-size:12.5px;
  line-height:1.6;overflow-x:auto;color:var(--ink)}

.steps{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:14px}
.steps li{display:grid;grid-template-columns:28px 1fr;gap:12px;align-items:start}
.steps .n{font-family:"IBM Plex Mono",monospace;font-size:11px;letter-spacing:.12em;color:var(--mute);padding-top:3px}
.steps p{margin:0;font-size:13.5px;color:var(--mute)}
.steps b{color:var(--ink);font-weight:600}

/* ── the card ───────────────────────────────────────────────────────────────
   Back showing, front hidden, and a slow rotation on hover. Three nested
   elements rather than one, because the tilt and the flip are different jobs on
   the same axis: the outer holds the perspective, the middle takes the pointer
   tilt from script, the inner takes the flip from CSS. Put both on one element
   and whichever writes last wins — the card either tilts or flips, never both. */
.card{perspective:1400px;width:100%;max-width:300px;aspect-ratio:5/7;margin-inline:auto}
.card-tilt{width:100%;height:100%;transform-style:preserve-3d;will-change:transform}
.card3d{position:relative;width:100%;height:100%;transform-style:preserve-3d;
  transition:transform 1.5s cubic-bezier(.2,.7,.2,1)}
.card:hover .card3d,.card:focus-within .card3d{transform:rotateY(180deg)}
.card .face{position:absolute;inset:0;background-size:cover;background-position:center;
  border:1px solid var(--line2);
  -webkit-backface-visibility:hidden;backface-visibility:hidden;
  box-shadow:0 18px 40px rgba(0,0,0,.55)}
.card .face.front{transform:rotateY(180deg);background:#000}
/* ── the unrevealed front: black with drifting dots ─────────────────────────
   Not an image. Two tiled gradient fields at different sizes and speeds, which
   gives parallax inside a 300px card for a few hundred bytes and no request.
   🔴 The loop is seamless because each layer scrolls its background by EXACTLY
   one tile height — 220px and 160px. Any other distance and the field visibly
   jumps once per cycle, which is the tell that it is a loop rather than a drift. */
.card .dots{position:absolute;inset:0;background-repeat:repeat;pointer-events:none}
.card .dots.far{
  background-size:220px 220px;opacity:.45;animation:drift-far 26s linear infinite;
  background-image:
    radial-gradient(circle 1.2px at 30px 40px,#f5f4f2 100%,transparent 0),
    radial-gradient(circle 1px   at 152px 92px,#f5f4f2 100%,transparent 0),
    radial-gradient(circle 1.4px at 82px 168px,#f5f4f2 100%,transparent 0),
    radial-gradient(circle 1px   at 196px 202px,#f5f4f2 100%,transparent 0),
    radial-gradient(circle 1.1px at 112px 26px,#f5f4f2 100%,transparent 0),
    radial-gradient(circle 1px   at 12px 130px,#f5f4f2 100%,transparent 0)}
.card .dots.near{
  background-size:160px 160px;opacity:.85;animation:drift-near 15s linear infinite;
  background-image:
    radial-gradient(circle 2px   at 40px 118px,#f5f4f2 100%,transparent 0),
    radial-gradient(circle 1.6px at 122px 28px,#f5f4f2 100%,transparent 0),
    radial-gradient(circle 2.4px at 92px 96px,#f5f4f2 100%,transparent 0),
    radial-gradient(circle 1.5px at 18px 58px,#f5f4f2 100%,transparent 0)}
@keyframes drift-far{from{background-position:0 0}to{background-position:0 -220px}}
@keyframes drift-near{from{background-position:0 0}to{background-position:0 -160px}}
/* a sheen that travels as the card turns — reads as a surface catching light */
.card .face::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(115deg,transparent 34%,rgba(255,255,255,.16) 48%,transparent 62%);
  opacity:0;transition:opacity .5s}
.card:hover .face::after{opacity:1}
.card button{all:unset;display:block;width:100%;height:100%;cursor:pointer}
/* 🔴 The flip is a deliberate interaction, not ambience, so reduced motion keeps
   it but takes the travel out — it snaps instead of turning, and the tilt stops. */
@media (prefers-reduced-motion:reduce){
  .card3d{transition:none}
  .card .face::after{display:none}
  .card .dots{animation:none}
}

/* ── the walkthrough ────────────────────────────────────────────────────────
   🔴 Built to be SCANNED, not read. Three short columns of fragments — nobody
   works through a paragraph to find out whether they need a wallet. Every line
   is one thing, under about six words, and the type is set so the three columns
   can be taken in at a glance rather than in sequence. */
.prose{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:22px 30px}
.prose section{min-width:0}
.prose h3{margin:0 0 10px;font-family:"IBM Plex Mono",monospace;font-size:10px;
  letter-spacing:.16em;text-transform:uppercase;color:var(--mute);font-weight:500}
.prose ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
/* 🔴 Scoped to ul on purpose. Unscoped, this rule's ::before injected a THIRD
   child into the numbered steps' two-column grid, pushing their text into the
   12px track — every step wrapped one word per line. The steps are an ol; only
   the plain lists get a bullet. */
.prose ul li{display:grid;grid-template-columns:12px 1fr;gap:9px;font-size:14px;
  line-height:1.4;color:var(--ink)}
.prose ul li::before{content:"·";color:var(--dim);font-family:"IBM Plex Mono",monospace}
/* the "don't need" column reads as crossed off, not as another list of chores */
.prose .skip li{color:var(--dim)}
.prose .skip li::before{content:"×"}
.prose .steps{gap:10px}
.prose .steps p{font-size:14px;line-height:1.4;color:var(--ink)}

.note{border-left:2px solid var(--line2);padding:2px 0 2px 12px;color:var(--mute);font-size:13px}
.chip{display:inline-flex;align-items:center;height:22px;padding:0 8px;border:1px solid var(--line2);
  font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mute)}

.foot{display:flex;flex-wrap:wrap;gap:16px;align-items:center;padding:16px 0 28px;border-top:1px solid var(--line);
  color:var(--dim);font-family:"IBM Plex Mono",monospace;font-size:10.5px;letter-spacing:.12em;text-transform:uppercase}
.foot .sp{margin-left:auto}

/* 🔴 phone: the stage collapses to one column and the nav hides. Measured at 390 —
   a 12-col grid that keeps 8/4 on a phone is what pushes scrollWidth past the viewport. */
@media (max-width:900px){
  .vp,.rail,.full{grid-column:span 12}
  .nav{display:none}
}
