/* ============================================================
   PlugIdle — Retro CRT edition.
   Green/amber phosphor, scanlines, pixel fonts. Mobile-first.
   ============================================================ */
:root{
  --bg:#080b10; --bg2:#0b1018; --panel:#0e141f; --panel2:#111a28;
  --line:#16241d; --line2:#1c2b3a;
  --green:#46f07a; --green-dim:#2a9a52;
  --amber:#ffc24d; --cyan:#54dcff; --magenta:#ff63d6; --red:#ff6a6a;
  --text:#c9f0d4; --dim:#7a9485; --ink:#04070b;
  --pixel:'Press Start 2P', monospace;
  --term:'VT323', 'Courier New', monospace;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --r:12px; --r-sm:8px;   /* corner radii: cards/blocks vs small controls */
}
*{ box-sizing:border-box; -webkit-tap-highlight-color:transparent; touch-action:manipulation; }
html,body{ margin:0; height:100%; overscroll-behavior:none; }
body{
  background:var(--bg); color:var(--text); font-family:var(--term);
  overflow:hidden; user-select:none; -webkit-user-select:none;
  font-size:20px; line-height:1.05;
}

/* ---------- CRT overlays ---------- */
.crt::after{
  content:""; position:fixed; inset:0; pointer-events:none; z-index:60;
  background:repeating-linear-gradient(0deg,rgba(0,0,0,0) 0,rgba(0,0,0,0) 2px,rgba(0,0,0,.14) 3px,rgba(0,0,0,0) 4px);
  mix-blend-mode:multiply;
}
.crt::before{
  content:""; position:fixed; inset:0; pointer-events:none; z-index:61;
  background:radial-gradient(ellipse at center, rgba(0,0,0,0) 62%, rgba(0,0,0,.38) 100%);
}
#flicker{ position:fixed; inset:0; pointer-events:none; z-index:59; background:var(--green); opacity:0; mix-blend-mode:overlay; animation:flick 7s infinite; }
@keyframes flick{ 0%,96%,100%{opacity:0} 97%{opacity:.03} 98%{opacity:.015} }
/* Animations toggle / reduced-motion: kill the moving CRT FX */
body.noanim #flicker, body.noanim.crt::after{ display:none; }
body.noanim .surge{ animation:none; }
@media (prefers-reduced-motion: reduce){
  /* Replace motion, keep the look: drop the flicker, surge pulse, and tap ring
     (transform/opacity churn = vestibular triggers); static scanlines stay. */
  #flicker{ animation:none; display:none; }
  .surge{ animation:none; }
  .tapping .pulse{ animation:none; }
  .socket-btn.autotap-fire .pulse{ animation:none; }
  .autotap-badge{ animation:none; opacity:.85; }
  .float{ animation-duration:.4s; }
}

/* ---------- App shell ---------- */
#app{
  position:fixed; top:0; left:0; right:0; display:flex; flex-direction:column;
  height:100vh;            /* fallback */
  height:100dvh;           /* track the dynamic viewport so the tab bar stays reachable */
  max-width:520px; margin:0 auto; background:var(--bg2);
  box-shadow:0 0 0 2px var(--line), 0 0 40px #000 inset;
  padding-top:var(--safe-top);
}
/* Tablets / wide screens: widen the column so cord rows have room for their
   flavor descriptions (which fitFlavor then shows). Phones keep the 520px
   column, where most descriptions don't fit and stay hidden. */
@media (min-width: 600px){ #app{ max-width:700px; } }

/* ---------- Header ---------- */
.topbar{ padding:12px 14px 11px; border-bottom:2px solid var(--line); background:linear-gradient(#0a0f18,#0c1119); }
.logo{ font-family:var(--pixel); font-size:13px; color:var(--green); text-shadow:0 0 6px rgba(70,240,122,.4); letter-spacing:1px; text-align:center; }
.logo .b{ color:var(--amber); text-shadow:0 0 6px rgba(255,194,77,.4); }
.stats{ display:flex; justify-content:space-between; align-items:flex-end; margin-top:10px; }
.watts{ font-size:42px; color:var(--green); text-shadow:0 0 12px rgba(70,240,122,.35); line-height:.85; font-variant-numeric:tabular-nums; }
.watts small{ font-size:18px; color:var(--dim); }
.rate{ text-align:right; }
.wps{ font-size:20px; color:var(--cyan); } .wps b{ color:var(--amber); }
.taprate{ font-size:16px; color:var(--dim); margin-top:2px; }
.coresline{ font-size:16px; color:var(--magenta); margin-top:2px; }

/* ---------- Active surge buffs (single-row ticker) ---------- */
.buff-bar{
  display:flex; justify-content:center; align-items:center;
  max-height:0; overflow:hidden; transition:max-height .2s ease, padding .2s ease;
  background:#070b11; border-bottom:2px solid transparent;
}
.buff-bar.show{ max-height:52px; padding:6px 10px; border-bottom-color:var(--line); }
/* a live toast borrows the banner slot: pills collapse, then slide back */
.buff-bar.show.suppressed{ max-height:0; padding:0 10px; border-bottom-color:transparent; }
/* 2+ pills with motion on: scroll like a ticker. Without motion (reduced
   motion / anim off) they stay a single row the user can swipe instead. */
.buff-bar.show:not(.animate){ overflow-x:auto; scrollbar-width:none; }
.buff-bar.show:not(.animate)::-webkit-scrollbar{ display:none; }
.buff-track{ display:flex; align-items:center; flex:0 0 auto; }
.buff-bar.animate .buff-track{ animation:ticker linear infinite; will-change:transform; }
@keyframes ticker{ from{ transform:translateX(0); } to{ transform:translateX(-50%); } }
body.noanim .buff-bar.animate .buff-track{ animation:none; }
@media (prefers-reduced-motion: reduce){ .buff-bar.animate .buff-track{ animation:none; } }
.buff{
  font-family:var(--pixel); font-size:8px; letter-spacing:.5px; padding:6px 11px; margin-right:8px;
  background:linear-gradient(90deg, rgba(84,220,255,.15), rgba(255,194,77,.15));
  border:1px solid var(--amber); border-radius:999px; color:var(--amber); white-space:nowrap;
  text-shadow:0 0 6px rgba(255,194,77,.4); flex:0 0 auto;
}
/* centered single/short row: no trailing gap throwing off the centering */
.buff-bar:not(.animate) .buff-track .buff:last-child{ margin-right:0; }

/* ---------- Main / panels ---------- */
main{ flex:1; overflow:hidden; display:flex; }
.panel{ display:none; width:100%; }
.panel.active{ display:flex; flex-direction:column; flex:1; min-height:0; width:100%; padding:10px 10px 0; }
#p-up.active, #p-goals.active, #p-more.active, #p-surge.active, #p-plug.active,
#p-zap.active, #p-arsenal.active{ overflow-y:auto; padding-bottom:14px; -webkit-overflow-scrolling:touch; }
/* #p-plug / #p-zap scroll as a whole (the big tap button scrolls away, the toolbar
   pins); the cord / weapon list is plain flow content, not its own scroll container.
   flex-shrink:0 keeps the list at full content height: as a shrinkable flex item it
   otherwise collapses the panel's scrollable padding-bottom, so the last card jams
   against the tab bar (the grid panels like #p-up don't hit this). */
#cordlist, #weaponlist{ min-height:0; flex-shrink:0; }
/* No top padding on the tap panels so the sticky toolbar pins flush to the top of
   the scroll area — no sliver above it. The hero (socket-wrap / arena) carries the
   top breathing room instead, as scroll content. */
#p-plug.active, #p-zap.active{ padding-top:0; }
#p-plug .socket-wrap, #p-zap .arena{ margin-top:8px; }
main ::-webkit-scrollbar{ width:5px; } main ::-webkit-scrollbar-thumb{ background:var(--line2); border-radius:3px; }

/* ---------- Socket (PLUG tab) ---------- */
.socket-wrap{ display:flex; flex-direction:column; align-items:center; justify-content:center; padding:4px 0 2px; position:relative; }
.socket-btn{
  width:140px; height:140px; border:none; background:transparent; cursor:pointer; position:relative;
  display:flex; align-items:center; justify-content:center;
}
.socket-btn:active{ transform:translateY(2px) scale(.985); }
.socket-ring{
  position:absolute; inset:0; border-radius:50%;
  background:radial-gradient(circle at 50% 42%, #16331f, #0a1a10 70%, #060d08 100%);
  border:4px solid var(--green-dim); box-shadow:0 0 0 3px #0a1410, 0 0 22px rgba(70,240,122,.25), inset 0 0 24px rgba(0,0,0,.7);
}
.pulse{ position:absolute; inset:-6px; border-radius:50%; border:3px solid var(--green); opacity:0; }
.tapping .pulse{ animation:ring .5s ease-out; }
@keyframes ring{ 0%{opacity:.6; transform:scale(.95)} 100%{opacity:0; transform:scale(1.25)} }
.socket-svg{ width:86px; height:86px; position:relative; z-index:2; filter:drop-shadow(0 0 6px rgba(70,240,122,.5)); }
.taphint{ font-family:var(--pixel); font-size:9px; color:var(--dim); margin-top:6px; letter-spacing:1px; }
.tapinfo{ font-family:var(--term); font-size:15px; color:var(--cyan); margin-top:4px; text-align:center; min-height:18px; padding:0 8px; }
/* Auto-Tapper: persistent badge + a ring pulse on the socket each auto-tap. */
.autotap-badge{
  margin-top:8px; font-family:var(--pixel); font-size:8px; letter-spacing:1px; color:var(--green);
  border:1px solid var(--green-dim); border-radius:999px; padding:5px 11px;
  background:rgba(70,240,122,.06); animation:autotapBlink 1.6s ease-in-out infinite;
}
@keyframes autotapBlink{ 0%,100%{ opacity:.55; box-shadow:0 0 0 0 rgba(70,240,122,0); } 50%{ opacity:1; box-shadow:0 0 10px rgba(70,240,122,.35); } }
.socket-btn.autotap-fire .pulse{ animation:ring .45s ease-out; }

/* ---------- Section titles ---------- */
.sectitle{
  display:flex; align-items:center; gap:8px;
  font-family:var(--pixel); font-size:10px; color:var(--amber); margin:8px 2px 11px;
  letter-spacing:1px; text-shadow:none;
}
.sectitle::after{ content:""; flex:1; height:1px; background:linear-gradient(90deg, var(--line2), transparent); }
.dimcount{ color:var(--dim); }

/* ---------- Bulk-buy bar (segmented control) ---------- */
.bulk-bar{
  display:flex; gap:4px; margin-bottom:12px; padding:4px;
  background:#0a1016; border:1px solid var(--line2); border-radius:11px;
}
.bulk-btn{
  flex:1; min-height:44px; font-family:var(--pixel); font-size:9px; padding:9px 0; cursor:pointer;
  background:transparent; color:var(--dim); border:none;
  border-radius:8px; transition:background .12s ease, color .12s ease;
}
.bulk-btn.active{ color:var(--ink); background:var(--green); border-color:var(--green); box-shadow:0 0 10px rgba(70,240,122,.3); }

/* ---------- Cord cards ---------- */
.card{
  display:flex; align-items:center; gap:11px; padding:11px 12px; margin-bottom:9px; width:100%;
  text-align:left; background:linear-gradient(var(--panel),var(--panel2));
  border:1px solid var(--line2); position:relative;
  border-radius:var(--r); overflow:hidden;
  color:var(--text); cursor:pointer; font-family:var(--term);
  transition:border-color .15s ease, box-shadow .15s ease;
}
.card.buyable:not(:has(.cost.no)){ border-color:var(--green-dim); box-shadow:0 0 10px rgba(70,240,122,.08); }
.card.locked{ opacity:.4; filter:grayscale(.5); }
.card:active.buyable{ transform:translateY(1px); }
.card .ico{ font-size:30px; width:42px; text-align:center; flex:0 0 42px; }
.card .body{ flex:1; min-width:0; }
/* nm + meta never wrap — keeps each card a fixed height no matter how wide the
   numbers in .right get, so buying never reflows/jiggles the list. */
.card .nm{ font-size:21px; color:var(--text); line-height:1; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.card .meta{ font-size:16px; color:var(--dim); margin-top:2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.card .meta .pos{ color:var(--cyan); }
/* Cord flavor text is shown per-row by JS (fitFlavor) only when it fully fits the
   row on one line; otherwise it stays hidden rather than ellipsis-truncating.
   Default hidden so it never flashes truncated before the JS measure runs. */
.card .meta .flavor{ display:none; }
/* Fixed-width column with tabular digits so changing counts/costs never resize
   the card. Numbers are kept compact (fmt) in JS so they fit without clipping. */
.card .right{ text-align:right; flex:0 0 108px; width:108px; }
.card .owned{ font-family:var(--pixel); font-size:12px; color:var(--green); white-space:nowrap; overflow:hidden; font-variant-numeric:tabular-nums; }
.card .owned small{ font-family:var(--term); font-size:15px; color:var(--amber); }
.card .cost{ font-size:20px; color:var(--amber); margin-top:3px; white-space:nowrap; overflow:hidden; font-variant-numeric:tabular-nums; }
.card .cost.ok{ color:var(--green); }
.card .cost.no{ color:var(--red); }
.card .mnote{ font-size:14px; color:var(--magenta); margin-top:2px; white-space:nowrap; overflow:hidden; font-variant-numeric:tabular-nums; }
.milestone{ position:absolute; bottom:0; left:0; right:0; height:3px; background:#0a1410; }
.milestone i{ display:block; height:100%; background:var(--magenta); box-shadow:0 0 6px var(--magenta); border-radius:0 2px 2px 0; transition:width .2s ease; }
.empty-note{ color:var(--dim); text-align:center; padding:24px 10px; font-size:18px; }

/* ---------- Compact tap button on the Upgrades tab ---------- */
.taptab{
  position:sticky; top:0; z-index:3; width:100%;
  font-family:var(--pixel); font-size:11px; padding:13px; margin-bottom:10px;
  background:linear-gradient(#13241a,#0c170f); color:var(--green);
  border:1px solid var(--green); border-radius:var(--r); cursor:pointer; letter-spacing:1px;
  text-shadow:0 0 6px rgba(70,240,122,.45); box-shadow:0 2px 10px rgba(0,0,0,.4);
}
.taptab:active{ transform:translateY(2px); }
.taptab b{ color:var(--amber); }

/* ---------- Plug-tab sticky toolbar (big socket collapses into a compact bar) ---------- */
.plug-sentinel{ height:1px; }
/* Pins at the top once the big socket scrolls past; covers cords scrolling under it. */
.plug-toolbar{ position:sticky; top:0; z-index:3; background:var(--bg2); padding-bottom:9px; }
.plug-toolbar .bulk-bar{ margin-bottom:0; }
/* Compact tap bar: same look as the Upgrades .taptab but a touch taller. Hidden
   (display:none — reserves no space) until #p-plug collapses, then it appears and
   fades in. The toolbar is the sticky element — this one is not separately sticky. */
.plug-taptab{ position:relative; top:auto; padding:17px; margin-bottom:9px; display:none; }
/* Generic: any scroll-collapse panel (Plug or Zap) reveals its compact bar. */
.plug-collapsed .plug-taptab{ display:block; animation:plugBarIn .2s ease; }
@keyframes plugBarIn{ from{ opacity:0; transform:translateY(-6px); } to{ opacity:1; transform:none; } }
body.noanim .plug-taptab{ animation:none; }

/* ---------- Upgrade grid ---------- */
.ugrid{ display:grid; grid-template-columns:1fr 1fr; gap:9px; }
.upg{
  background:linear-gradient(var(--panel),var(--panel2)); border:1px solid var(--line2);
  padding:11px; display:flex; flex-direction:column; gap:5px; cursor:pointer; text-align:left;
  border-radius:var(--r); color:var(--text); font-family:var(--term);
  transition:border-color .15s ease, box-shadow .15s ease, opacity .15s ease;
}
.upg.ok{ border-color:var(--green); box-shadow:0 0 10px rgba(70,240,122,.12); }
.upg.no{ opacity:.55; }
.upg.bought{ border-color:var(--green-dim); opacity:.45; cursor:default; }
.upg:active.ok{ transform:translateY(1px); }
.upg .un{ font-size:18px; line-height:1; color:var(--text); }
.upg .ud{ font-size:14px; line-height:1.15; color:var(--dim); flex:1; }
.upg .uc{ font-size:17px; color:var(--amber); }
.upg.no .uc{ color:var(--red); }
.upg.bought .uc{ color:var(--green); }
/* Core (prestige) upgrades — magenta accent to read as a separate currency */
.upg.core{ border-left:4px solid var(--magenta); }
.upg.core.ok{ border-color:var(--magenta); }
.upg.core .uc{ color:var(--magenta); }
.upg.core.no .uc{ color:var(--dim); }
.upg.core.bought .uc{ color:var(--green); }
/* The "???" upgrade — the endgame goal (unlocks the Voltlands). A full-width,
   centered banner that pulses magenta↔cyan so it stands out as a prize, and
   stays vivid even when it's still unaffordable. */
.upg.core.mystery{
  grid-column:1 / -1; opacity:1; text-align:center; align-items:center;
  padding:18px 12px; gap:9px; position:relative; overflow:hidden;
  border-color:var(--magenta);
  background:linear-gradient(120deg,#1c0d22,var(--panel) 55%,#0c1a20);
  animation:mysteryGlow 2.6s ease-in-out infinite;
}
.upg.core.mystery .un{
  font-size:30px; letter-spacing:.22em; color:var(--magenta);
  text-shadow:0 0 10px var(--magenta), 0 0 24px rgba(255,99,214,.5);
  animation:mysteryFlicker 3.3s steps(1) infinite;
}
.upg.core.mystery .ud{ flex:none; color:var(--cyan); opacity:.85; letter-spacing:.05em; }
.upg.core.mystery .uc{
  font-size:20px; color:var(--amber); text-shadow:0 0 8px rgba(255,194,77,.5);
}
.upg.core.mystery.ok{ animation:mysteryGlowReady 1.1s ease-in-out infinite; }   /* affordable: it screams buy-me */
@keyframes mysteryGlow{
  0%,100%{ box-shadow:0 0 11px rgba(255,99,214,.28), inset 0 0 26px rgba(255,99,214,.06); }
  50%{ box-shadow:0 0 22px rgba(84,220,255,.42), inset 0 0 32px rgba(84,220,255,.10); }
}
@keyframes mysteryGlowReady{
  0%,100%{ box-shadow:0 0 16px rgba(255,99,214,.55); border-color:var(--magenta); }
  50%{ box-shadow:0 0 32px rgba(255,99,214,.95); border-color:var(--cyan); }
}
@keyframes mysteryFlicker{ 0%,92%,100%{opacity:1} 94%{opacity:.5} 96%{opacity:.85} 98%{opacity:.6} }
/* animations-off / reduced-motion: keep the banner bold but hold it still */
body.noanim .upg.core.mystery, body.noanim .upg.core.mystery .un{ animation:none; }

/* ---------- Goals / achievements ---------- */
.goal{ display:flex; gap:11px; align-items:center; padding:11px 12px; margin-bottom:9px; background:var(--panel); border:1px solid var(--line2); border-radius:var(--r); }
.goal.done{ border-color:var(--green); background:linear-gradient(var(--panel),#0c1f14); }
.goal .gi{ font-size:26px; width:34px; text-align:center; flex:0 0 34px; }
.goal.locked .gi{ filter:grayscale(1) brightness(.5); }
.goal .gbody{ flex:1; min-width:0; }
.goal .gn{ font-size:20px; color:var(--text); }
.goal.done .gn{ color:var(--green); }
.goal .gd{ font-size:14px; color:var(--dim); }
.goal .gcheck{ color:var(--green); font-size:22px; font-family:var(--pixel); }
.ach-prog{ height:5px; background:#0a1410; margin-top:5px; overflow:hidden; border-radius:3px; }
.ach-prog i{ display:block; height:100%; background:linear-gradient(90deg,var(--amber),var(--green)); box-shadow:0 0 6px rgba(70,240,122,.4); border-radius:3px; transition:width .2s ease; }
.ach-progtext{ font-size:13px; color:var(--dim); margin-top:3px; }

/* ---------- MORE: blocks / buttons / settings / save ---------- */
.block{ background:var(--panel); border:1px solid var(--line2); border-radius:14px; padding:15px; margin-bottom:12px; }
.block h3{ font-family:var(--pixel); font-size:11px; color:var(--magenta); margin:0 0 10px; letter-spacing:1px; }
.block p{ font-size:16px; color:var(--dim); margin:0 0 10px; line-height:1.25; }
.block p.center{ text-align:center; margin-top:8px; }
.block .hi{ color:var(--green); }
.block .mag{ color:var(--magenta); }
.block .cyn{ color:var(--cyan); }
.coregain{ font-size:22px; color:var(--magenta); text-align:center; margin:6px 0; }
/* Storm Shards (⚡) use the cyan accent to read as a separate currency from cores (◆). */
.shardgain{ font-size:22px; color:var(--cyan); text-align:center; margin:6px 0; }
.bigbtn{
  width:100%; font-family:var(--pixel); font-size:11px; padding:14px; cursor:pointer;
  background:linear-gradient(#13241a,#0c170f); color:var(--green); border:1px solid var(--green);
  border-radius:var(--r); letter-spacing:1px; text-shadow:0 0 6px rgba(70,240,122,.45);
}
.bigbtn:active{ transform:translateY(2px); }
.bigbtn.dis{ opacity:.4; border-color:var(--dim); color:var(--dim); text-shadow:none; cursor:not-allowed; }
.bigbtn.danger{ color:var(--red); border-color:var(--red); text-shadow:0 0 6px rgba(255,106,106,.4); background:linear-gradient(#241313,#170c0c); }
.toggle{ display:flex; justify-content:space-between; align-items:center; padding:9px 0; border-bottom:1px solid var(--line); font-size:20px; }
.toggle[hidden]{ display:none; }   /* the hidden attr must beat .toggle's display:flex */
.toggle:last-of-type{ border:none; }
.sw{ font-family:var(--pixel); font-size:10px; min-height:44px; min-width:64px; padding:7px 14px; border:1px solid var(--line2); border-radius:var(--r-sm); background:#0a0f18; color:var(--dim); cursor:pointer; transition:color .12s ease, border-color .12s ease; }
.sw.on{ color:var(--green); border-color:var(--green); }
.muted{ color:var(--dim); font-size:14px; margin:10px 0 0; }
.version{ font-family:var(--pixel); font-size:8px; color:var(--green-dim); text-align:center; letter-spacing:1px; margin-top:8px; }
textarea{ width:100%; height:70px; background:#060a0e; color:var(--green); border:1px solid var(--line2); border-radius:var(--r-sm); font-family:var(--term); font-size:16px; padding:8px; resize:none; }
.row2{ display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:8px; }
.smbtn{ font-family:var(--pixel); font-size:9px; min-height:44px; padding:11px; background:#0a0f18; color:var(--cyan); border:1px solid var(--line2); border-radius:var(--r-sm); cursor:pointer; }
.smbtn.danger{ color:var(--red); border-color:var(--red); }

/* ---------- Stats list ---------- */
.stat-list{ list-style:none; margin:0; padding:0; }
.stat-list li{ display:flex; justify-content:space-between; padding:8px 2px; border-bottom:1px solid var(--line); font-size:18px; }
.stat-list li:last-child{ border:none; }
.stat-list span{ color:var(--dim); }
.stat-list b{ color:var(--text); font-variant-numeric:tabular-nums; }

/* ---------- Bottom tab bar ---------- */
.tabbar{ display:flex; border-top:1px solid var(--line); background:#070b11; padding-bottom:var(--safe-bottom); }
.tab{ flex:1; min-height:56px; padding:9px 2px 8px; text-align:center; cursor:pointer; border:none; color:var(--dim); background:none; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:5px; position:relative; transition:color .15s ease; }
.tab .ti{ font-size:22px; line-height:1; }
.tab .tl{ font-family:var(--pixel); font-size:7px; letter-spacing:0; }
.tab.active{ color:var(--green); background:linear-gradient(rgba(70,240,122,.08), rgba(70,240,122,0)); text-shadow:0 0 8px rgba(70,240,122,.5); }
.tab.active::before{
  content:""; position:absolute; top:-1px; left:22%; right:22%; height:2px;
  background:var(--green); border-radius:0 0 2px 2px; box-shadow:0 0 8px rgba(70,240,122,.6);
}
/* affordability dot: something on this tab can be bought right now */
.tdot{
  position:absolute; top:7px; right:calc(50% - 19px); width:8px; height:8px; border-radius:50%;
  background:var(--red); box-shadow:0 0 6px rgba(255,106,106,.8);
  animation:dotpulse 1.6s ease-in-out infinite;
}
@keyframes dotpulse{ 0%,100%{ opacity:.7; transform:scale(.9); } 50%{ opacity:1; transform:scale(1.1); } }
body.noanim .tdot{ animation:none; }
@media (prefers-reduced-motion: reduce){ .tdot{ animation:none; } }

/* ---------- Floating tap numbers ---------- */
.floaters{ position:fixed; inset:0; pointer-events:none; z-index:55; }
.float{ position:absolute; font-family:var(--pixel); font-size:12px; color:var(--green); pointer-events:none; text-shadow:0 0 6px rgba(70,240,122,.7); animation:floatup 1s ease-out forwards; white-space:nowrap; }
@keyframes floatup{ 0%{opacity:1; transform:translateY(0)} 100%{opacity:0; transform:translateY(-60px)} }

/* ---------- Power surge spark ---------- */
.surge-layer{ position:fixed; inset:0; pointer-events:none; overflow:hidden; z-index:40; }
.surge{
  position:absolute; width:54px; height:54px; border:none; background:transparent; cursor:pointer;
  pointer-events:auto; font-size:40px; line-height:54px; text-align:center; color:var(--amber);
  filter:drop-shadow(0 0 8px var(--amber)); animation:sparkpulse .6s infinite alternate;
}
@keyframes sparkpulse{ from{ transform:scale(.85) rotate(-8deg); filter:drop-shadow(0 0 4px var(--amber)); } to{ transform:scale(1.1) rotate(8deg); filter:drop-shadow(0 0 14px var(--amber)); } }

/* ---------- Toast banner ----------
   In-flow strip in the same slot as the buff bar: while a toast is live the
   buff pills (.suppressed) collapse and the toast takes their place, so
   notifications never overlay the shop cards or upgrade buttons. */
.toast-wrap{
  display:flex; justify-content:center; align-items:center;
  max-height:0; overflow:hidden; transition:max-height .2s ease, padding .2s ease;
  background:#070b11; border-bottom:2px solid transparent;
  pointer-events:none;
}
.toast-wrap.show{ max-height:60px; padding:6px 10px; border-bottom-color:var(--line); }
.toast{
  font-family:var(--pixel); font-size:9px; padding:7px 14px; max-width:96%;
  background:rgba(8,20,13,.92); border:1px solid var(--green); color:var(--green);
  border-radius:999px; letter-spacing:1px;
  animation:tin .25s ease-out, tout .3s 2.7s forwards; text-align:center;
  box-shadow:0 0 10px rgba(70,240,122,.15);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.toast.gold{
  background:rgba(26,20,7,.92); border-color:var(--amber); color:var(--amber);
  box-shadow:0 0 10px rgba(255,194,77,.18);
}
@keyframes tin{ from{opacity:0; transform:translateY(-6px)} }
@keyframes tout{ to{opacity:0} }

/* ---------- Modal ---------- */
.modal{ position:fixed; inset:0; z-index:90; display:none; align-items:center; justify-content:center; background:rgba(2,5,8,.82); padding:24px; }
.modal.show{ display:flex; }
.mbox{ width:100%; max-width:420px; background:var(--panel); border:2px solid var(--green); border-radius:14px; padding:20px; text-align:center; box-shadow:0 0 30px rgba(70,240,122,.2), 0 12px 40px rgba(0,0,0,.5); }
.mbox h2{ font-family:var(--pixel); font-size:13px; color:var(--green); margin:0 0 12px; }
.mbox h2.danger{ color:var(--red); }
.mbox p{ font-size:19px; color:var(--text); margin:8px 0; }
.mbox p.dim{ color:var(--dim); font-size:16px; }
.mbox .big{ font-size:30px; color:var(--amber); }

/* ---------- Power Store (Android shell only) ---------- */
#bonusArea .bigbtn{ width:100%; }
#bonusArea .smbtn small{ display:block; font-family:var(--term); font-size:13px; color:var(--dim); margin-top:3px; letter-spacing:0; }
#themeBtns{ display:flex; flex-wrap:wrap; gap:4px; justify-content:flex-end; }
.theme-sw{ min-width:0; padding:7px 8px; font-size:8px; }

/* ---------- Purchased CRT themes (theme_pack_phosphor) ----------
   Re-declare the palette on body so every descendant picks it up. */
body[data-theme="amber"]{
  --green:#ffb83d; --green-dim:#8f6a1f;
  --amber:#ff8c3d; --cyan:#ffd34e;
  --text:#f7e6bf; --dim:#90805e;
  --line:#3b321d; --line2:#5a4a22;
}
body[data-theme="ice"]{
  --green:#41d8ff; --green-dim:#1f6f8f;
  --cyan:#9fe9ff;
  --text:#bfe9f7; --dim:#6e8390;
  --line:#1d2f3b; --line2:#22405a;
}
body[data-theme="vapor"]{
  --green:#ff4fd8; --green-dim:#8f1f6f;
  --amber:#41d8ff; --cyan:#ffd34e; --magenta:#3dfc6b;
  --text:#f7bfe9; --dim:#906e85;
  --line:#3b1d33; --line2:#5a2250;
}

/* ---------- Grid Bonus (achievements -> production) ---------- */
.gridbonus{
  font-family:var(--pixel); font-size:8px; line-height:1.6; letter-spacing:.5px;
  color:var(--amber); border:1px solid var(--line); border-radius:var(--r); background:rgba(255,194,77,.05);
  padding:9px 11px; margin:0 2px 10px; text-shadow:none;
}

/* ============================================================
   The Voltlands (idle slayer) + wormhole
   ============================================================ */

/* world switching: tabs belong to grid / volt / both */
body[data-world="grid"] .tab[data-tworld="volt"]{ display:none; }
body[data-world="volt"] .tab[data-tworld="grid"]{ display:none; }

/* world switch: lives in the tab bar but is an action, not a tab —
   magenta-accented (the wormhole's colour) so it reads as the portal control. */
.worldswitch{
  flex:1; min-height:56px; padding:9px 2px 8px; cursor:pointer; position:relative;
  border:none; border-left:1px solid var(--line);
  background:linear-gradient(rgba(255,99,214,.12), rgba(255,99,214,0));
  color:var(--magenta); display:flex; flex-direction:column; align-items:center; justify-content:center; gap:5px;
  text-shadow:0 0 8px rgba(255,99,214,.5); animation:wormpulse 2.4s ease-in-out infinite;
}
.worldswitch::before{
  content:""; position:absolute; top:-1px; left:18%; right:18%; height:2px;
  background:var(--magenta); border-radius:0 0 2px 2px; box-shadow:0 0 8px rgba(255,99,214,.7);
}
.worldswitch .ti{ font-size:22px; line-height:1; }
.worldswitch .tl{ font-family:var(--pixel); font-size:7px; letter-spacing:0; white-space:nowrap; }
.worldswitch[hidden]{ display:none; }
@keyframes wormpulse{ 0%,100%{ opacity:.85; } 50%{ opacity:1; } }
body.noanim .worldswitch{ animation:none; }

/* World switch moved off the tab bar into a dedicated More-page block (below Core
   Upgrades). Override the tab-bar layout so it reads as a full-width portal button. */
#worldswitchBlock .worldswitch{
  width:100%; flex-direction:row; gap:10px; min-height:52px; padding:14px;
  border:1px solid var(--magenta); border-radius:12px;
  background:linear-gradient(rgba(255,99,214,.16), rgba(255,99,214,.04));
}
#worldswitchBlock .worldswitch::before{ display:none; }
#worldswitchBlock .worldswitch .ti{ font-size:24px; }
#worldswitchBlock .worldswitch .tl{ font-size:12px; letter-spacing:1px; }

/* ---------- Surge Grid (combat research tree) ---------- */
.surge-intro{ font-size:15px; color:var(--dim); margin:2px 2px 12px; line-height:1.4; }

/* the tree flows vertically as connected spines (not the 2-col upgrade grid) */
.surge-tree{ display:flex; flex-direction:column; }

/* per-branch accents — explicit rgba (no color-mix) so older webviews are fine */
.sg--trunk{ --acc:var(--green); --acc-soft:rgba(70,240,122,.13); --acc-line:rgba(70,240,122,.55); }
.sg--crit { --acc:var(--red);   --acc-soft:rgba(255,106,106,.14); --acc-line:rgba(255,106,106,.55); }
.sg--flow { --acc:var(--cyan);  --acc-soft:rgba(84,220,255,.14);  --acc-line:rgba(84,220,255,.55); }
.sg--hunt { --acc:var(--amber); --acc-soft:rgba(255,194,77,.16);  --acc-line:rgba(255,194,77,.55); }

/* a spine = vertical chain of nodes joined by a glowing conduit */
.sg-spine{ display:flex; flex-direction:column; gap:11px; }
.sg-node{
  position:relative; display:flex; align-items:center; gap:11px; width:100%; text-align:left;
  cursor:pointer; color:var(--text); font-family:var(--term);
  background:linear-gradient(var(--panel),var(--panel2));
  border:1px solid var(--line2); border-left:3px solid var(--acc-line);
  border-radius:var(--r); padding:10px 12px;
  transition:border-color .15s ease, box-shadow .15s ease, opacity .15s ease;
}
.sg-node::before{ content:""; position:absolute; left:27px; top:-11px; width:2px; height:11px; background:var(--acc-line); }
.sg-spine > .sg-node:first-child::before{ display:none; }
.sg-node:active{ transform:translateY(1px); }
.sg-ico{ font-size:23px; line-height:1; width:30px; flex:0 0 30px; text-align:center; }
.sg-txt{ flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.sg-nm{ font-size:18px; line-height:1.05; color:var(--text); }
.sg-dsc{ font-size:13px; line-height:1.18; color:var(--dim); }
.sg-node .uc{ flex:0 0 auto; font-size:16px; white-space:nowrap; color:var(--amber); font-family:var(--term); }
/* node states */
.sg-node.is-owned{ background:linear-gradient(90deg,var(--acc-soft),var(--panel2)); border-left-color:var(--acc); }
.sg-node.is-owned .sg-ico{ filter:drop-shadow(0 0 5px var(--acc)); }
.sg-node.is-owned .uc{ color:var(--acc); }
.sg-node.is-affordable{ border-color:var(--acc); box-shadow:0 0 11px -3px var(--acc), inset 0 0 16px -10px var(--acc); }
.sg-node.is-affordable .uc{ color:var(--acc); }
.sg-node.is-unaffordable{ opacity:.78; }
.sg-node.is-unaffordable .uc{ color:var(--red); opacity:1; }   /* keep the cost legible (AA) even while the node is dimmed */
/* locked: a node/path blocked by the chosen branch (or a corrupt save) — reads as disabled, not tappable */
.sg-branchcard.is-locked, .sg-node.is-locked{ opacity:.5; box-shadow:none; }

/* FORK divider + the three-way path choice */
.sg-fork{ position:relative; text-align:center; font-family:var(--pixel); font-size:9px; letter-spacing:2px; color:var(--amber); margin:15px 0 11px; text-shadow:0 0 8px rgba(255,194,77,.4); }
.sg-fork::before{ content:""; position:absolute; left:50%; top:-13px; width:2px; height:13px; background:rgba(255,194,77,.5); transform:translateX(-50%); }
.sg-choice{ display:grid; grid-template-columns:1fr 1fr 1fr; gap:7px; }
.sg-branchcard{
  display:flex; flex-direction:column; align-items:center; gap:5px; text-align:center; padding:12px 6px;
  cursor:pointer; color:var(--text); font-family:var(--term);
  background:linear-gradient(var(--panel),var(--panel2)); border:1px solid var(--acc); border-radius:var(--r);
  box-shadow:0 0 13px -5px var(--acc); transition:box-shadow .15s ease, transform .1s ease;
}
.sg-branchcard:active{ transform:translateY(1px); }
.sg-branchcard .sg-ico{ font-size:27px; width:auto; flex:none; filter:drop-shadow(0 0 5px var(--acc)); }
.sg-bname{ font-family:var(--pixel); font-size:8px; letter-spacing:1px; color:var(--acc); }
.sg-beff{ font-size:12px; line-height:1.2; color:var(--dim); }
.sg-branchcard .uc{ font-size:15px; color:var(--acc); font-family:var(--term); }
.sg-branchcard.is-unaffordable{ opacity:.82; box-shadow:none; }
.sg-branchcard.is-unaffordable .uc{ color:var(--red); }

/* chosen-path header + paths-not-taken footer */
.sg-pathlabel{ position:relative; text-align:center; font-family:var(--pixel); font-size:10px; letter-spacing:2px; color:var(--acc); margin:16px 0 12px; text-shadow:0 0 9px var(--acc-soft); }
.sg-pathlabel::before{ content:""; position:absolute; left:50%; top:-14px; width:2px; height:14px; background:var(--acc-line); transform:translateX(-50%); }
.sg-givenup{ margin-top:13px; padding:9px 11px; border:1px dashed var(--line2); border-radius:var(--r); font-size:13px; line-height:1.35; color:var(--dim); text-align:center; }
.sg-givenup b{ color:var(--text); font-weight:normal; }

/* build-preset bar */
.surge-presets{ display:flex; gap:14px; flex-wrap:wrap; align-items:center; margin:0 2px 12px; font-family:var(--pixel); font-size:9px; color:var(--dim); }
.surge-presets .sp-grp{ display:inline-flex; gap:5px; align-items:center; }
.sp-btn{ font-family:var(--pixel); font-size:9px; min-width:32px; min-height:30px; padding:6px 9px; border:1px solid var(--line2); border-radius:6px; background:#0a0f18; color:var(--cyan); cursor:pointer; }
.sp-btn:active{ transform:translateY(1px); }
.discharge-btn{ font-family:var(--pixel); font-size:11px; margin-top:8px; padding:11px 18px; min-height:44px; border:1px solid var(--amber); border-radius:10px; background:linear-gradient(rgba(255,194,61,.14), rgba(255,194,61,.02)); color:var(--amber); cursor:pointer; }
.discharge-btn[hidden]{ display:none; }
.discharge-btn:disabled{ opacity:.5; color:var(--dim); border-color:var(--line2); cursor:default; }
.discharge-btn.ready{ animation:wormpulse 1.2s ease-in-out infinite; box-shadow:0 0 12px rgba(255,194,61,.3); }

/* arena */
.arena{ display:flex; flex-direction:column; align-items:center; padding:8px 0 4px; }
.zone{
  font-family:var(--pixel); font-size:8px; letter-spacing:1px; color:var(--cyan);
  text-shadow:none; margin-bottom:8px;
}
.enemy-btn{
  width:150px; height:150px; border-radius:50%; cursor:pointer;
  background:radial-gradient(circle, #131a2a 0%, #0a0f18 70%);
  border:3px solid var(--line2); position:relative;
  display:flex; align-items:center; justify-content:center;
  transition:transform .06s;
}
.enemy-btn:active{ transform:scale(.94); }
.enemy-btn.boss{ border-color:var(--red); box-shadow:0 0 18px rgba(255,106,106,.5); }
.enemy-emoji{ font-size:72px; line-height:1; filter:drop-shadow(0 0 8px rgba(84,220,255,.45)); }
.enemy-btn.zapped .enemy-emoji{ animation:zapflash .18s ease-out; }
@keyframes zapflash{
  0%{ filter:drop-shadow(0 0 2px #fff) brightness(3); transform:translateX(0); }
  40%{ transform:translateX(-4px); }
  70%{ transform:translateX(3px); }
  100%{ filter:drop-shadow(0 0 8px rgba(84,220,255,.45)) brightness(1); transform:translateX(0); }
}
body.noanim .enemy-btn.zapped .enemy-emoji{ animation:none; }
.enemy-name{
  font-family:var(--pixel); font-size:10px; color:var(--amber); margin-top:10px;
  letter-spacing:1px; text-shadow:0 0 6px rgba(255,194,77,.4);
}
.enemy-reward{ font-family:var(--term); font-size:13px; color:var(--dim); margin-top:3px; }
.hpbar{
  width:78%; max-width:340px; height:14px; margin-top:8px;
  background:#0a0f18; border:1px solid var(--line2); border-radius:999px; overflow:hidden;
}
.hpbar i{
  display:block; height:100%; width:100%;
  background:linear-gradient(90deg, var(--red), var(--amber));
  transition:width .12s linear; border-radius:999px;
}
.hptext{ font-family:var(--term); font-size:15px; color:var(--dim); margin-top:4px; }
.float.crit{ color:var(--amber); font-size:15px; text-shadow:0 0 8px rgba(255,194,77,.8); }

/* Weapon cards live in the volt world — their buyable accent is cyan (rate),
   not the grid's green, so each world keeps one consistent production colour. */
#p-zap .card:not(.locked):not(:has(.cost.no)){ border-color:#1f6f8f; box-shadow:0 0 10px rgba(84,220,255,.08); }

/* wormhole cutscene */
#wormhole{
  position:fixed; inset:0; z-index:200; display:none;
  background:#000; align-items:center; justify-content:center; overflow:hidden;
}
#wormhole.show{ display:flex; }
#wormhole .spiral{
  position:absolute; width:160vmax; height:160vmax; border-radius:50%;
  background:conic-gradient(from 0deg, #000 0deg, var(--magenta) 40deg, #000 80deg,
    var(--cyan) 130deg, #000 180deg, var(--magenta) 230deg, #000 280deg, var(--cyan) 330deg, #000 360deg);
  opacity:.5; animation:spiralspin 4.2s cubic-bezier(.5,.1,.9,.9) forwards;
}
@keyframes spiralspin{
  0%{ transform:rotate(0) scale(.05); opacity:0; }
  25%{ opacity:.65; }
  100%{ transform:rotate(1080deg) scale(1.25); opacity:.9; }
}
#wormhole .wormtext{
  position:relative; z-index:1; font-family:var(--pixel); font-size:11px; line-height:2.4;
  color:var(--green); text-shadow:0 0 8px rgba(61,252,107,.8); text-align:left;
}
#wormhole .wormtext p{ margin:0; opacity:0; animation:wormline .4s forwards; }
#wormhole .wormtext p:nth-child(1){ animation-delay:.5s; }
#wormhole .wormtext p:nth-child(2){ animation-delay:1.6s; }
#wormhole .wormtext p:nth-child(3){ animation-delay:2.8s; }
@keyframes wormline{ to{ opacity:1; } }

/* ============================================================
   Tablet / landscape (>=820px): bottom tab bar becomes a left nav
   rail, #app becomes a rail|header / rail|main grid, PLUG and ZAP
   split into two panes, and the card lists go multi-column. CSS-only
   and additive — below 820px the phone single-column UI is untouched.
   Adapted to the current #p-plug markup (the plug-scroll redesign
   added .plug-sentinel + a .plug-toolbar holding the bulk bar).
   ============================================================ */
@media (min-width: 820px) {

  /* ---- Shell: rail + header + main as a grid ---- */
  #app {
    max-width: none; width: 100vw;
    flex-direction: initial;          /* cancel the phone column flex */
    display: grid;
    grid-template-columns: 118px 1fr;
    grid-template-rows: auto auto 1fr;
    grid-template-areas:
      "rail header"
      "rail notif"
      "rail main";
    box-shadow: none;                 /* drop the phone "device" frame glow */
  }
  .topbar { grid-area: header; padding: 16px 26px; }
  main    { grid-area: main; }
  /* Streak / buff / toast banners share one slot at the top of the content column
     (they're mutually exclusive — a toast collapses the buff pills). Without this
     they auto-place into an implicit cell in the narrow rail and get cut off. */
  #buffBar, #toast { grid-area: notif; }

  /* ---- Bottom tab bar -> left vertical rail ---- */
  .tabbar {
    grid-area: rail;
    flex-direction: column; align-items: stretch;
    border-top: none; border-right: 1px solid var(--line);
    padding: 16px 11px; gap: 7px;
  }
  .tab { flex: none; min-height: 0; padding: 12px 0; border-radius: 12px; }
  .tab.active { background: linear-gradient(rgba(70,240,122,.10), rgba(70,240,122,.02)); }
  /* active indicator: left bar (reset the phone's top underline) */
  .tab.active::before {
    top: 24%; bottom: 24%; left: 0; right: auto;
    width: 3px; height: auto; border-radius: 0 3px 3px 0;
  }
  .tab .ti { font-size: 23px; }
  /* world-switch pinned to the bottom of the rail */
  .worldswitch {
    flex: none; margin-top: auto;
    flex-direction: column; align-items: center; gap: 5px;
    padding: 12px 0; border-radius: 12px;
  }
  /* affordability dots reposition for the rail */
  .tdot { top: 7px; right: 26px; }

  /* ---- PLUG: socket hero | (title / bulk bar / cord grid) ----
     The big socket stays visible on tablet, so the phone collapse
     machinery is dormant: hide the sentinel, drop the toolbar's sticky
     styling, and the compact tap bar stays hidden (the panel doesn't
     scroll here — #cordlist does). Bulk bar gets its own row. */
  #p-plug.active {
    display: grid;
    grid-template-columns: 392px 1fr;
    grid-template-rows: auto auto 1fr;
    grid-template-areas:
      "socket title"
      "socket bulk"
      "socket list";
    padding: 0; overflow: hidden;
  }
  #p-plug .socket-wrap {
    grid-area: socket; justify-content: center;
    border-right: 1px solid var(--line); padding: 24px;
  }
  #p-plug .sectitle { grid-area: title; margin: 16px 22px 4px; }
  #p-plug .plug-sentinel { display: none; }
  /* The big socket is always visible on tablet, so the phone compact tap bar is
     never needed. Keep it hidden even if the now zero-size collapse sentinel trips
     the scroll handler into adding .plug-collapsed. */
  #p-plug.active .plug-taptab { display: none; }
  #p-plug .plug-toolbar {
    grid-area: bulk; position: static; background: none;
    padding: 0 22px 4px; z-index: auto;
  }
  #cordlist {
    grid-area: list; padding: 0 22px 18px;
    display: grid; grid-template-columns: 1fr 1fr; gap: 9px;
    align-content: start; overflow-y: auto; min-height: 0;
  }
  .socket-btn { width: 240px; height: 240px; }
  .socket-svg { width: 138px; height: 138px; }

  /* ---- ZAP: arena | weapon grid ---- */
  #p-zap.active {
    display: grid;
    grid-template-columns: 452px 1fr;
    grid-template-rows: auto auto 1fr;
    grid-template-areas:
      "arena title"
      "arena bulk"
      "arena list";
    padding: 0; overflow: hidden;
  }
  #p-zap .arena {
    grid-area: arena; justify-content: center;
    border-right: 1px solid var(--line2); padding: 24px;
  }
  #p-zap .sectitle { grid-area: title; margin: 16px 22px 4px; }
  /* The big enemy button stays visible on tablet, so the phone collapse rig is
     dormant: no sentinel, and the compact tap bar stays hidden. The zap toolbar
     still holds the bulk bar (x1/x5/x10/MAX), so it gets its own grid row and
     static styling like #p-plug rather than being removed. */
  #p-zap .plug-sentinel { display: none; }
  #p-zap.active .plug-taptab { display: none; }
  #p-zap .plug-toolbar {
    grid-area: bulk; position: static; background: none;
    padding: 0 22px 4px; z-index: auto;
  }
  #weaponlist {
    grid-area: list; padding: 0 22px 18px;
    display: grid; grid-template-columns: 1fr 1fr; gap: 9px;
    align-content: start; overflow-y: auto; min-height: 0;
  }
  .enemy-btn   { width: 236px; height: 236px; }
  .enemy-emoji { font-size: 110px; }
  .hpbar       { width: 80%; max-width: 420px; }

  /* ---- UPGRADE / ARSENAL: 3-column grids ---- */
  #p-up.active, #p-arsenal.active { padding: 18px 26px; }
  #uplist, #zuplist { grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
  /* "tap to plug" shortcut on the Upgrades page goes inline, top-right */
  #p-up .taptab { align-self: flex-end; width: auto; padding: 14px 30px; margin-bottom: 14px; }

  /* ---- GOALS: 2-column achievement grid (real container id is #goallist) ---- */
  #p-goals.active { padding: 18px 26px; }
  #goallist { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-content: start; }

  /* ---- MORE: two columns ---- */
  #p-more.active {
    display: grid; grid-template-columns: 1fr 1fr; gap: 18px;
    padding: 18px 26px; align-content: start;
  }
}
