:root{
  --bg:#050508;
  --bg-surface:#0a0a12;
  --text:#C4C4D8;             /* was #a0a0b8 */
  --text-dim:#8B8BA8;         /* was #404058 */
  --text-bright:#EDEDF5;      /* was #c8c8d4 */
  --accent:#6060cc;
  --accent-glow:#8a7fbf;      /* signal / near-line highlight */
  --glitch-r:#ff2020;
  --glitch-c:#00ffff;
  --border:#34344E;           /* was #1a1a2e */
  --border-mid:#454568;       /* was #252540; stays a step above --border */
  --neo:#00d0d0;              /* neocities subnet */
  --straw:#e0303c;            /* straw.page subnet */
  --wire-op:0.24;             /* resting mesh opacity */
}

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}

.wired{
  position:fixed;inset:0;
  background:var(--bg);color:var(--text);
  font-family:'Share Tech Mono','VT323',monospace;font-size:14px;
  overflow:hidden;cursor:none;
  -webkit-font-smoothing:none;user-select:none;
}
.wired a,.wired button{cursor:none}

@view-transition{navigation:auto}
::view-transition-old(root){animation-duration:.08s}
::view-transition-new(root){animation-duration:.16s}

.wired::after{
  content:'';position:absolute;inset:0;pointer-events:none;z-index:9996;
  background:radial-gradient(ellipse 120% 95% at 50% 42%,transparent 55%,rgba(0,0,0,.30) 100%);
}

.wired::before{
  content:'';position:absolute;inset:0;
  background:repeating-linear-gradient(to bottom,transparent 0px,transparent 2px,rgba(0,0,0,.07) 2px,rgba(0,0,0,.07) 4px);
  pointer-events:none;z-index:9998;
}
.wired.flickering{animation:flicker .15s steps(1) forwards}

.grain{position:absolute;inset:0;pointer-events:none;z-index:9997;opacity:.038;mix-blend-mode:screen}

.wcur{position:fixed;width:12px;height:12px;border:1px solid var(--accent-glow);pointer-events:none;z-index:10000;transform:translate(-50%,-50%);transition:transform .05s,border-color .15s;mix-blend-mode:difference}
.wcur::after{content:'';position:absolute;inset:3px;background:var(--accent-glow);opacity:.5}
.wcur.hover{transform:translate(-50%,-50%) scale(1.8);border-color:var(--glitch-r)}
.wcur.hover::after{background:var(--glitch-r)}
.wcur.click{transform:translate(-50%,-50%) scale(.7)}

nav{position:absolute;top:0;left:0;right:0;padding:1.6rem 2.4rem;display:flex;align-items:center;gap:2.4rem;z-index:1000;font-size:12px;letter-spacing:.12em;background:linear-gradient(to bottom,var(--bg) 0%,transparent 100%)}
nav .nav-brand{color:var(--text-dim);margin-right:auto;font-size:11px;letter-spacing:.2em}
nav .nav-pages{display:flex;align-items:center;gap:2.4rem;min-width:0}
nav .nav-jump{display:none;background:none;border:none;font:inherit;letter-spacing:inherit;color:var(--accent-glow);padding:.2rem 0;transition:color .1s,text-shadow .15s}
nav .nav-jump:hover{color:var(--text-bright);text-shadow:0 0 8px var(--accent-glow)}
nav a{color:var(--text-dim);text-decoration:none;transition:color .1s,text-shadow .15s;position:relative}
nav a.active{color:var(--text)}
nav a.active::before{content:'> ';color:var(--accent)}
nav a:hover{color:var(--text-bright);text-shadow:0 0 8px var(--accent-glow)}

.layer-badge{position:absolute;top:1.6rem;right:2rem;font-size:10px;letter-spacing:.18em;color:var(--text-dim);opacity:.45;pointer-events:none;z-index:200}

/* ── stage ── the fixed viewport onto an unbounded plane ──
   .stage never moves. Everything spatial lives in .world, which
   takes a single translate+scale; the grid below is a repeating
   background whose position/size JS syncs to that same transform,
   so it tiles forever at zero cost. */
.stage{
  position:absolute;inset:0;z-index:10;touch-action:none;
  background-image:
    linear-gradient(to right, rgba(96,96,204,.055) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(96,96,204,.055) 1px, transparent 1px),
    linear-gradient(to right, rgba(96,96,204,.11) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(96,96,204,.11) 1px, transparent 1px);
}
.stage.panning{cursor:none}

.world{position:absolute;top:0;left:0;width:100%;height:100%;transform-origin:0 0;will-change:transform}
/* no transition while the pointer drives it — only on programmatic recenter */
.world.eased{transition:transform .5s cubic-bezier(.22,.61,.36,1)}

/* ── procedurally generated dead space ────────────────
   Chunks of derelict hosts spawned as you pan into empty
   regions and dropped once they fall far enough behind.
   Inert by design: never mesh members, never simulated,
   never hit-tested. */
.chunk{position:absolute;pointer-events:none;z-index:8}
.chunk svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.chunk .stub{stroke:var(--border);stroke-width:1;opacity:.5;stroke-dasharray:2 5}

.dead{
  position:absolute;z-index:3;width:126px;padding:5px 6px;
  border:1px dashed rgba(90,90,132,.62);background:rgba(7,7,13,.62);
  font-size:8px;letter-spacing:.07em;line-height:1.5;color:var(--text-dim);
  opacity:.72;pointer-events:none;
}
.dead .dname{color:#8f8fb0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dead .dip{opacity:.72;font-size:7px}
.dead .dstat{font-size:7px;letter-spacing:.11em;color:#8a5a70}
.dead.rst .dstat{color:#a8484f}
.dead.decay{opacity:.42;border-style:dotted}
.dead::after{
  content:'';position:absolute;inset:0;
  background:repeating-linear-gradient(to bottom,transparent 0 3px,rgba(0,0,0,.16) 3px 4px);
}

/* ── broken hosts ─────────────────────────────────────
   Not "switched off" — physically come apart. The clip
   paths take bites out of the box *and* its border, so
   the frame ends mid-run the way a snapped panel does.
   Solid border, because a dashed one reads as tidy. */
.dead.broken{
  border-style:solid;border-color:rgba(110,104,126,.5);
  background:rgba(9,8,12,.72);opacity:.62;
}
.dead.broken .dname{color:#7d7d97}
.dead.broken .dstat{color:#6f5560}
.dead.brk1{clip-path:polygon(0 0,100% 0,100% 44%,86% 50%,100% 58%,100% 100%,32% 100%,26% 76%,0 68%)}
.dead.brk2{clip-path:polygon(0 0,58% 0,64% 15%,100% 12%,100% 100%,20% 100%,12% 70%,0 62%)}
.dead.brk3{clip-path:polygon(7% 0,100% 0,94% 30%,100% 40%,100% 100%,0 100%,0 26%)}
/* bottom-right is the one corner all three variants leave standing, so the
   surviving LED goes there rather than getting clipped away with the rest */
.dead.broken .led{top:auto;bottom:6px}

/* the one LED still going. period + phase are randomised per host in JS —
   in lockstep this reads as decoration, out of step it reads as a lot of
   separate machines quietly failing */
.dead .led{
  position:absolute;right:6px;top:6px;width:4px;height:4px;
  background:#ff3a2a;box-shadow:0 0 5px #ff3a2a;opacity:.06;
  animation:led-dying 6s steps(1,end) infinite;
}
@keyframes led-dying{
  0%,100%{opacity:.06}
  1.5%{opacity:1}
  3%{opacity:.08}
  4.5%{opacity:.8}
  6.5%{opacity:.06}
}

/* ── botnet ───────────────────────────────────────────
   Toxic green, kept clear of the reds (drops/glitch) and
   the amber (routers) so infection reads as its own
   thing rather than "another kind of broken". */
.dead.rogue{
  border-style:solid;border-color:rgba(127,220,58,.5);
  background:rgba(9,15,6,.82);opacity:.92;
}
.dead.rogue .dname{color:#a6e86a}
.dead.rogue .dip{color:#6f9152}
.dead.rogue .dstat{color:#7fdc3a;opacity:.9}
.dead.rogue .led{background:#7fdc3a;box-shadow:0 0 6px #7fdc3a}
/* The beacon glow. Held on a pseudo-element and faded by opacity rather than
   animating box-shadow directly — a hostile chunk can hold two dozen of these
   and an animated box-shadow repaints every one of them every frame. */
.dead.rogue::before{
  content:'';position:absolute;inset:-1px;pointer-events:none;
  box-shadow:0 0 15px rgba(127,220,58,.45);
  opacity:0;animation:rogue-pulse 2.8s ease-in-out infinite;
}
@keyframes rogue-pulse{0%,100%{opacity:0}50%{opacity:1}}
.dead.infected{border-color:rgba(127,220,58,.3);opacity:.68}
.dead.infected .dname{color:#8fbf72}
.dead.infected .dstat{color:#7fdc3a;opacity:.7}

/* C2 traffic crawling from a rogue into whatever it has got hold of */
.chunk .corrupt{
  stroke:#7fdc3a;stroke-width:1.4;opacity:.42;stroke-dasharray:3 5;
  animation:corrupt-crawl 1.1s linear infinite;
}
@keyframes corrupt-crawl{to{stroke-dashoffset:-16}}

/* an empty perimeter where a shared host used to be */
.dcluster{
  position:absolute;z-index:0;pointer-events:none;
  border:1px dashed rgba(88,88,118,.3);background:rgba(8,8,14,.26);
}
.dcluster .dc-cap{
  position:absolute;left:3px;top:-13px;white-space:nowrap;
  font-size:8px;letter-spacing:.13em;color:var(--text-dim);opacity:.45;
}

/* ── private nets ─────────────────────────────────────
   The grey monoliths of the middle ring. Industrial and
   deliberately unreadable: a slab, a hazard stripe, rack
   rows you can half make out, and smoke over the lot. */
.pblock{
  position:absolute;z-index:1;pointer-events:none;overflow:hidden;
  border:1px solid rgba(148,148,170,.28);
  background:
    repeating-linear-gradient(90deg,rgba(255,255,255,.018) 0 2px,transparent 2px 9px),
    linear-gradient(158deg,rgba(28,28,36,.94),rgba(13,13,19,.96));
  box-shadow:inset 0 0 46px rgba(0,0,0,.85),0 0 24px rgba(0,0,0,.5);
}
.pblock .haz{
  height:5px;opacity:.5;
  background:repeating-linear-gradient(45deg,#40401f 0 6px,#15150f 6px 12px);
}
.pblock .rack{
  position:absolute;left:9%;height:3px;
  background:rgba(150,150,175,.14);border-left:2px solid rgba(150,150,175,.3);
}
.pblock .pb-meta{position:absolute;left:9px;bottom:8px;line-height:1.6}
.pblock .pb-org{font-size:10px;letter-spacing:.14em;color:#b6b6c8;opacity:.72}
.pblock .pb-as{font-size:8px;letter-spacing:.1em;color:#8a8aa0;opacity:.6}
.pblock .pb-stat{font-size:8px;letter-spacing:.16em;color:#9a8a6a;opacity:.62}
.pblock.island{border-color:rgba(148,148,170,.16)}
.pblock.island .pb-stat{color:#8a6a6a}

/* Smoke. Layered gradients rather than filter:blur — a blur pass over a
   700px box, several on screen at once, is exactly the per-frame cost this
   whole layer exists to avoid. Two sheets drifting against each other at
   different rates so it never reads as one sliding texture.
   Sibling of .pblock, not a child: see fillPrivate. */
.pfog{
  position:absolute;z-index:4;pointer-events:none;
  background:
    radial-gradient(ellipse 42% 38% at 30% 36%,rgba(150,152,168,.34),transparent 68%),
    radial-gradient(ellipse 46% 42% at 70% 62%,rgba(128,130,150,.3),transparent 70%),
    radial-gradient(ellipse 40% 34% at 55% 26%,rgba(140,142,160,.24),transparent 66%),
    radial-gradient(ellipse 62% 54% at 48% 52%,rgba(104,106,126,.26),transparent 74%);
  animation:fog-drift 38s ease-in-out infinite alternate;
}
.pfog::before{
  content:'';position:absolute;inset:0;
  background:
    radial-gradient(ellipse 38% 34% at 66% 32%,rgba(158,160,178,.26),transparent 66%),
    radial-gradient(ellipse 44% 38% at 32% 68%,rgba(120,122,142,.24),transparent 70%);
  animation:fog-drift2 52s ease-in-out infinite alternate;
}
@keyframes fog-drift{
  from{transform:translate(-14px,6px) scale(1)}
  to{transform:translate(16px,-8px) scale(1.1)}
}
@keyframes fog-drift2{
  from{transform:translate(18px,-9px) scale(1.07)}
  to{transform:translate(-16px,10px) scale(1)}
}

/* edge routers hanging off the slab — amber, matching .node.router */
.pgw{
  position:absolute;z-index:2;width:24px;height:14px;pointer-events:none;
  border:1px solid rgba(240,180,60,.4);background:rgba(20,15,8,.92);
}
.pgw::after{
  content:'';position:absolute;left:3px;top:4px;width:3px;height:3px;
  background:#f0b43c;opacity:.5;box-shadow:0 0 4px #f0b43c;
}
.chunk .pstub{stroke:rgba(240,180,60,.28);stroke-width:1.4}
/* peering between private nets: the heaviest cable out here */
.chunk .ptrunk{stroke:rgba(196,178,142,.26);stroke-width:2.6}

/* archived capture frames the far-field chunks occasionally replay */
.relic{
  position:absolute;transform:translate(-50%,-50%);
  white-space:pre;font-size:12px;line-height:1.05;
  color:#a8484f;text-shadow:0 0 14px rgba(168,72,79,.55),0 0 3px rgba(168,72,79,.8);
  pointer-events:none;user-select:none;
}

/* top-left, clear of the nav above and the routing HUD at the bottom */
.view-ctl{position:absolute;left:2rem;top:5.2rem;z-index:320;display:flex;align-items:center;gap:.6rem;font-size:9px;letter-spacing:.1em;color:var(--text-dim)}
.view-ctl .vc-pos{opacity:.55;font-variant-numeric:tabular-nums}
/* which ring of the wired the camera is over — the legend for the whole
   further-out-is-worse progression */
.view-ctl .vc-zone{letter-spacing:.15em;opacity:.75;transition:color .3s}
.view-ctl .vc-zone[data-z="fringe"]{color:var(--text-dim)}
.view-ctl .vc-zone[data-z="private"]{color:#b0b0c2}
.view-ctl .vc-zone[data-z="decay"]{color:#a8848f}
.view-ctl .vc-zone[data-z="hostile"]{color:#7fdc3a;opacity:.9}
.view-ctl .vc-home{padding:.25rem .5rem;background:transparent;border:1px solid var(--border-mid);color:var(--text-dim);font:inherit;letter-spacing:.1em;transition:border-color .12s,color .12s}
.view-ctl .vc-home:hover{border-color:var(--accent-glow);color:var(--text-bright)}

.wire-net{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible}
.wire{stroke:var(--accent);stroke-width:1;opacity:var(--wire-op);transition:opacity .12s,stroke .12s}
.wire.near{opacity:.55;stroke:var(--accent-glow);filter:drop-shadow(0 0 3px var(--accent-glow))}
.wire.lit{opacity:.5;stroke:var(--accent-glow)}
.stage.focusing .wire{opacity:.02}
.stage.focusing .wire.lit{opacity:.55;stroke:var(--accent-glow);filter:drop-shadow(0 0 2px var(--accent-glow))}
.wire.glitch{stroke:var(--glitch-c);opacity:.9;stroke-dasharray:3 4;filter:drop-shadow(0 0 4px var(--glitch-c))}
/* backbone proper — edge router to edge router. Heavy and amber to match
   .node.router, because this is the one link carrying every cluster's transit */
.wire.trunk{stroke:#c8913a;stroke-width:2.6;opacity:.32}
.wire.trunk.near,.wire.trunk.lit{stroke:#f0b43c;opacity:.68;filter:drop-shadow(0 0 4px rgba(240,180,60,.6))}
.stage.focusing .wire.trunk{opacity:.04}
.stage.focusing .wire.trunk.lit{opacity:.68}

.bubble{fill:rgba(0,208,208,.018);stroke:var(--neo);stroke-width:1;stroke-dasharray:2 6;opacity:.5;transition:opacity .3s}

.bubble-straw{fill:rgba(224,48,60,.018);stroke:var(--straw);stroke-width:1;stroke-dasharray:2 6;opacity:.5;transition:opacity .3s}

.pkt-layer{pointer-events:none}
.pkt{pointer-events:auto;filter:drop-shadow(0 1px 2px rgba(0,0,0,.9))}
.pkt-hit{fill:transparent;pointer-events:all}
.pkt-in{transform-box:fill-box;transform-origin:center;transition:transform .1s ease-out}
.pkt:hover .pkt-in{transform:scale(1.45)}      /* grow to inspect */
.env-body{fill:#f4f4ee;stroke:#000;stroke-width:1}
.env-flap{fill:none;stroke:#000;stroke-width:.9;stroke-linejoin:round;stroke-linecap:round}
.env-strip{stroke:none}                         /* colour set per protocol in JS */
.stage.no-pkt .pkt-layer{display:none}
.stage.dragging .pkt-layer{opacity:0}
.stage.dragging .node{transition:none}

.stage.locked .node{cursor:not-allowed}

.pkt-ctl{position:absolute;right:2rem;bottom:5.4rem;z-index:320;width:198px;padding:.55rem .6rem;border:1px solid var(--border-mid);background:rgba(9,9,17,.86);backdrop-filter:blur(2px);font-size:10px;letter-spacing:.08em}
.pkt-ctl.warn{border-color:rgba(138,127,191,.45)}
.pkt-ctl .ctl-h{color:var(--text);letter-spacing:.16em;font-size:9px;margin-bottom:.45rem;opacity:.8}
.ctl-row{display:flex;align-items:center;gap:.45rem;width:100%;padding:.28rem .3rem;margin-bottom:.18rem;background:transparent;border:1px solid transparent;color:var(--text-dim);font:inherit;letter-spacing:.08em;text-align:left;transition:border-color .12s,color .12s}
.ctl-row:hover{border-color:var(--border-mid);color:var(--text)}
.ctl-row.on{color:var(--text-bright)}
.ctl-led{flex:none;width:6px;height:6px;border:1px solid var(--text-dim);background:transparent;transition:background .12s,box-shadow .12s,border-color .12s}
.ctl-row.on .ctl-led{background:var(--accent-glow);border-color:var(--accent-glow);box-shadow:0 0 6px var(--accent-glow)}
.ctl-name{flex:1}
.ctl-val{font-size:9px;letter-spacing:.12em;opacity:.75}
.ctl-q{margin-top:.35rem;font-size:9px;color:var(--text-dim);opacity:.7;letter-spacing:.06em}
.ctl-note{margin-top:.15rem;font-size:8.5px;letter-spacing:.05em;color:var(--text-dim);opacity:.75}
.pkt-ctl.warn .ctl-note{color:var(--accent-glow);opacity:.75}
.pkt-ctl.nudge{animation:ctl-nudge .4s ease-out}
@keyframes ctl-nudge{
  0%,100%{transform:translateX(0);border-color:var(--border-mid)}
  15%{transform:translateX(-4px);border-color:var(--glitch-r)}
  45%{transform:translateX(4px);border-color:var(--glitch-r)}
  75%{transform:translateX(-2px);border-color:var(--glitch-r)}
}

.pdu-win{position:fixed;z-index:9500;display:none;width:322px;border:1px solid var(--border-mid);background:rgba(7,7,13,.96);box-shadow:0 0 34px rgba(0,0,0,.7);font-size:10px;letter-spacing:.05em}
.pdu-win.on{display:block}
.pdu-bar{display:flex;align-items:center;gap:.5rem;padding:.4rem .5rem;border-bottom:1px solid var(--border-mid);background:rgba(138,127,191,.08)}
.pdu-title{flex:1;font-size:9px;letter-spacing:.11em;color:var(--text-bright);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pdu-x{flex:none;width:16px;height:16px;line-height:1;background:transparent;border:1px solid var(--border-mid);color:var(--text-dim);font:inherit;font-size:11px;transition:color .12s,border-color .12s}
.pdu-x:hover{color:var(--glitch-r);border-color:var(--glitch-r)}
.pdu-body{padding:.5rem .6rem;max-height:62vh;overflow-y:auto}
.pdu-sec{padding:.35rem 0;border-bottom:1px dashed var(--border)}
.pdu-sec:last-child{border-bottom:0}
.pdu-h{font-size:8.5px;letter-spacing:.14em;color:var(--text-dim);margin-bottom:.35rem}
.pdu-f{display:flex;gap:.5rem;line-height:1.75}
.pdu-k{flex:none;width:82px;color:var(--text-dim);font-size:9px}
.pdu-v{flex:1;color:var(--text);word-break:break-word}
.pdu-v.strong{color:var(--text-bright)}
.pdu-v em{font-style:normal;color:var(--text-dim);font-size:9px}
.pdu-dot{display:inline-block;width:7px;height:7px;margin-right:.4rem;border:1px solid rgba(0,0,0,.6);vertical-align:middle}
.osi-row{display:flex;gap:.45rem;line-height:1.7}
.osi-n{flex:none;width:18px;color:var(--accent-glow);opacity:.75;font-size:9px}
.osi-k{flex:none;width:64px;color:var(--text-dim);font-size:9px}
.osi-v{flex:1;color:var(--text);font-size:9px;word-break:break-word}

.node{position:absolute;top:0;left:0;display:flex;align-items:stretch;gap:7px;width:152px;padding:7px 8px;background:rgba(9,9,17,.74);border:1px solid var(--border-mid);text-decoration:none;backdrop-filter:blur(2px);will-change:transform;transition:border-color .15s,box-shadow .15s,opacity .2s;z-index:12}
.node:hover,.node.src{border-color:var(--accent-glow);box-shadow:0 0 14px rgba(138,127,191,.28)}
.node.local{border-color:var(--accent)}
.node.local.src,.node.local:hover{border-color:var(--accent-glow);box-shadow:0 0 16px rgba(138,127,191,.36)}
.node.neo{border-color:rgba(0,208,208,.35)}
.node.neo:hover,.node.neo.src{border-color:var(--neo);box-shadow:0 0 14px rgba(0,208,208,.22)}
.node.straw{border-color:rgba(224,48,60,.35)}
.node.straw:hover,.node.straw.src{border-color:var(--straw);box-shadow:0 0 14px rgba(224,48,60,.22)}
/* edge routers — infrastructure, not destinations */
.node.router{width:132px;border-color:rgba(240,180,60,.4);cursor:default}
.node.router:hover,.node.router.src{border-color:#f0b43c;box-shadow:0 0 14px rgba(240,180,60,.22)}
.node.router .nsub{color:#f0b43c}
.node.router .thumb span{color:#f0b43c}
.stage.focusing .node:not(.src){opacity:.28}
.node .meta{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center;gap:1px}
.node .nname{font-size:11px;color:var(--text-bright);letter-spacing:.03em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.node .nsub{font-size:8px;color:var(--accent-glow);letter-spacing:.06em;opacity:.7}
.node.neo .nsub{color:var(--neo)}
.node.straw .nsub{color:var(--straw)}
.node .nip{font-size:7px;color:var(--text-dim);letter-spacing:.08em;margin-top:1px}
.node .thumb{flex:none;width:42px;height:42px;position:relative;border:1px solid var(--border-mid);background:repeating-linear-gradient(135deg,#0a0a14 0 5px,#12121f 5px 10px);display:flex;align-items:center;justify-content:center;overflow:hidden}
.node .thumb::after{content:'';position:absolute;inset:0;background:repeating-linear-gradient(to bottom,transparent 0 3px,rgba(0,0,0,.25) 3px 4px);pointer-events:none}
.node .thumb span{font-size:8px;letter-spacing:.12em;color:var(--text-dim);z-index:1}
.node .thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0}

.hud{position:absolute;left:2.2rem;bottom:1.1rem;z-index:300;font-size:10px;letter-spacing:.06em;color:var(--text-dim);line-height:1.55;max-width:320px;pointer-events:none}
.hud-h{color:var(--text);letter-spacing:.16em;margin-bottom:.25rem}
.hud-stat span{display:block}
.hud-sep{color:var(--border-mid);margin:.2rem 0;letter-spacing:0}
.hud-log{min-height:3.2em;margin-top:.2rem}
.hud-log div{white-space:nowrap;overflow:hidden}
.hud-log .drop{color:var(--glitch-r)}
.hud-log .ok{color:var(--accent-glow);opacity:.55}

.hint{position:absolute;right:2rem;bottom:1.6rem;z-index:300;font-size:9px;letter-spacing:.1em;color:var(--text-dim);opacity:.5;text-align:right;pointer-events:none;line-height:1.8}

.neo-cap{position:absolute;z-index:11;font-size:9px;letter-spacing:.14em;color:var(--neo);opacity:.6;pointer-events:none;white-space:nowrap}

.straw-cap{position:absolute;z-index:11;font-size:9px;letter-spacing:.14em;color:var(--straw);opacity:.6;pointer-events:none;white-space:nowrap}
.neo-cap b,.straw-cap b{font-weight:normal;color:var(--text-dim);opacity:.8;margin-left:.5rem}

.handshake{position:absolute;inset:0;z-index:9000;display:none;align-items:center;justify-content:center;background:rgba(3,3,6,.72);backdrop-filter:blur(1px)}
.handshake.on{display:flex}
.hs-box{border:1px solid var(--border-mid);background:rgba(9,9,17,.92);padding:1.6rem 2rem;min-width:340px;font-size:12px;line-height:2;box-shadow:0 0 40px rgba(0,0,0,.6)}
.hs-box .hl{display:block;color:var(--text);opacity:0;white-space:nowrap}
.hs-box .hl.ok b{color:var(--accent-glow)}
.hs-box .hl.done{color:var(--accent-glow)}

@keyframes flicker{0%{opacity:1}20%{opacity:.94}40%{opacity:1}60%{opacity:.97}80%{opacity:.93}100%{opacity:1}}

@media (hover:none) and (pointer:coarse){
  .wired,.wired a,.wired button{cursor:auto}
  .hint{display:none}
  /* the drifting smoke is the only always-on animation big enough for a
     phone GPU to notice — the gradients stay, the motion goes */
  .pfog,.pfog::before{animation:none}
}

@media (prefers-reduced-motion:reduce){
  .pfog,.pfog::before{animation:none}
  .dead .led{animation:none;opacity:.5}
  .dead.rogue::before{animation:none;opacity:.6}
  .chunk .corrupt{animation:none}
}

/* ── mobile ──────────────────────────────────────────
   The map itself needs no layout work here — it's laid
   out in a fixed virtual world and the camera fits it
   to the screen. This block only compacts the chrome. */
@media (max-width:640px){
  nav{padding:1rem 1.2rem;gap:1.2rem;font-size:11px}
  nav .nav-pages{gap:1.2rem}
  nav .nav-pages a{display:none}
  nav .nav-pages a.active{display:inline;white-space:nowrap}
  nav .nav-jump{display:inline-block}

  .layer-badge{display:none}
  .hint{display:none}             /* no room next to the HUD at this width */
  .view-ctl{left:1.2rem;top:3.6rem;font-size:8px;gap:.45rem}
  .view-ctl .vc-zone{letter-spacing:.1em}

  .hud{left:1.2rem;bottom:.9rem;font-size:9px;max-width:220px}
  .hud-log{display:none}          /* scrolling log is noise at this size */
  .hud-sep{display:none}

  .pkt-ctl{right:1.2rem;bottom:.9rem;width:170px;padding:.45rem .5rem}

  .hs-box{min-width:0;width:calc(100vw - 2.4rem);padding:1.2rem 1.1rem;font-size:11px}

  .pdu-win{max-width:calc(100vw - 1.6rem)}
}
