/* NULL SIGNAL — style.css */
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&display=swap');
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
  --ts:1;  /* user text-scale multiplier (Settings > DISPLAY > TEXT SIZE); default 1 = no change */
  --bg:#030210;--panel:#06041a;--line:#12103a;--dim:#28206a;
  --pur:#6633ff;--purL:#9966ff;--cyan:#00eeff;
  --gold:#ffcc00;--red:#ff3355;--grn:#00ff88;
  --txt:#ccd6ff;--mut:#8480c0;   /* muted/secondary text — brightened from #3a3560 (contrast ~1.7:1, unreadable) to ~5:1 so upgrade/weapon descriptions + all faint labels are legible */
  --FH:'Orbitron',sans-serif;--FM:'Share Tech Mono',monospace;
}
html,body{width:100%;height:100%;overflow:hidden;background:var(--bg);font-family:var(--FM);color:var(--txt);-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;touch-action:manipulation}
img,canvas{image-rendering:pixelated;image-rendering:-moz-crisp-edges;image-rendering:crisp-edges}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
@keyframes pulse{0%,100%{opacity:.4}50%{opacity:1}}
@keyframes shake{0%,100%{transform:translateX(0)}20%{transform:translateX(-6px)}40%{transform:translateX(6px)}60%{transform:translateX(-4px)}80%{transform:translateX(4px)}}
@keyframes popIn{from{transform:scale(.8);opacity:0}to{transform:scale(1);opacity:1}}
@keyframes xpop{0%{opacity:1;transform:translateY(0)}100%{opacity:0;transform:translateY(-40px)}}

/* LAYOUT */
#game{position:fixed;inset:0;display:flex;flex-direction:column;background:var(--bg)}

/* ── FIRST-RUN INTRO ────────────────────────────────────────── */
#intro,#onboard{display:none;position:fixed;inset:0;z-index:400;background:rgba(3,2,16,.94);
  align-items:center;justify-content:center;padding:20px;animation:fadeIn .2s}
#intro.on,#onboard.on{display:flex}
/* #intro doubles as the HOW TO PLAY reference (reopened from the operator-select header), so
   it must paint above char-select (850), the settings sheet (900) and meta-shop (901). */
#intro{z-index:910}
/* max-height + overflow-y keeps the box on-screen when content is tall — large TEXT SIZE and/or
   a short LANDSCAPE viewport (phone landscape, CrazyGames iframes). Without it, the centered box
   overflows top AND bottom and the START/GOT IT button is clipped off with no way to scroll to it.
   (100vh - 40px) leaves the container's 20px padding on each side; overflow-y:auto only scrolls
   when actually needed, so the common (portrait/low-scale) case is visually unchanged. */
#intro-box,#onboard-box{width:100%;max-width:430px;background:var(--panel);border:1px solid var(--pur);
  box-shadow:0 0 60px rgba(102,51,255,.3);padding:22px 20px;text-align:center;
  max-height:calc(100vh - 40px);overflow-y:auto}
#onboard-box .vol-row{margin:10px 0}
.ob-hint{font-size:calc(8px*var(--ts,1));color:var(--mut);letter-spacing:.5px;line-height:1.6;margin:4px 0 16px}
.in-title{font-family:var(--FH);font-size:calc(22px*var(--ts,1));letter-spacing:5px;color:var(--purL)}
.in-tag{font-size:calc(9px*var(--ts,1));color:var(--mut);letter-spacing:1px;margin-top:6px;margin-bottom:18px}
.in-rows{display:flex;flex-direction:column;gap:11px;text-align:left;margin-bottom:16px}
.in-row{display:flex;gap:10px;align-items:flex-start;font-size:calc(10px*var(--ts,1));line-height:1.6;color:var(--txt)}
.in-row b{color:var(--cyan);font-weight:400}
.in-ico{color:var(--purL);font-size:calc(13px*var(--ts,1));line-height:1.3;width:16px;flex-shrink:0;text-align:center}
.in-keys{display:flex;flex-wrap:wrap;gap:12px;justify-content:center;font-size:calc(8px*var(--ts,1));
  color:var(--mut);letter-spacing:1px;padding:10px 0;border-top:1px solid var(--line)}
.in-keys kbd{font-family:var(--FM);background:var(--line);border:1px solid var(--dim);
  padding:1px 5px;margin-right:2px;color:var(--txt);font-size:calc(8px*var(--ts,1))}
.in-note{font-size:calc(8px*var(--ts,1));color:var(--gold);letter-spacing:.5px;line-height:1.7;
  padding:9px;background:rgba(255,204,0,.06);border:1px solid rgba(255,204,0,.25);margin-bottom:16px}
.in-btn{width:100%;padding:13px;font-family:var(--FH);font-size:calc(12px*var(--ts,1));letter-spacing:4px;
  background:linear-gradient(135deg,var(--pur),var(--purL));color:#fff;border:none;cursor:pointer}
.in-btn:active{opacity:.85}
/* Keyboard hints are meaningless on a touch device. */
@media (max-width:1023px){.in-keys{display:none}}

/* ── COLLAPSIBLE SECTIONS (side panel) ─────────────────────── */
.sect-head{
  display:flex;align-items:center;gap:8px;
  padding:9px 12px;cursor:pointer;user-select:none;
  background:rgba(102,51,255,.06);
  border-bottom:1px solid var(--line);
  transition:background .15s;
}
.sect-head:hover{background:rgba(102,51,255,.14)}
.sect-head:active{background:rgba(102,51,255,.2)}
.sect-caret{
  font-size:calc(10px*var(--ts,1));color:var(--mut);line-height:1;flex-shrink:0;
  display:inline-block;transition:transform .18s ease;
}
.sect-head.open .sect-caret{transform:rotate(90deg);color:var(--purL)}
.sect-titles{display:flex;flex-direction:column;gap:1px;flex:1;min-width:0}
.sect-title{font-family:var(--FH);font-size:calc(9px*var(--ts,1));letter-spacing:2px}
.sect-sub{font-size:calc(7px*var(--ts,1));color:var(--mut);letter-spacing:.5px}
.sect-cur{font-family:var(--FH);font-size:calc(15px*var(--ts,1));flex-shrink:0}
/* Collapsed bodies are removed from layout entirely, so the panel shrinks to
   just its headers rather than leaving a gap. */
.sect-body.collapsed{display:none}

/* Scrollbars: the browser default is a pale strip that reads as a rendering bug
   against this palette. Styled for both engines. */
*{scrollbar-width:thin;scrollbar-color:var(--dim) transparent}
::-webkit-scrollbar{width:8px;height:8px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--dim);border-radius:0}
::-webkit-scrollbar-thumb:hover{background:var(--pur)}

/* STAGE — single column by default (narrow screens), three columns on desktop. */
#stage{flex:1;min-height:0;display:flex;flex-direction:column}
/* Panel geometry lives here, not inline, so the desktop media query can override it. */
#panel-upg{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;background:var(--bg)}
#panel-board{flex:1;min-height:0;flex-direction:column;background:var(--bg)}

#topbar{flex-shrink:0;display:flex;align-items:center;justify-content:space-between;padding:max(env(safe-area-inset-top),6px) 12px 6px;background:rgba(6,4,26,.96);border-bottom:1px solid var(--line);gap:6px}
#tb-left{display:flex;flex-direction:column;align-items:flex-start;gap:2px}
#tb-mid{display:flex;gap:10px;align-items:center}
/* Base state must be declared BEFORE the desktop media query that turns it on —
   media queries add no specificity, so a later rule of equal weight wins. */
#tb-stats{display:none}
/* Control labels are desktop-only; on a phone they wrap and crowd the row, so
   narrow screens fall back to icon + state as before. */
.ctrl-lbl{display:none}
#tb-shields{display:flex;gap:2px;align-items:center}
.shield{font-size:calc(16px*var(--ts,1));transition:all .2s}
.shield.lost{opacity:.15;filter:grayscale(1)}
.tb-cur{display:flex;align-items:center;gap:3px;font-family:var(--FH);font-size:calc(11px*var(--ts,1))}
#score-val{font-family:var(--FH);font-size:calc(14px*var(--ts,1));color:var(--gold);letter-spacing:1px}

/* DIFF BAR */
#diff-bar{flex-shrink:0;display:flex;align-items:center;gap:8px;padding:4px 12px;background:rgba(6,4,26,.85);border-bottom:1px solid var(--line)}
#diff-lbl{font-family:var(--FH);font-size:calc(8px*var(--ts,1));color:var(--purL);letter-spacing:2px;white-space:nowrap}
#diff-prog{flex:1;height:3px;background:var(--line);border-radius:0;overflow:hidden}
#diff-fill{height:100%;background:linear-gradient(90deg,var(--pur),var(--cyan));width:0%;transition:width .3s}
#streak-lbl{font-family:var(--FH);font-size:calc(11px*var(--ts,1));color:var(--cyan);min-width:32px;text-align:right}

/* RADAR */
#radar-wrap{flex:1;min-height:0;position:relative;overflow:hidden;background:var(--bg)}
#main-canvas{position:absolute;inset:0;width:100%;height:100%}
#phase-lbl{position:absolute;top:6px;left:50%;transform:translateX(-50%);font-family:var(--FH);font-size:calc(8px*var(--ts,1));color:var(--purL);letter-spacing:2px;animation:pulse 2s infinite;pointer-events:none;z-index:5;white-space:nowrap}
#alert-lbl{display:none;position:absolute;top:calc(50% - 40px);left:50%;transform:translateX(-50%);font-family:var(--FH);font-size:calc(14px*var(--ts,1));font-weight:900;color:var(--red);letter-spacing:4px;pointer-events:none;z-index:10;text-shadow:0 0 30px var(--red);animation:pulse .5s infinite}
/* Breach flash: a SOFT opacity fade (not a hard on/off), desaturated red, low alpha — so
   it's not a photosensitive strobe. Driven by a .on class + a de-stacking timer in JS. */
#hit-flash{position:absolute;inset:0;background:rgba(255,80,90,.12);pointer-events:none;z-index:8;opacity:0;transition:opacity .28s ease}
#hit-flash.on{opacity:1}
#kill-feed{position:absolute;top:28px;left:6px;display:flex;flex-direction:column;gap:2px;pointer-events:none;z-index:6}
.kf-row{font-size:calc(8px*var(--ts,1));letter-spacing:.5px;padding:2px 6px;background:rgba(3,2,16,.8);border-left:2px solid;animation:fadeIn .15s;white-space:nowrap}
.xtal-pop{position:absolute;pointer-events:none;z-index:50;font-family:var(--FH);font-size:calc(9px*var(--ts,1));color:var(--cyan);animation:xpop .8s ease-out forwards}

/* UPGRADE STATUS STRIP */
#upg-strip{position:absolute;right:8px;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;gap:6px;pointer-events:none;z-index:5;background:rgba(3,2,16,.75);padding:10px 8px;border-radius:0;border:1px solid rgba(102,51,255,.2)}
.us-row{display:flex;align-items:center;gap:5px;line-height:1}
.us-row.active{color:var(--purL)}
.us-sym{font-size:calc(16px*var(--ts,1));width:20px;text-align:center}
.us-lv{font-family:var(--FH);font-size:calc(13px*var(--ts,1));min-width:14px;color:var(--cyan)}

/* STATUS BAR (bottom of radar) */
#status-bar{position:absolute;bottom:0;left:0;right:0;display:flex;justify-content:space-around;align-items:center;padding:4px 0 calc(4px + env(safe-area-inset-bottom));background:rgba(3,2,16,.75);border-top:1px solid rgba(102,51,255,.2);pointer-events:none;z-index:5}
.sb-item{display:flex;flex-direction:column;align-items:center;gap:1px}
.sb-val{font-family:var(--FH);font-size:calc(11px*var(--ts,1));color:var(--purL)}
.sb-lbl{font-size:calc(7px*var(--ts,1));color:var(--mut);letter-spacing:1px}

/* WEAPON BAR */
#weapon-bar{flex-shrink:0;display:flex;height:38px;border-top:1px solid var(--line);background:rgba(3,2,16,.95)}
.wb-btn{flex:1;min-height:44px;display:flex;align-items:center;justify-content:center;gap:4px;border:none;background:transparent;border-right:1px solid var(--line);border-bottom:2px solid transparent;font-family:var(--FM);font-size:calc(9px*var(--ts,1));color:var(--mut);cursor:pointer;transition:all .15s;letter-spacing:.5px;-webkit-tap-highlight-color:transparent}
.wb-btn:last-child{border-right:none}
.wb-btn.active{background:rgba(255,255,255,.04);border-bottom-color:currentColor}
.wb-btn.locked{opacity:.4}
.wb-lv{font-size:calc(7px*var(--ts,1));padding:1px 3px;border:1px solid currentColor;border-radius:0;opacity:.7}

/* CONTROLS */
#controls{flex-shrink:0;height:40px;display:flex;border-top:1px solid var(--line);background:rgba(6,4,26,.96)}
.ctrl-btn{flex:1;min-height:44px;display:flex;align-items:center;justify-content:center;gap:5px;border:none;background:transparent;border-right:1px solid var(--line);font-family:var(--FM);font-size:calc(9px*var(--ts,1));color:var(--purL);cursor:pointer;letter-spacing:.5px;-webkit-tap-highlight-color:transparent}
.ctrl-btn:last-child{border-right:none}
.ctrl-btn.on{color:var(--cyan);background:rgba(0,238,255,.04)}
/* keyboard-shortcut key badges + rebinding UI — desktop only (keyboard context) */
.kb-hint{display:none;margin-left:5px;font-size:calc(8px*var(--ts,1));font-family:var(--FM);line-height:1;padding:1px 4px;border:1px solid var(--dim);border-radius:2px;color:var(--mut);letter-spacing:0;flex-shrink:0}
.ctrl-btn.on .kb-hint{border-color:currentColor;color:currentColor}
.kb-key{min-width:30px;text-align:center;font-family:var(--FM);font-size:calc(10px*var(--ts,1));padding:2px 7px;border:1px solid var(--cyan);border-radius:2px;color:var(--cyan)}
.set-row.rebinding{background:rgba(255,209,102,.1)}
.set-row.rebinding .kb-key{border-color:var(--gold);color:var(--gold)}
.kb-section{display:none}
/* AUTO/BUY toggle chips are compact so FLARE + OVERDRIVE keep their room on narrow phones */
#cc-auto,#cc-autoupg{flex:0 0 auto;padding:0 10px;gap:4px}
@media (min-width:1024px){ .kb-hint{display:inline-block} .kb-section{display:block}
  /* ultra-wide: don't let FLARE/OVERDRIVE stretch across the whole bar — cap + centre the group */
  #controls{justify-content:center} #flare-btn,#od-btn{max-width:420px} }
/* RANK MODE: the ranked lock. Active state reads red = committed/hardcore. */
.ctrl-btn.rank.on{color:var(--red);background:rgba(255,51,85,.08)}
/* Assist buttons while rank mode is engaged: visibly locked out. */
.ctrl-btn.locked{opacity:.3;cursor:not-allowed}

/* BOTTOM NAV BAR */
#nav-bar{flex-shrink:0;display:flex;min-height:48px;background:var(--panel);border-top:2px solid var(--line)}
.nav-btn{flex:1;min-height:44px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;border:none;background:transparent;cursor:pointer;color:var(--mut);transition:all .15s;border-top:2px solid transparent;-webkit-tap-highlight-color:transparent}
.nav-btn.active{color:var(--purL);border-top-color:var(--purL);background:rgba(102,51,255,.06)}
.nav-btn:active{background:rgba(102,51,255,.1)}
/* SETTINGS SHEET — bottom-sheet overlay (mirrors the callsign modal). Holds the set-once
   toggles that used to crowd the combat row; buttons keep their .ctrl-btn look as rows. */
/* z-index 900 sits ABOVE char-select (850) and the game-over overlay (800) but below the
   ad interstitial (9999), so Settings opens on top from operator-select AND in-game. */
#settings-sheet{display:none;position:fixed;inset:0;z-index:900;background:rgba(3,2,16,.82);align-items:flex-end;justify-content:center}
#settings-sheet.on{display:flex}
.ss-panel{width:100%;max-width:480px;background:var(--panel);border-top:2px solid var(--pur);padding:14px 14px calc(16px + env(safe-area-inset-bottom));max-height:82vh;overflow-y:auto;animation:ssup .18s ease-out}
@keyframes ssup{from{transform:translateY(28px);opacity:.5}to{transform:translateY(0);opacity:1}}
.ss-head{display:flex;align-items:center;justify-content:space-between;font-family:var(--FH);font-size:calc(13px*var(--ts,1));letter-spacing:3px;color:var(--cyan);margin-bottom:4px}
.ss-x{background:transparent;border:none;color:var(--mut);font-size:calc(15px*var(--ts,1));cursor:pointer;padding:4px 6px;line-height:1;min-height:32px}
.ss-group{font-family:var(--FM);font-size:calc(8px*var(--ts,1));letter-spacing:1.5px;color:var(--mut);margin:13px 0 6px}
.ss-note{font-size:calc(8px*var(--ts,1));color:var(--gold);letter-spacing:.4px;margin:6px 2px 2px;opacity:.85}
.ctrl-btn.set-row{flex:none;width:100%;justify-content:space-between;padding:0 14px;min-height:48px;border:1px solid var(--line);border-right:1px solid var(--line);background:rgba(255,255,255,.015);margin-bottom:6px;font-size:calc(11px*var(--ts,1))}
.ctrl-btn.set-row .ctrl-lbl{flex:1;text-align:left;margin-left:8px}
.ss-done{width:100%;margin-top:14px;padding:13px;font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:3px;background:var(--pur);color:#fff;border:none;cursor:pointer;min-height:46px}
/* AUDIO MIX sliders — one row per category (label · slider · %). */
.vol-row{display:flex;align-items:center;gap:12px;min-height:44px;padding:0 2px}
.vol-lbl{flex:0 0 84px;text-align:left;font-family:var(--FM);font-size:calc(11px*var(--ts,1));letter-spacing:.5px;color:var(--txt)}
/* Tall, transparent input = a big finger target (34px), while the visible track stays
   thin (6px). touch-action:none so a horizontal drag moves the slider instead of
   scrolling the sheet. Thumb is 24px (comfortable to grab on a phone). */
.vol-slider{flex:1;min-width:0;-webkit-appearance:none;appearance:none;height:34px;background:transparent;outline:none;cursor:pointer;touch-action:none;-webkit-tap-highlight-color:transparent}
.vol-slider::-webkit-slider-runnable-track{height:6px;border-radius:3px;background:var(--line)}
.vol-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;margin-top:-9px;width:24px;height:24px;border-radius:50%;background:var(--cyan);border:2px solid #041018;box-shadow:0 0 7px rgba(0,238,255,.55)}
.vol-slider:active::-webkit-slider-thumb{background:#7ff6ff}
.vol-slider::-moz-range-track{height:6px;border-radius:3px;background:var(--line)}
.vol-slider::-moz-range-progress{height:6px;border-radius:3px;background:var(--cyan)}
.vol-slider::-moz-range-thumb{width:22px;height:22px;border-radius:50%;background:var(--cyan);border:2px solid #041018}
.vol-pct{flex:0 0 42px;text-align:right;font-family:var(--FM);font-size:calc(11px*var(--ts,1));color:var(--cyan)}
/* Top-bar SETTINGS button — the desktop entry (the bottom nav + its gear tab are hidden
   on desktop). A clearly-labelled bright pill so it can't be missed. Hidden on mobile,
   where the ⚙ nav tab is the entry instead. */
#tb-settings{display:none;align-items:center;gap:6px;background:rgba(0,238,255,.08);border:1px solid var(--cyan);color:var(--cyan);font-family:var(--FM);font-size:calc(10px*var(--ts,1));letter-spacing:1.5px;line-height:1;cursor:pointer;padding:7px 12px;min-height:32px;margin-left:12px;white-space:nowrap;-webkit-tap-highlight-color:transparent}
#tb-settings>span{font-size:calc(13px*var(--ts,1))}
#tb-settings:hover{background:rgba(0,238,255,.18)}
#tb-settings:active{background:rgba(0,238,255,.28)}
.nav-icon{font-size:calc(18px*var(--ts,1));line-height:1}
.nav-lbl{font-family:var(--FH);font-size:calc(7px*var(--ts,1));letter-spacing:1px}

/* BANNER AD */
/* Bottom banner. Holds a fixed 728x90 ad; fitBanner() scales #ad-slot to the
   strip width on narrow screens and sets the strip height to match, so the ad is
   always fully visible (native on desktop, shrunk on phones) and never overflows. */
#banner-ad{flex-shrink:0;height:56px;display:flex;align-items:center;justify-content:center;background:#050214;border-top:1px solid var(--line);overflow:hidden}
#ad-slot{position:relative;width:728px;height:90px;flex-shrink:0;transform-origin:center center;display:flex;align-items:center;justify-content:center}
/* "ADVERTISEMENT" label sitting BEHIND the ad (z-index:-1) so the loading/empty
   slot is identifiable instead of an invisible black strip; the ad iframe paints
   on top once it fills. #ad-slot's transform makes this a self-contained layer. */
.ad-ph{position:absolute;inset:0;z-index:-1;display:flex;align-items:center;justify-content:center;color:var(--mut);font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:3px;opacity:.4;border:1px dashed var(--line)}



/* WEAPON UPGRADE CARDS */
.wsection{border-bottom:1px solid var(--line)}
.wheader{display:flex;align-items:center;gap:9px;padding:9px 12px 7px;cursor:pointer;transition:background .12s}
.wheader:hover{background:rgba(102,51,255,.07)}
.wsection.active .wheader{background:rgba(102,51,255,.09)}
.wheader-sym{display:flex;align-items:center;justify-content:center;width:24px;flex-shrink:0}
.wheader-info{flex:1;min-width:0}
.wheader-name{font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:2px;margin-bottom:2px}
.wheader-desc{font-size:calc(8px*var(--ts,1));color:var(--mut);line-height:1.4}
/* Unlock buttons sit side by side; stacked they crowded the row and made the
   cheaper option look subordinate rather than alternative. */
.wunlock-wrap{display:flex;gap:5px;flex-shrink:0}
.wunlock{display:flex;align-items:center;gap:3px;font-family:var(--FH);font-size:calc(9px*var(--ts,1));
  padding:6px 8px;cursor:pointer;border:1px solid var(--gold);background:rgba(255,204,0,.08);
  color:var(--gold);letter-spacing:1px;border-radius:2px;white-space:nowrap;transition:all .12s}
.wunlock:hover{background:rgba(255,204,0,.18)}
.wunlock.crystal{border-color:var(--cyan);color:var(--cyan);background:rgba(0,238,255,.08)}
.wunlock.crystal:hover{background:rgba(0,238,255,.18)}
.wunlock[disabled]{opacity:.4;cursor:default}

.wupg-list{padding:2px 12px 8px}
.wupg-row{display:grid;grid-template-columns:1fr auto;grid-template-areas:'name cost' 'meter cost';
  column-gap:8px;row-gap:3px;align-items:center;padding:6px 0;
  border-bottom:1px solid var(--line);cursor:default;transition:opacity .12s}
.wupg-row:last-child{border-bottom:none}
.wupg-row.can{cursor:pointer}
.wupg-row.can:hover .wupg-cost{background:rgba(255,204,0,.18)}
.wupg-name{grid-area:name;font-size:calc(9px*var(--ts,1));color:var(--txt);letter-spacing:.5px}
.wupg-sub{color:var(--mut);font-size:calc(8px*var(--ts,1))}
.wupg-meter{grid-area:meter;display:flex;align-items:center;gap:5px}
.wupg-bars{display:flex;gap:2px;flex:1}
.wupg-bar{height:3px;flex:1;max-width:16px;background:rgba(255,255,255,.09)}
.wupg-bar.filled{background:currentColor}
.wupg-cost{grid-area:cost;display:flex;align-items:center;gap:3px;font-family:var(--FH);font-size:calc(9px*var(--ts,1));
  white-space:nowrap;padding:4px 6px;border:1px solid var(--line);border-radius:2px;color:var(--mut)}
.wupg-row.can .wupg-cost{color:var(--gold);border-color:rgba(255,204,0,.5);background:rgba(255,204,0,.08)}
.wupg-row.max .wupg-cost{color:var(--grn);border-color:rgba(0,255,136,.3)}
.wupg-row.max{opacity:.55}

/* PERMA CARDS */
/* ── UPGRADE CARDS ──────────────────────────────────────────
   Three states have to be readable at a glance while the radar is moving:
   affordable, not yet affordable, and maxed. The old card showed all three
   almost identically — a 3px border tint was the only difference — so you had
   to read the numbers to know whether a tap would do anything. */
.pcard{
  display:grid;
  grid-template-columns:26px 1fr auto;
  grid-template-areas:'sym name cost' 'sym desc cost' 'sym meter cost';
  align-items:center;
  column-gap:9px;row-gap:2px;
  padding:9px 12px;
  border-bottom:1px solid var(--line);
  border-left:3px solid transparent;
  cursor:default;
  transition:background .12s,border-color .12s;
}
.pcard-sym{grid-area:sym;display:flex;align-items:center;justify-content:center}
.pcard-name{grid-area:name;font-family:var(--FH);font-size:calc(10px*var(--ts,1));color:var(--txt);letter-spacing:1px}
.pcard-desc{grid-area:desc;font-size:calc(8px*var(--ts,1));color:var(--mut);line-height:1.45}
.pcard-meter{grid-area:meter;display:flex;align-items:center;gap:6px;margin-top:3px}
.pcard-cost{grid-area:cost;display:flex;align-items:center;gap:3px;
  font-family:var(--FH);font-size:calc(11px*var(--ts,1));color:var(--mut);white-space:nowrap;
  padding:4px 7px;border:1px solid var(--line);border-radius:2px;transition:all .12s}
.pcard-cost img{opacity:.5;transition:opacity .12s}

/* Affordable: the only state that is actually clickable, so it is the only one
   that lights up and responds to hover. */
.pcard.can{cursor:pointer;border-left-color:var(--cyan)}
.pcard.can .pcard-name{color:#fff}
.pcard.can .pcard-cost{color:var(--cyan);border-color:var(--cyan);background:rgba(0,238,255,.09)}
.pcard.can .pcard-cost img{opacity:1}
.pcard.can:hover{background:rgba(0,238,255,.06)}
.pcard.can:active{background:rgba(0,238,255,.14)}

/* Too expensive: legible, clearly inert. Dimming the whole row made the text
   unreadable, so only the cost chip signals it. */
.pcard.cant .pcard-cost{color:var(--red);border-color:rgba(255,51,85,.35)}

/* Maxed: finished, not broken — keep it readable rather than faded out. */
.pcard.max{opacity:.55}
.pcard.max .pcard-cost{color:var(--grn);border-color:rgba(0,255,136,.3);background:rgba(0,255,136,.06)}

/* Level meter — segment count IS the max, so "how far along" needs no reading. */
.pmeter{display:flex;gap:2px;flex:1}
.pseg{height:3px;flex:1;max-width:14px;background:rgba(255,255,255,.09);transition:background .15s}
.pseg.on{background:var(--cyan)}
.pcard.max .pseg.on{background:var(--grn)}
.plv{font-family:var(--FH);font-size:calc(8px*var(--ts,1));color:var(--mut);letter-spacing:.5px;flex-shrink:0}



/* GAME OVER */
#overlay{display:none;position:fixed;inset:0;z-index:800;background:rgba(3,2,16,.94);align-items:center;justify-content:center}
#overlay.on{display:flex;animation:fadeIn .3s}
/* max-height + overflow-y:auto is a UNIVERSAL safety net: a too-tall game-over box
   (heavy retention text + the 300x250 ad) can otherwise clip its title off the top
   and the ad off the bottom with no scroll — and that happens at ANY viewport short
   enough, including width>=1024 short-landscape (1280x720 laptops) which the
   max-width:1023 landscape rule doesn't cover. overflow-x stays hidden so the ad's
   negative side margins never add a horizontal scrollbar. */
#ov-box{text-align:center;padding:30px 24px;max-width:320px;width:90vw;border:2px solid var(--pur);background:var(--panel);border-radius:0;animation:popIn .3s;box-shadow:0 0 40px rgba(102,51,255,.3),inset 0 0 20px rgba(102,51,255,.05);image-rendering:pixelated;overflow-x:hidden;overflow-y:auto;max-height:94vh;-webkit-overflow-scrolling:touch}
/* Game-over 300x250 ad. Negative side margins reclaim the box padding so the fixed
   300px unit fits inside the 320px modal; empty until the ad injects on first death. */
#ov-ad{margin:16px -15px 0;display:flex;justify-content:center}
#ov-ad:empty{display:none}
.ov-icon{font-size:calc(44px*var(--ts,1));margin-bottom:12px}
.ov-title{font-family:var(--FH);font-size:calc(18px*var(--ts,1));font-weight:900;letter-spacing:3px;margin-bottom:6px}
.ov-sub{font-size:calc(10px*var(--ts,1));color:var(--mut);letter-spacing:1px;line-height:1.8;margin-bottom:18px}
.ov-btn{font-family:var(--FH);font-size:calc(10px*var(--ts,1));letter-spacing:2px;padding:11px 24px;min-height:44px;border:none;cursor:pointer;border-radius:0;display:block;width:100%;margin-bottom:8px;-webkit-tap-highlight-color:transparent}
.ov-btn.pri{background:var(--pur);color:#fff}
.ov-btn.sec{background:transparent;border:1px solid var(--dim);color:var(--mut)}
.ov-btn.daily{background:rgba(0,238,255,.10);border:1px solid var(--cyan);color:var(--cyan);line-height:1.25}
.ov-btn.daily .cta-sub{display:block;font-size:calc(8px*var(--ts,1));letter-spacing:1px;color:#7fd8e6;margin-top:3px;font-family:var(--FM)}
.ov-btn.daily.done{background:transparent;border-color:var(--dim);color:var(--mut);cursor:default}
.ov-btn.daily.done .cta-sub{color:var(--mut)}   /* --dim (#28206a) on --panel was ~1.4:1 — unreadable; --mut is ~5:1 */
/* Two secondary actions side by side (OPERATOR LOG / CODEX). */
.ov-btn-row{display:flex;flex-wrap:wrap;justify-content:center;gap:8px}
.ov-btn-row .ov-btn{flex:1 1 calc(50% - 4px);width:auto;min-width:0;margin-bottom:0;font-size:calc(9px*var(--ts,1));letter-spacing:1px;padding:11px 6px}

/* ── operator (character) select ── */
.char-select{display:none;position:fixed;inset:0;z-index:850;background:rgba(3,2,16,.96);align-items:center;justify-content:center;image-rendering:pixelated}
.char-select.on{display:flex;animation:fadeIn .3s}
.cs-inner{width:94%;max-width:520px;max-height:92vh;overflow-y:auto;padding:18px 16px;text-align:center;-webkit-overflow-scrolling:touch}
/* Header row: title + the HOW TO PLAY / SETTINGS controls. A centered flex row that WRAPS,
   so on a narrow phone the buttons drop to their own line under the title instead of the
   old absolute-pinned buttons overlapping "SELECT OPERATOR". */
.cs-head{position:relative;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:8px 12px;font-family:var(--FH);font-size:calc(15px*var(--ts,1));letter-spacing:4px;color:var(--cyan);margin-bottom:14px}
.cs-head-btns{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:8px;max-width:100%}
/* Settings + HOW TO PLAY entries on the operator-select header. Cyan settings pill (opens the
   body-level sheet) + a gold HELP pill (reopens the how-to intro) — both flex children now. */
.cs-settings-btn{display:inline-flex;align-items:center;gap:5px;background:rgba(0,238,255,.08);border:1px solid var(--cyan);color:var(--cyan);font-family:var(--FM);font-size:calc(9px*var(--ts,1));letter-spacing:1.5px;line-height:1;cursor:pointer;padding:6px 10px;min-height:30px;-webkit-tap-highlight-color:transparent}
.cs-settings-btn>span{font-size:calc(12px*var(--ts,1))}
.cs-settings-btn:hover{background:rgba(0,238,255,.18)}
.cs-settings-btn:active{background:rgba(0,238,255,.28)}
.cs-help-btn{display:inline-flex;align-items:center;gap:5px;background:rgba(255,209,102,.1);border:1px solid var(--gold);color:var(--gold);font-family:var(--FM);font-size:calc(9px*var(--ts,1));letter-spacing:1.5px;line-height:1;cursor:pointer;padding:6px 10px;min-height:30px;-webkit-tap-highlight-color:transparent}
.cs-help-btn>span{font-size:calc(12px*var(--ts,1))}
.cs-help-btn:hover{background:rgba(255,209,102,.2)}
.cs-help-btn:active{background:rgba(255,209,102,.3)}
/* META-SHOP entry on operator-select — a prominent GREEN currency pill so it clearly reads as a
   spendable SHOP (◆ balance + SHOP + ▸ arrow), not a passive number. Draw-only; opens showMetaShop(). */
.cs-shop-btn{display:inline-flex;align-items:center;gap:6px;background:linear-gradient(180deg,rgba(0,255,136,.18),rgba(0,255,136,.05));border:1px solid var(--grn);color:var(--grn);font-family:var(--FH);font-size:calc(10px*var(--ts,1));letter-spacing:2px;line-height:1;cursor:pointer;padding:7px 13px;min-height:32px;box-shadow:0 0 12px rgba(0,255,136,.22);-webkit-tap-highlight-color:transparent;animation:shopGlow 2.6s ease-in-out infinite}
.cs-shop-btn b{font-family:var(--FM);font-size:calc(13px*var(--ts,1));color:#8affc0;letter-spacing:1px}
.cs-shop-arrow{font-size:calc(13px*var(--ts,1))}
.cs-shop-btn:hover{background:linear-gradient(180deg,rgba(0,255,136,.3),rgba(0,255,136,.12));box-shadow:0 0 18px rgba(0,255,136,.34)}
.cs-shop-btn:active{transform:translateY(1px)}
@keyframes shopGlow{0%,100%{box-shadow:0 0 10px rgba(0,255,136,.16)}50%{box-shadow:0 0 18px rgba(0,255,136,.34)}}
@media (prefers-reduced-motion:reduce){.cs-shop-btn{animation:none}}
.cs-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:8px}
.cs-card{background:rgba(255,255,255,.02);border:1px solid var(--line);border-radius:0;padding:8px 6px;cursor:pointer;color:#ccd6ff;font-family:'Share Tech Mono',monospace;text-align:center;transition:border-color .12s,background .12s}
.cs-card:active{transform:scale(.98)}
.cs-card.sel{border-color:var(--cc);box-shadow:inset 0 0 0 2px var(--cc)}
.cs-card.locked{opacity:.5;cursor:default}
.cs-port{width:40px;height:40px;margin:0 auto 6px;display:flex;align-items:center;justify-content:center;font-family:var(--FH);font-size:calc(20px*var(--ts,1));font-weight:900;border:1px solid rgba(255,255,255,.15);image-rendering:pixelated;position:relative;overflow:hidden}
.cs-port-img,.dlg-port-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;image-rendering:auto;display:block}
.cs-name{font-family:var(--FH);font-size:calc(10px*var(--ts,1));letter-spacing:1.5px;margin-bottom:2px}
.cs-title{font-size:calc(8px*var(--ts,1));color:var(--mut);letter-spacing:.5px;margin-bottom:5px}
.cs-passive{font-size:calc(8px*var(--ts,1));color:var(--cyan);letter-spacing:.3px;line-height:1.4;min-height:22px}
.cs-card.locked .cs-passive{color:var(--gold)}
.cs-quote{font-size:calc(10px*var(--ts,1));color:#88ffaa;font-style:italic;margin:14px 4px 12px;min-height:16px;letter-spacing:.5px}
.cs-start{font-family:var(--FH);font-size:calc(12px*var(--ts,1));letter-spacing:3px;padding:13px 24px;min-height:44px;width:100%;border:none;border-radius:0;background:var(--pur);color:#fff;cursor:pointer}
.cs-start:active{transform:scale(.99)}
.cs-modhead{font-family:var(--FH);font-size:calc(9px*var(--ts,1));letter-spacing:2px;color:var(--mut);margin:2px 4px 8px;text-align:center}
.cs-mods{display:grid;grid-template-columns:repeat(auto-fill,minmax(138px,1fr));gap:6px;margin-bottom:14px}
.mod-chip{display:flex;flex-direction:column;gap:3px;padding:8px 10px;border:1px solid var(--line);background:rgba(255,255,255,.02);color:#ccd6ff;cursor:pointer;text-align:left;min-width:0}
.mod-chip .mod-ds{overflow-wrap:anywhere}
.mod-chip.sel{border-color:var(--mc);box-shadow:inset 0 0 14px -7px var(--mc),inset 0 0 0 1px var(--mc)}
.mod-nm{font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:1px;color:var(--mc)}
.mod-ds{font-size:calc(9px*var(--ts,1));color:var(--mut);line-height:1.3}
.mod-chip:active{transform:scale(.99)}

/* ── OPERATOR LOG (stats) + CODEX (records) — full-screen DOM overlays ── */
.log-overlay{display:none;position:fixed;inset:0;z-index:860;background:rgba(3,2,16,.97);align-items:flex-start;justify-content:center;image-rendering:pixelated;-webkit-tap-highlight-color:transparent}
.log-overlay.on{display:flex;animation:fadeIn .25s}
.log-inner{width:94%;max-width:480px;max-height:100vh;overflow-y:auto;-webkit-overflow-scrolling:touch;
  padding:calc(20px + env(safe-area-inset-top)) 16px calc(24px + env(safe-area-inset-bottom));text-align:left}
.log-head{font-family:var(--FH);font-size:calc(16px*var(--ts,1));letter-spacing:4px;color:var(--cyan);text-align:center;margin-bottom:16px}
.log-sect{font-family:var(--FH);font-size:calc(10px*var(--ts,1));letter-spacing:3px;color:var(--gold);margin:18px 0 8px;padding-bottom:4px;border-bottom:1px solid var(--line)}
.log-row{display:flex;justify-content:space-between;align-items:baseline;gap:12px;padding:5px 2px;font-family:'Share Tech Mono',monospace;border-bottom:1px solid rgba(255,255,255,.04)}
.log-k{font-size:calc(10px*var(--ts,1));letter-spacing:1px;color:var(--mut)}
.log-v{font-family:var(--FH);font-size:calc(12px*var(--ts,1));color:#ccd6ff;text-align:right}
.log-ms{display:flex;align-items:center;gap:8px;padding:6px 2px;font-family:'Share Tech Mono',monospace;font-size:calc(10px*var(--ts,1));opacity:.55}
.log-ms.done{opacity:1}
.log-ms-box{font-family:var(--FH);font-size:calc(12px*var(--ts,1));color:var(--mut);width:16px;text-align:center}
.log-ms.done .log-ms-box{color:var(--cyan)}
.log-ms-t{flex:1;letter-spacing:.5px;color:#ccd6ff}
.log-ms-r{font-family:var(--FH);font-size:calc(9px*var(--ts,1));letter-spacing:1px}
.cx-entry{padding:8px 2px;border-bottom:1px solid rgba(255,255,255,.04)}
.cx-t{font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:1.5px;margin-bottom:3px}
.cx-sub{font-size:calc(8px*var(--ts,1));color:var(--mut);letter-spacing:1px}
.cx-d{font-family:'Share Tech Mono',monospace;font-size:calc(10px*var(--ts,1));color:#9fb0d8;line-height:1.5}
.cx-line{font-family:'Share Tech Mono',monospace;font-size:calc(10px*var(--ts,1));color:#9fb0d8;line-height:1.5;padding:4px 2px}
.cx-line b{color:var(--gold);margin-right:5px}
.log-close{font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:3px;margin-top:22px;padding:13px 24px;min-height:44px;width:100%;border:1px solid var(--dim);border-radius:0;background:transparent;color:var(--mut);cursor:pointer;-webkit-tap-highlight-color:transparent}
.log-close:active{transform:scale(.99)}

/* ── first-run tutorial (pure overlay, shown once; never touches the sim) ── */
.tutorial{display:none;position:fixed;inset:0;z-index:900;background:rgba(3,2,16,.97);align-items:center;justify-content:center;cursor:pointer;image-rendering:pixelated;-webkit-tap-highlight-color:transparent}
.tutorial.on{display:flex;animation:fadeIn .25s}
.tut-inner{width:90%;max-width:440px;padding:24px 18px;text-align:center}
.tut-count{font-family:var(--FH);font-size:calc(10px*var(--ts,1));letter-spacing:3px;color:var(--mut);margin-bottom:20px}
.tut-title{font-family:var(--FH);font-size:calc(15px*var(--ts,1));line-height:1.5;letter-spacing:2px;color:var(--cyan);margin-bottom:12px}
.tut-sub{font-family:'Share Tech Mono',monospace;font-size:calc(11px*var(--ts,1));letter-spacing:.5px;color:#ccd6ff;margin-bottom:28px}
.tut-hint{font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:3px;color:var(--gold);animation:tutPulse 1.1s ease-in-out infinite}
@keyframes tutPulse{0%,100%{opacity:.45}50%{opacity:1}}

/* ── wave / boss overlays (draw-only) ── */
.boss-bar{display:none;position:fixed;top:calc(78px + env(safe-area-inset-top));left:50%;transform:translateX(-50%);width:88%;max-width:440px;z-index:180;pointer-events:none;text-align:center}
.boss-bar.on{display:block;animation:fadeIn .25s}
.bb-name{font-family:var(--FH);font-size:calc(10px*var(--ts,1));letter-spacing:2px;color:var(--bc,#ff3355);margin-bottom:3px;text-shadow:0 0 6px rgba(0,0,0,.85)}
.bb-track{height:8px;background:rgba(0,0,0,.6);border:1px solid rgba(255,255,255,.25)}
.bb-fill{height:100%;width:100%;background:#ff3355;transition:width .15s linear}
/* pointer-events:none makes the breather non-blocking — taps fall straight through
   to the radar so the player is never gated by the overlay while it counts down. */
.wave-clear{display:none;position:fixed;inset:0;z-index:190;pointer-events:none;align-items:center;justify-content:center}
.wave-clear.on{display:flex;animation:fadeIn .25s}
.wc-inner{background:rgba(3,2,16,.82);border:2px solid var(--pur);padding:22px 30px;text-align:center;image-rendering:pixelated}
.wc-title{font-family:var(--FH);font-size:calc(18px*var(--ts,1));letter-spacing:3px;color:var(--cyan);margin-bottom:4px}
.wc-chapter{font-size:calc(9px*var(--ts,1));letter-spacing:2px;color:var(--mut);margin-bottom:12px}
.wc-next{font-size:calc(10px*var(--ts,1));letter-spacing:1px;color:#ccd6ff;margin-bottom:10px}
.wc-next b{color:var(--gold)}
.wc-count{font-family:var(--FH);font-size:calc(34px*var(--ts,1));color:var(--purL)}
.boss-intro{display:none;position:fixed;top:32%;left:50%;transform:translateX(-50%);z-index:195;pointer-events:none;text-align:center}
.boss-intro.on{display:block;animation:fadeIn .2s}
.bi-warn{font-family:var(--FH);font-size:calc(12px*var(--ts,1));letter-spacing:4px;color:var(--red);margin-bottom:6px;animation:blink 1s infinite;text-shadow:0 0 8px rgba(0,0,0,.9)}
.bi-name{font-family:var(--FH);font-size:calc(22px*var(--ts,1));letter-spacing:3px;text-shadow:0 0 10px rgba(0,0,0,.9)}
.radar-jam{display:none;position:fixed;top:calc(182px + env(safe-area-inset-top));left:50%;transform:translateX(-50%);z-index:185;pointer-events:none;font-family:var(--FH);font-size:calc(9px*var(--ts,1));letter-spacing:2px;color:#aa66ff;text-shadow:0 0 6px rgba(0,0,0,.9)}
.radar-jam.on{display:block;animation:blink .6s infinite}
.event-fx{display:none;position:fixed;inset:0;z-index:170;pointer-events:none}
.event-fx.on{display:block;animation:fadeIn .3s}
.ef-name{position:absolute;top:calc(158px + env(safe-area-inset-top));left:50%;transform:translateX(-50%);font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:3px;white-space:nowrap;text-shadow:0 0 8px rgba(0,0,0,.9);animation:blink 1.2s infinite}

/* ── RPG dialogue box + boss quote (draw-only) ── */
/* Fully tappable: the whole box dismisses (JS binds click+touchstart on it), and a
   generous min-height keeps the tap target comfortable on touch. Cleared of the notch
   via the top safe-area inset. */
.dialogue-box{position:fixed;top:calc(78px + env(safe-area-inset-top));left:50%;transform:translateX(-50%);width:90%;max-width:360px;min-height:44px;background:rgba(3,2,15,.92);border:2px solid var(--pur);padding:10px;display:flex;align-items:flex-start;gap:10px;z-index:200;font-family:'Share Tech Mono',monospace;animation:dlgSlideIn .2s ease-out;image-rendering:pixelated;cursor:pointer;-webkit-tap-highlight-color:transparent}
.dlg-portrait{width:44px;height:44px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-family:var(--FH);font-size:calc(22px*var(--ts,1));color:#04040a;font-weight:900;border:1px solid rgba(255,255,255,.2);image-rendering:pixelated;position:relative;overflow:hidden}
.dlg-content{flex:1;min-width:0}
.dlg-name{font-family:var(--FH);font-size:calc(10px*var(--ts,1));letter-spacing:2px;margin-bottom:4px}
.dlg-text{font-size:calc(11px*var(--ts,1));color:#ccd6ff;line-height:1.45}
.dlg-tap{font-size:calc(8px*var(--ts,1));color:rgba(255,255,255,.3);align-self:flex-end;letter-spacing:1px;animation:blink 1s infinite}
@keyframes dlgSlideIn{from{opacity:0;transform:translateX(-50%) translateY(-10px)}}
/* During a boss fight the dialogue drops below the boss HP bar so HP stays visible. */
.dialogue-box.below-boss{top:calc(107px + env(safe-area-inset-top))}
.boss-quote{position:fixed;top:42%;left:50%;transform:translateX(-50%);z-index:196;pointer-events:none;font-family:var(--FH);font-size:calc(16px*var(--ts,1));letter-spacing:2px;color:#fff;text-align:center;width:90%;max-width:400px;text-shadow:0 0 12px rgba(255,255,255,.45);animation:fadeIn .3s}

/* INTERSTITIAL */
#inter{display:none;position:fixed;inset:0;z-index:9999;background:rgba(3,2,16,.97);align-items:center;justify-content:center}
#inter.on{display:flex;animation:fadeIn .2s}
.i-wrap{width:min(380px,92vw);background:var(--panel);border:1px solid var(--pur);box-shadow:0 0 60px rgba(102,51,255,.2);overflow:hidden}
.i-head{display:flex;align-items:center;justify-content:space-between;padding:8px 14px;background:rgba(102,51,255,.08);border-bottom:1px solid var(--line)}
.i-lbl{font-size:calc(9px*var(--ts,1));color:var(--mut);letter-spacing:2px;font-family:var(--FH)}
.i-skip{font-size:calc(10px*var(--ts,1));padding:4px 10px;cursor:pointer;font-family:var(--FM);border:1px solid var(--dim);background:transparent;color:var(--mut)}
.i-skip.ready{color:var(--purL);border-color:var(--purL)}
.i-prog{height:2px;background:var(--dim)}
.i-prog-fill{height:100%;background:var(--pur);width:100%;transition:width 5s linear}
.i-body{height:260px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;background:radial-gradient(ellipse at 50% 40%,#0d0525,#030210 70%);position:relative}
.i-foot{font-size:calc(8px*var(--ts,1));color:var(--mut);text-align:center;padding:5px 0;border-top:1px solid var(--line);letter-spacing:1px}

/* CHAPTER-CLEAR beat — a full-screen milestone shown when a boss falls (below the
   game-over overlay z-800; nothing else is up during the sim-held pause). */
/* META-SHOP inner card is built inline in game.js (showMetaShop) with no max-height, so at
   large text scale on a short phone its rows + DONE button ran off the bottom with no way to
   scroll. Cap + scroll it here without touching game.js (child-of-id selector, no re-gate).
   (100vh - 44px) leaves room inside the container's 20px padding so centering never clips the top. */
#meta-shop > div{max-height:calc(100vh - 44px);overflow-y:auto;-webkit-overflow-scrolling:touch}

#chapter-clear{display:none;position:fixed;inset:0;z-index:790;background:rgba(3,2,16,.94);align-items:center;justify-content:center;padding:20px}
#chapter-clear.on{display:flex;animation:fadeIn .3s}
/* max-height + scroll so the chapter-clear beat (badge/title/stats + the 1-of-3 upgrade
   DRAFT) can never clip its cards off a short viewport at large text scale — the draft
   cards must stay reachable or the player is stuck. (100vh - 44px) accounts for the
   container's 20px padding both sides so a centered over-tall panel isn't top-clipped. */
.cc-inner{text-align:center;max-width:360px;width:92vw;max-height:calc(100vh - 44px);overflow-y:auto;-webkit-overflow-scrolling:touch;image-rendering:pixelated;animation:popIn .3s}
.cc-badge{display:inline-block;font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:3px;padding:4px 12px;border:1px solid var(--purL);margin-bottom:14px}
.cc-title{font-family:var(--FH);font-size:calc(26px*var(--ts,1));letter-spacing:6px;color:#fff;margin-bottom:6px}
.cc-name{font-family:var(--FH);font-size:calc(12px*var(--ts,1));letter-spacing:2px;margin-bottom:18px}
.cc-stats{display:flex;justify-content:center;flex-wrap:wrap;gap:8px 16px;font-family:var(--FM);font-size:calc(11px*var(--ts,1));color:var(--mut);letter-spacing:1px;margin-bottom:16px}
.cc-next{font-size:calc(11px*var(--ts,1));color:#ccd6ff;letter-spacing:1px;margin-bottom:22px}
.cc-btn{font-family:var(--FH);font-size:calc(12px*var(--ts,1));letter-spacing:3px;padding:13px 30px;min-height:46px;border:none;border-radius:0;background:var(--pur);color:#fff;cursor:pointer;-webkit-tap-highlight-color:transparent}
.cc-btn:active{transform:scale(.98)}
.cc-claim{display:inline-block;font-family:var(--FH);font-size:calc(12px*var(--ts,1));letter-spacing:2px;padding:12px 26px;min-height:44px;margin-bottom:10px;border:1px solid var(--gold);border-radius:0;background:rgba(255,204,0,.12);color:var(--gold);cursor:pointer;-webkit-tap-highlight-color:transparent}
.cc-claim:active{transform:scale(.98)}
.cc-claim.claimed,.cc-claim[disabled]{border-color:var(--grn);background:rgba(0,255,136,.08);color:var(--grn);cursor:default}

/* ── LEADERBOARD ────────────────────────────────────────── */
#board-head{flex-shrink:0;padding:10px 12px;background:var(--panel);border-bottom:2px solid var(--line)}
.bh-row{display:flex;justify-content:space-between;align-items:flex-start;gap:8px}
.bh-title{font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:2px;color:var(--cyan)}
.bh-season{font-family:var(--FM);font-size:calc(8px*var(--ts,1));color:var(--mut);margin-top:2px}
.bh-timer{font-family:var(--FH);font-size:calc(13px*var(--ts,1));color:var(--gold);letter-spacing:1px;text-align:right}
.bh-timer-lbl{font-size:calc(7px*var(--ts,1));color:var(--mut);letter-spacing:1px;text-align:right;margin-top:1px}
/* Ranked / Auto board switch. */
.bd-tabs{display:flex;gap:4px;margin-top:8px}
.bd-tab{flex:1;padding:6px;background:transparent;border:1px solid var(--line);color:var(--mut);font-family:var(--FH);font-size:calc(8px*var(--ts,1));letter-spacing:1.5px;cursor:pointer;border-radius:2px}
.bd-tab.on{border-color:var(--cyan);color:var(--cyan);background:rgba(0,238,255,.06)}
.bd-note{margin-top:8px;font-size:calc(8px*var(--ts,1));color:var(--mut);letter-spacing:.5px;line-height:1.6;text-align:center}
.bd-daily-play{width:100%;margin-top:8px;padding:11px;font-size:calc(11px*var(--ts,1));letter-spacing:2px}
.bd-flair{font-family:var(--FH);font-size:calc(6.5px*var(--ts,1));letter-spacing:1px;padding:1px 4px;margin-left:6px;border-radius:2px;border:1px solid currentColor;vertical-align:middle;white-space:nowrap}
.bd-flair.f1{color:#7fae7f}
.bd-flair.f2{color:#00eeff}
.bd-flair.f3{color:#ffcc55}
.tut-guide{display:none;position:fixed;left:50%;bottom:calc(96px + env(safe-area-inset-bottom));transform:translateX(-50%);z-index:210;pointer-events:none;width:320px;max-width:88%;text-align:center;background:rgba(6,4,26,.95);border:1px solid var(--cyan);border-radius:3px;padding:10px 14px;font-size:calc(11px*var(--ts,1));color:#ccd6ff;line-height:1.5;box-shadow:0 0 22px -6px var(--cyan)}
.tut-guide.on{display:block;animation:fadeIn .3s}
.tut-guide b{color:var(--cyan);font-family:var(--FH);font-size:calc(10px*var(--ts,1));letter-spacing:1.5px}

/* ── leaderboard callsign ── */
.callsign-btn{width:100%;margin-top:8px;padding:8px;background:transparent;border:1px solid var(--purL);color:var(--purL);font-family:var(--FH);font-size:calc(9px*var(--ts,1));letter-spacing:2px;cursor:pointer;border-radius:0;min-height:36px}
.callsign-btn:active{background:rgba(153,102,255,.15)}
.callsign{display:none;position:fixed;inset:0;z-index:860;background:rgba(3,2,16,.96);align-items:center;justify-content:center;padding:20px}
.callsign.on{display:flex;animation:fadeIn .2s}
.cn-inner{width:100%;max-width:340px;background:var(--panel);border:2px solid var(--pur);padding:20px;text-align:center}
.cn-head{font-family:var(--FH);font-size:calc(13px*var(--ts,1));letter-spacing:3px;color:var(--cyan);margin-bottom:5px}
.cn-sub{font-size:calc(8px*var(--ts,1));color:var(--mut);letter-spacing:.5px;margin-bottom:14px}
.cn-input{width:100%;padding:11px;background:rgba(0,0,0,.4);border:1px solid var(--dim);color:var(--txt);font-family:var(--FH);font-size:calc(14px*var(--ts,1));letter-spacing:2px;text-align:center;border-radius:0;outline:none}
.cn-input:focus{border-color:var(--cyan)}
.cn-msg{min-height:14px;font-size:calc(9px*var(--ts,1));color:var(--red);letter-spacing:.5px;margin:8px 0}
.cn-set{width:100%;padding:12px;font-family:var(--FH);font-size:calc(11px*var(--ts,1));letter-spacing:3px;background:var(--pur);color:#fff;border:none;border-radius:0;cursor:pointer;min-height:44px}
.cn-set:disabled{opacity:.6;cursor:default}
.cn-cancel{width:100%;margin-top:8px;padding:9px;font-family:var(--FH);font-size:calc(9px*var(--ts,1));letter-spacing:2px;background:transparent;border:1px solid var(--dim);color:var(--mut);border-radius:0;cursor:pointer;min-height:36px}
#board-list{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch}
.bd-row{display:flex;align-items:center;gap:8px;padding:7px 12px;border-bottom:1px solid var(--line)}
.bd-row.me{background:rgba(0,238,255,.07);box-shadow:inset 2px 0 0 var(--cyan)}
.bd-rank{font-family:var(--FH);font-size:calc(11px*var(--ts,1));color:var(--mut);width:30px;flex-shrink:0}
.bd-row.me .bd-rank{color:var(--cyan)}
.bd-rank.top1{color:var(--gold)}
.bd-rank.top2{color:#c0c8e0}
.bd-rank.top3{color:#cd7f32}
.bd-handle{flex:1;font-family:var(--FM);font-size:calc(10px*var(--ts,1));color:var(--txt);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bd-score{font-family:var(--FH);font-size:calc(11px*var(--ts,1));color:var(--gold);flex-shrink:0}
.bd-wave{font-family:var(--FM);font-size:calc(8px*var(--ts,1));color:var(--mut);width:34px;text-align:right;flex-shrink:0}
.bd-msg{padding:24px 16px;text-align:center;font-size:calc(9px*var(--ts,1));color:var(--mut);letter-spacing:1px;line-height:1.9}

/* ── DESKTOP LAYOUT ─────────────────────────────────────────
   Browser-first: the radar goes large and central, with upgrades and the
   leaderboard as permanent side columns. Tabbing between them wasted the screen
   and hid the leaderboard — the one thing a competitive player wants in view
   while playing.

   Must sit near the end of the file: these override base rules of equal
   specificity, and media queries add none of their own. */
@media (min-width:1024px){
  body{background:radial-gradient(ellipse at 50% 20%,#0a0620,#020109 70%)}

  #stage{flex-direction:row;align-items:stretch;gap:0}

  /* DOM order is radar-first (narrow screens want it first); desktop reorders. */
  #panel-upg  {order:1}
  #radar-wrap {order:2}
  #panel-board{order:3}

  /* Side columns are fixed-width so the radar keeps a stable square-ish box —
     a radar that resizes as panels reflow would change tap geometry mid-run. */
  #panel-upg,#panel-board{
    flex:0 0 300px;width:300px;min-height:0;
    display:flex;flex-direction:column;
    background:var(--panel);
  }
  #panel-upg{border-right:1px solid var(--line);overflow-y:auto}
  #panel-board{border-left:1px solid var(--line)}

  #radar-wrap{flex:1 1 auto;min-width:0}

  /* The bottom tab bar only switches between views; on desktop every view is on
     screen at once, so the whole bar is redundant. */
  #nav-bar{display:none}

  /* Bigger hit targets and type now that there is room for them. */
  #score-val{font-size:calc(20px*var(--ts,1))}
  .tb-cur{font-size:calc(13px*var(--ts,1))}
  .shield{font-size:calc(18px*var(--ts,1))}
  #weapon-bar{height:52px}
  .wb-btn{font-size:calc(11px*var(--ts,1))}
  #controls{height:46px}
  .bd-row{padding:9px 12px}
  .bd-handle{font-size:calc(11px*var(--ts,1))}
  .bd-score{font-size:calc(12px*var(--ts,1))}

  /* ── things that duplicate something else on desktop ──
     Every panel is already on screen, and the shard counter in the top bar is
     itself the "watch ad" button — so the whole nav row was one redundant
     control taking a full 48px strip away from the radar. */
  #nav-bar{display:none}
  /* ...but the nav's gear tab was the ONLY Settings entry, so surface a gear in the
     top bar on desktop (the bottom nav, gear included, is hidden above). */
  #tb-settings{display:inline-flex}

  /* The status strip floated over the radar repeating KILLS / BEST LV / WEAPON.
     Weapon is already shown by the highlighted weapon button, and the other two
     now live in the top bar — off the play area, where they belong. */
  #status-bar{display:none}

  /* The floating upgrade strip duplicates the left column. */
  #upg-strip{display:none !important}

  .ctrl-lbl{display:inline}

  /* Stats in the top bar (desktop only). */
  #tb-stats{display:flex;gap:22px;align-items:center}
  .tb-stat{display:flex;flex-direction:column;align-items:center;gap:1px}
  .tb-stat-val{font-family:var(--FH);font-size:calc(15px*var(--ts,1));color:var(--purL);line-height:1}
  .tb-stat-lbl{font-size:calc(7px*var(--ts,1));color:var(--mut);letter-spacing:1.5px}

  /* ── Center the mobile-first modals on desktop ──────────────────────────
     These overlays are authored mobile-first: the SETTINGS sheet slides up
     from the bottom (align-items:flex-end) and the log overlays (stats /
     codex / goals) pin to the TOP so long lists scroll from the first row.
     On a tall desktop window that strands them against an edge — and the
     settings sheet, now taller with the keybind section, ran its DONE button
     off the bottom of the screen. Center both here (the panels keep their own
     max-height + internal scroll, so nothing clips). Mobile is untouched —
     this lives inside the >=1024px query only. */
  #settings-sheet{align-items:center}
  .ss-panel{border:2px solid var(--pur);max-height:88vh}   /* full border reads as a card, not a sheet */
  .log-overlay{align-items:center}
  .log-inner{max-height:92vh}
}

/* ── LANDSCAPE / SHORT-VIEWPORT LAYOUT ───────────────────────
   MUST stay at the end of the file. Media queries carry no extra specificity, so
   these overrides lose to the same-specificity rules above if placed earlier —
   which is exactly what happened once: the query matched and nothing applied.

   The portrait stack gives the radar whatever height is left after topbar + diff
   + weapon + controls + nav + banner — in a short landscape window that is a tiny
   sliver. So below the desktop breakpoint, landscape gets a GRID instead: the
   radar takes the full column height on the LEFT, and every strip of chrome moves
   into a narrow rail on the RIGHT. The radar stays a circle of R=min(W,H)/2 (see
   game.js draw), so a full-height square-ish cell gives the biggest playable board
   and the tap geometry is identical to portrait — only the surrounding chrome
   moved. This is what makes the game render at CrazyGames' 907x510 / 821x462 iframe
   sizes (portable landscape phones benefit too) instead of a dead rotate screen. */
#rotate-prompt{display:none}
@media (orientation:landscape) and (max-width:1023px){
  #game{
    display:grid;
    grid-template-columns:1fr clamp(174px,26vw,290px);
    grid-template-rows:auto auto auto auto 1fr auto;
    grid-template-areas:
      "radar top"
      "radar diff"
      "radar weap"
      "radar ctrl"
      "radar ."
      "radar nav";
  }
  #stage    {grid-area:radar;min-width:0;min-height:0}
  #topbar   {grid-area:top;flex-wrap:wrap;border-left:1px solid var(--line)}
  #diff-bar {grid-area:diff;border-left:1px solid var(--line)}
  #weapon-bar{grid-area:weap;border-left:1px solid var(--line)}
  #controls {grid-area:ctrl;border-left:1px solid var(--line)}
  #nav-bar  {grid-area:nav;border-top:2px solid var(--line);border-left:2px solid var(--line)}
  /* The banner would eat the rail's height; drop it in short landscape (it is off
     entirely on the CrazyGames build, and this is an edge size on the live site). */
  #banner-ad{display:none}

  /* OPERATOR-SELECT in landscape: the portrait panel scrolls (START MISSION ends up
     below the fold) and wastes the width. Reflow .cs-inner into TWO columns — the
     operator cards on the LEFT, quote + modifier chips + START stacked on the RIGHT
     — so everything fits without scrolling and the primary button is always visible.
     grid-template-areas places the flat siblings into cells regardless of DOM order
     (no markup change, so game.js stays byte-identical). */
  .cs-inner{
    width:96%;max-width:920px;max-height:96vh;
    display:grid;
    grid-template-columns:1.4fr 1fr;
    grid-template-areas:
      "head    head"
      "ops     quote"
      "ops     modhead"
      "ops     mods"
      "ops     start";
    grid-template-rows:auto auto auto auto 1fr;
    /* align-content:START, not center: the panel keeps the base overflow-y:auto, and a
       CENTERED over-tall grid pushes its overflow off BOTH ends (top rows then become
       unreachable — you can only scroll down). At the real sizes the panel is exactly
       content-height so start looks identical to center; it only matters when content
       exceeds 96vh in the ~341-370px band just above the rotate cutoff. */
    column-gap:24px;text-align:left;align-content:start;
  }
  .cs-head   {grid-area:head;margin:0 0 12px;text-align:center}
  /* Pin the counts so neither grid reflows to a taller shape as the column narrows
     — auto-fill would drop operators to 3-wide (3 rows) and mods to 1-wide (6 rows)
     on short phones, pushing START back below the fold. Fixed 4×2 ops + 2×3 mods
     stays compact from ~600px up. minmax(0,1fr) lets cards shrink without overflow. */
  .cs-grid   {grid-area:ops;align-self:center;grid-template-columns:repeat(4,minmax(0,1fr))}
  /* Narrow 4-col cells (~70px on small phones) are thinner than the longest operator
     name ("ARCHITECT", ~80px). Tighten tracking and let it wrap so the label never
     spills past its card into the gutter. */
  .cs-grid .cs-name{letter-spacing:.5px;overflow-wrap:anywhere}
  .cs-quote  {grid-area:quote;margin:0 2px 8px;text-align:center}
  .cs-modhead{grid-area:modhead;margin:0 2px 8px;text-align:left}
  .cs-mods   {grid-area:mods;margin:0 0 10px;align-self:start;grid-template-columns:repeat(2,minmax(0,1fr))}
  /* Guarantee a 44px tap target (the chips are otherwise only as tall as their text,
     which can dip under 44px with fallback-font metrics) and even out the 2×3 grid. */
  .cs-mods .mod-chip{min-height:44px;justify-content:center}
  .cs-start  {grid-area:start;align-self:start}

  /* GAME-OVER in landscape: the vertical stack (icon + GAME OVER + score/retention,
     then PLAY AGAIN + CHANGE OPERATOR + a 3-button row + a 300x250 ad) is ~620px
     tall — well past a short landscape viewport — and #ov-box has overflow:hidden
     with no scroll, so the title clips off the top and the ad off the bottom,
     unreachable. Split into two columns: the run SUMMARY (icon/title/score) on the
     LEFT, the ACTIONS on the RIGHT. The .ov-left/.ov-right wrappers are plain blocks
     in portrait (identical stack) and become the two flex columns only here. Drop
     the 300x250 ad: it can't fit a short viewport, it is a net-new surface only
     reachable since landscape became playable, and it is off on the CrazyGames build. */
  #ov-box{
    max-width:660px;width:94%;padding:18px 24px;
    display:flex;flex-direction:row;align-items:flex-start;gap:26px;
    text-align:center;
  }
  #ov-box .ov-left {flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center;text-align:center}
  #ov-box .ov-right{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center}
  #ov-box .ov-sub{margin-bottom:0}
  #ov-ad{display:none}

  /* UPGRADE panel: the WEAPONS | UPGRADES lists are content-width (~106 | ~221) and
     hug the left of the wide #stage column, wasting the right half in landscape. Let
     them GROW to fill it (flex-basis:0 so width is purely the grow ratio); UPGRADES
     kept wider (1.5x) to match its denser content, and the roomier split also
     de-cramps the tight WEAPONS column. min-width:0 lets them shrink w/o overflow.
     Portrait/desktop untouched (this is inside the landscape query; desktop stacks). */
  #panel-upg{flex-direction:row}
  #weapon-list{flex:1 1 0;min-width:0}
  #perma-list {flex:1.5 1 0;min-width:0;border-left:1px solid var(--line)}
}

/* Genuinely tiny landscape slivers (old/small phones): a radar circle under ~340px
   is unreadable no matter the layout. This sits AFTER the grid rule so it wins for
   those few sizes and asks for a rotate — honest beats unplayable. */
@media (orientation:landscape) and (max-height:340px){
  /* Hide the operator-select AND game-over too (not just #game): the opaque z-9999
     rotate prompt already paints over them, but hiding them outright keeps them from
     being occluded-but-interactive layers and survives any future z-order change. */
  #game,#char-select,#overlay{display:none}
  #rotate-prompt{
    display:flex;position:fixed;inset:0;z-index:9999;
    flex-direction:column;align-items:center;justify-content:center;gap:14px;
    background:var(--bg);text-align:center;padding:20px;
  }
  .rp-icon{font-family:var(--FH);font-size:calc(38px*var(--ts,1));color:var(--purL);animation:pulse 2s infinite;line-height:1}
  .rp-title{font-family:var(--FH);font-size:calc(13px*var(--ts,1));letter-spacing:3px;color:var(--cyan)}
  .rp-sub{font-size:calc(9px*var(--ts,1));color:var(--mut);letter-spacing:1px;line-height:1.8;max-width:280px}
}