:root {
  --bg: #0b0806;
  --panel: rgba(26, 18, 11, 0.92);
  --accent: #ff7a2f;
  --text: #f6edda;
}
* { box-sizing: border-box; }
html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  font-family: "Segoe UI", system-ui, sans-serif;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}
canvas { display: block; position: fixed; inset: 0; cursor: crosshair; }

.hidden { display: none !important; }

/* ---- Menu overlay ---- */
.overlay {
  position: fixed; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 50% 30%, #2a0f0f, #0b0806 70%);
  z-index: 10;
}
.panel {
  width: min(440px, 92vw);
  max-height: 94vh;            /* never taller than the viewport… */
  max-height: 94svh;          /* …(small-viewport unit accounts for mobile browser chrome) */
  overflow-y: auto;           /* scroll inside instead of overflowing off-screen */
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;        /* allow finger-scroll despite the global touch-action:none */
  background: var(--panel);
  border: 1px solid #4a3320;
  border-radius: 16px;
  padding: 26px 28px 22px;
  box-shadow: 0 20px 60px rgba(0,0,0,.6), 0 0 0 1px rgba(255,122,47,.15);
}

/* ---- Scrollbars ---- */
/* The OS-default chunky gray bars clash with the molten UI. Everything gets a
   slim, dark, theme-matched bar (Firefox: scrollbar-*; WebKit: pseudos — on
   Chromium 121+ scrollbar-color wins over the pseudos, same look either way). */
* { scrollbar-width: thin; scrollbar-color: rgba(122,84,46,.6) transparent; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: rgba(122,84,46,.6); border-radius: 5px; }
::-webkit-scrollbar-thumb:hover { background: rgba(160,112,62,.85); }
::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: transparent; }
/* Marketplace scrollers hide the bar COMPLETELY (wheel/touch still scrolls):
   the shop is sized to fit the viewport, so a visible bar there is pure noise. */
.panel.wide, .sdBody, .treeSideBody {
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.panel.wide::-webkit-scrollbar, .sdBody::-webkit-scrollbar, .treeSideBody::-webkit-scrollbar { display: none; }

/* ---- Main-menu hero art: layered CraftPix hell/lava scene w/ mouse parallax ---- */
#menu, #welcome { overflow: hidden; }            /* clip the oversized parallax layers */
.menuScene {
  position: absolute; inset: 0; z-index: 0;
  overflow: hidden; pointer-events: none;
  background: #e7ac6a;                            /* sky tone: fallback before images load */
}
.msLayer {
  position: absolute; inset: -7%;                 /* oversize so parallax never bares an edge */
  background: center bottom / cover no-repeat;
  image-rendering: pixelated;                     /* crisp pixel-art upscaling */
  /* --mx/--my come from initMenuScene (−1..1); --d is this layer's parallax depth (px) */
  transform: translate3d(calc(var(--mx,0) * var(--d) * 1px), calc(var(--my,0) * var(--d) * .5px), 0);
  transition: transform .2s ease-out;
  will-change: transform;
}
.msLayer.l7 { background-image: url("assets/menu/bg7.png"); --d: 3; }
.msLayer.l6 { background-image: url("assets/menu/bg6.png"); --d: 6; }
.msLayer.l5 { background-image: url("assets/menu/bg5.png"); --d: 9; }
.msLayer.l4 { background-image: url("assets/menu/bg4.png"); --d: 13; }
.msLayer.l3 { background-image: url("assets/menu/bg3.png"); --d: 18; }
.msLayer.l2 { background-image: url("assets/menu/bg2.png"); --d: 24; }
.msLayer.l1 { background-image: url("assets/menu/bg1.png"); --d: 34; }
/* pulsing lava light rising off the ridgeline */
.msGlow {
  position: absolute; left: 50%; bottom: -10%; transform: translateX(-50%);
  width: 130%; height: 62%; pointer-events: none; mix-blend-mode: screen;
  background: radial-gradient(ellipse at 50% 100%, rgba(255,120,40,.55), rgba(255,70,20,0) 62%);
  animation: msFlicker 5s ease-in-out infinite;
}
@keyframes msFlicker { 0%,100% { opacity: .5; } 50% { opacity: .92; } }
/* legibility veil so the panel + title read cleanly over the art */
.msVeil {
  position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(125% 92% at 50% 6%, rgba(10,7,16,0) 42%, rgba(10,7,16,.5) 100%),
    linear-gradient(180deg, rgba(10,7,16,.22) 0%, rgba(10,7,16,.03) 34%, rgba(10,7,16,.72) 100%);
}
/* the menu panel + title sit above the scene */
#menu > .panel, #welcome > .panel { position: relative; z-index: 1; }
#menu > .panel h1, #welcome > .panel h1 {
  color: #fff;
  text-shadow: 0 2px 8px rgba(0,0,0,.6), 0 0 24px rgba(255,120,40,.45);
}
/* "Grand Sigil" logo — the <h1><svg> wordmark in the menu + sign-in panels.
   Scales with the panel; fitMenuScale handles short viewports beyond this. */
.logoH { margin: 0 0 2px; line-height: 0; }
.bmgLogo { display: block; width: 100%; max-width: 330px; height: auto; margin: 0 auto; }
@media (max-width: 640px)  { .bmgLogo { max-width: 280px; } }
@media (max-height: 520px) { .bmgLogo { max-width: 230px; } }
@media (prefers-reduced-motion: reduce) {
  .msLayer { transition: none; }
  .msGlow { animation: none; opacity: .68; }
}

/* in-game pause menu — sits above the HUD/shop, with a see-through backdrop */
#escMenu { z-index: 30; background: rgba(10,7,16,.72); }
#escMenu .escDanger { color: #ff8a8a; border-color: #7a2a2a; }
#escMenu .escDanger:hover { background-color: #3a1414; }
h1 { margin: 0 0 4px; font-size: 34px; text-align: center; letter-spacing: .5px; }
.tag { margin: 0 0 18px; text-align: center; color: #c7b596; font-size: 14px; }
label { display: block; font-size: 13px; color: #d4c4a4; margin: 12px 0 4px; }
.hint { color: #8d7a58; font-weight: normal; }
input {
  width: 100%; padding: 11px 12px; margin-top: 4px;
  background: #191007; border: 1px solid #4a3320; border-radius: 9px;
  color: var(--text); font-size: 15px; outline: none;
}
input:focus { border-color: var(--accent); }
button {
  width: 100%; margin-top: 20px; padding: 13px;
  background: linear-gradient(180deg, #ff8a3d, #f0641e);
  border: none; border-radius: 10px; color: #1a0d00;
  font-size: 17px; font-weight: 700; cursor: pointer;
  transition: transform .06s ease, filter .15s;
}
button:hover { filter: brightness(1.08); }
button:active { transform: translateY(1px); }

.controls { margin-top: 22px; padding-top: 16px; border-top: 1px solid #3a2817; font-size: 13px; color: #d4c4a4; }
.controls .grid { display: grid; grid-template-columns: auto 1fr; gap: 6px 14px; margin-top: 10px; align-items: center; }
.controls small { display:block; margin-top:10px; color:#8d7a58; }
kbd {
  display: inline-block; padding: 2px 7px; margin: 0 1px;
  background: #281b0e; border: 1px solid #5c4526; border-bottom-width: 2px;
  border-radius: 5px; font-size: 12px; font-family: inherit;
}
.status { min-height: 18px; margin: 14px 0 0; text-align: center; color: #ffcf3f; font-size: 13px; }

/* ---- online + server browser ---- */
.online { text-align: center; font-size: 13px; color: #9fd8a0; margin: 14px 0 4px; }
.online b { color: #d7ffd8; }
.browserTop { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.browserTop .online { margin: 0; }
.browserTop button.ghost { width: auto; margin: 0; padding: 6px 12px; }
#search { margin-bottom: 12px; }
#serverList { max-height: 46vh; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.srv {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 11px 13px; margin-bottom: 7px;
  background: #191007; border: 1px solid #3a2817; border-radius: 9px;
  transition: border-color .1s, background .1s;
}
.srv:hover { border-color: var(--accent); background: #221808; }
.srv.full { opacity: .5; cursor: not-allowed; }
/* Cross-play wall: a room that won't accept this client's platform is greyed
   out and unclickable (like full), and shows a 🖥/📱 badge saying which it wants. */
.srv.blocked { opacity: .42; cursor: not-allowed; filter: grayscale(.4); }
.srv.blocked:hover { border-color: #3a2817; background: #191007; }
.srv.blocked .go { color: #a06a5a; }
.srv .splat {
  flex: none; font-size: 10.5px; font-weight: 700; letter-spacing: .3px; white-space: nowrap;
  padding: 2px 7px; border-radius: 99px;
  background: #1a1524; color: #b9a7e0; border: 1px solid #3a2c5c;
}
.srv .sflag { flex: none; display: inline-flex; width: 21px; height: 14px; margin-right: -2px; border-radius: 2px; overflow: hidden; box-shadow: 0 0 0 1px rgba(0,0,0,.45), inset 0 0 0 1px rgba(255,255,255,.12); }
.srv .sflag svg { display: block; width: 100%; height: 100%; }
.srv .sflag.code { align-items: center; justify-content: center; background: #2a2216; color: #c7b596; font-size: 9px; font-weight: 700; letter-spacing: .5px; }
.srv .sname { flex: 1; font-size: 15px; font-weight: 600; }
.srv .slock { flex: none; font-size: 12px; opacity: .85; filter: saturate(.4); }
/* Room-personality tags (🌱 noob-friendly / ⚖️ fair play / 🔥 veterans / 🟥🟦 teams) —
   tinted chips the lobby browser draws between the name and the head-count. */
.srv .stag {
  flex: none; font-size: 10.5px; font-weight: 700; letter-spacing: .3px; white-space: nowrap;
  padding: 2px 7px; border-radius: 99px;
  background: #2a2216; color: #c7b596; border: 1px solid #3a2817;
}
.srv .stag.noob  { background: #12250f; color: #8fe07a; border-color: #2c5423; }
.srv .stag.fair  { background: #241f0d; color: #e0cb7a; border-color: #55491f; }
.srv .stag.vets  { background: #2a120b; color: #ff9a66; border-color: #63301a; }
.srv .stag.teams { background: #101a2a; color: #7ab2e0; border-color: #1f3a5c; }
.srv .scount { font-size: 13px; color: #c7b596; white-space: nowrap; }
.srv .scount .live { color: #5ee06a; }
.srv .go { color: var(--accent); font-weight: 700; }
.createRow { display: flex; gap: 8px; margin-top: 10px; }
.createRow input { margin: 0; flex: 1 1 0; min-width: 0; }
.createRow button { width: auto; margin: 0; padding: 0 16px; font-size: 14px; white-space: nowrap; }

/* ---- Persistent wallet HUD (banked 🪙 gold + 💵 cash, top-left, always on
   top). Menu values from /auth/wallet; live in-match values from the snapshot. ---- */
#walletHud {
  position: fixed; top: 10px; left: 12px; z-index: 41;
  display: flex; align-items: center; gap: 7px;
  pointer-events: none; user-select: none;
}
#walletHud .whItem {
  display: inline-flex; align-items: center; gap: 5px;
  background: rgba(10,7,16,.72); border: 1px solid #4a3320; border-radius: 20px;
  padding: 5px 12px; font-size: 14px; font-weight: 700; color: #f0e2c4;
  text-shadow: 0 1px 2px #000; box-shadow: 0 2px 8px rgba(0,0,0,.4);
  backdrop-filter: blur(3px);
}
#walletHud .whItem.gold b { color: #ffd94a; }
#walletHud .whItem.cash b { color: #8fe89a; }
@media (max-width: 560px) { #walletHud .whItem { font-size: 12px; padding: 4px 9px; } }

/* ---- Audio controls (always on top) ---- */
#audioCtl {
  position: fixed; top: 10px; right: 12px; z-index: 40;
  display: flex; align-items: center; gap: 8px;
  background: rgba(10,7,16,.72); border: 1px solid #4a3320; border-radius: 20px;
  padding: 5px 12px 5px 9px; backdrop-filter: blur(3px);
  touch-action: auto; /* let the slider/button receive touch despite global touch-action:none */
}
#audioCtl button {
  width: auto; margin: 0; padding: 0; background: none; border: none;
  font-size: 20px; line-height: 1; cursor: pointer; filter: none;
}
#audioCtl button:hover { filter: brightness(1.2); }
#audioCtl input[type=range] {
  width: 74px; height: 18px; margin: 0; padding: 0;
  background: transparent; border: none; accent-color: var(--accent); cursor: pointer;
}
#audioCtl .audioSep { width: 1px; height: 20px; background: #4a3320; }
/* Collapse handle: a chevron button pinned to the pill's right edge. Collapsing
   hides every control but the handle, so the pill shrinks tight against the
   right screen edge; expanding reveals the sliders again to its left. */
#audioCtl #audioToggle { font-size: 15px; color: #cdb489; opacity: .8; }
#audioCtl #audioToggle:hover { opacity: 1; filter: brightness(1.2); }
#audioCtl.collapsed { gap: 0; padding: 5px 8px; }
#audioCtl.collapsed > :not(#audioToggle) { display: none; }

/* ---- HUD ---- */
/* Must outrank #touchUi (6): #hud is a stacking context, so #trainCtl's z-index:8
   is scoped INSIDE it and can never beat the touch layer on its own — at z-index 5
   the move zone swallowed every tap on the Training pills. Safe above the touch
   controls because #hud itself is pointer-events:none (only children that opt back
   in, i.e. #trainCtl, take taps) and none of its children overlap the touch UI. */
#hud { position: fixed; inset: 0; pointer-events: none; z-index: 7; }
/* Training-only controls — bottom-left corner, clear of the spellbar. A flex row
   so the pills sit side by side (never stretched to full width by base button{}). */
#trainCtl {
  position: absolute; left: 14px; bottom: 16px; z-index: 8; pointer-events: auto;
  display: flex; gap: 8px; align-items: flex-end;
}
#trainCtl button {
  width: auto; margin: 0; /* override base button{width:100%} so pills don't span the HUD */
  padding: 9px 14px; font-size: 14px; font-weight: 700; cursor: pointer;
  color: #ffe9c2; border-radius: 10px; position: relative; overflow: hidden;
}
/* 🎒 Loadout — shiny ember pill with a pulsing glow + a shine sweep to draw the eye. */
#trainShopBtn {
  background: linear-gradient(180deg, #4a2c66 0%, #2a1740 55%, #1c1030 100%);
  border: 1px solid #b28cff;
  box-shadow: 0 3px 12px rgba(0,0,0,.5), 0 0 14px rgba(150,100,255,.4);
  animation: trainShopPulse 2.2s ease-in-out infinite;
}
#trainShopBtn::before { /* diagonal light streak that periodically sweeps across */
  content: ""; position: absolute; top: 0; left: -75%; width: 45%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255,240,210,.55), transparent);
  transform: skewX(-20deg); pointer-events: none;
  animation: trainShopShine 3.6s ease-in-out infinite;
}
#trainShopBtn:hover { filter: brightness(1.12); border-color: #d8c4ff; }
@keyframes trainShopPulse {
  0%, 100% { box-shadow: 0 3px 12px rgba(0,0,0,.5), 0 0 12px rgba(150,100,255,.35); }
  50%      { box-shadow: 0 3px 16px rgba(0,0,0,.55), 0 0 26px rgba(180,140,255,.85); }
}
@keyframes trainShopShine { 0% { left: -75%; } 55%, 100% { left: 130%; } }
@media (prefers-reduced-motion: reduce) {
  #trainShopBtn { animation: none; }
  #trainShopBtn::before { animation: none; display: none; }
}
/* ⬆️ Level Up — warm gold pill so it reads as a distinct "power up" action. */
#trainLevelBtn {
  background: linear-gradient(180deg, #6a4a12 0%, #3a2708 100%);
  border: 1px solid #ffcf5a;
  box-shadow: 0 3px 12px rgba(0,0,0,.5), 0 0 12px rgba(255,190,70,.35);
}
#trainLevelBtn:hover { filter: brightness(1.12); border-color: #ffe08a; }
#trainLevelBtn:disabled { filter: grayscale(.5) brightness(.8); cursor: default; opacity: .7; }
#topbar {
  position: absolute; top: 12px; left: 0; right: 0;
  display: flex; justify-content: space-between; align-items: flex-start;
  padding: 0 16px;
}
#phaseInfo {
  background: rgba(10,7,16,.7); border: 1px solid #4a3320; border-radius: 10px;
  padding: 8px 14px; font-size: 15px; font-weight: 600;
  /* Drop below the fixed top-left wallet pill (#walletHud, z-index 41) so the
     gold/cash chips never render on top of the match info. The scoreboard (the
     topbar's right-hand item) stays at the top edge. */
  margin-top: 34px;
}
#scoreboard {
  background: rgba(10,7,16,.7); border: 1px solid #4a3320; border-radius: 10px;
  padding: 8px 14px; font-size: 13px; min-width: 150px;
  margin-top: 40px; /* drop below the audio control in the top-right corner */
}
#scoreboard .row { display: flex; justify-content: space-between; gap: 12px; padding: 2px 0; }
#scoreboard .row.dead { opacity: .45; text-decoration: line-through; }
#scoreboard .dot { display:inline-block; width:9px; height:9px; border-radius:50%; margin-right:6px; vertical-align:middle; }
#scoreboard .sc { white-space: nowrap; font-size: 12px; }
#scoreboard .pips { color: #ffcf3f; letter-spacing: 1px; margin-right: 3px; }
#scoreboard .champPts { color: #ffd94a; font-weight: 700; text-shadow: 0 0 6px rgba(255,193,60,.45); }

/* Full standings overlay — hold TAB during a match (see updateTabScore in main.js).
   pointer-events:none so holding TAB never blocks mouse aim underneath. */
#tabScore {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 40; width: min(560px, 94vw); max-height: 82vh; overflow-y: auto;
  background: rgba(10,7,16,.92); border: 1px solid #4a3320; border-radius: 14px;
  padding: 12px 14px; box-shadow: 0 12px 44px rgba(0,0,0,.6);
  pointer-events: none; font-size: 13px;
}
#tabScore .tsTitle {
  text-align: center; font-weight: 800; font-size: 16px; letter-spacing: .5px;
  color: #ffcf3f; margin-bottom: 8px;
}
#tabScore .tsRow {
  display: grid; grid-template-columns: 26px 1fr 40px 40px 54px 58px 66px;
  align-items: center; gap: 4px; padding: 3px 5px; border-radius: 6px;
}
/* Championship adds a leading ✨ Points column (its running total decides the champ). */
#tabScore.champ .tsRow { grid-template-columns: 26px 1fr 52px 40px 40px 54px 58px 66px; }
#tabScore .tsNum.pts { color: #ffd94a; font-weight: 700; }
#tabScore .tsRow.tsHead { color: #b79a7a; font-size: 12px; border-bottom: 1px solid #3a2a1a; margin-bottom: 3px; align-items: end; }
/* header stat columns: icon stacked over a short word (kills/deaths/gold/damage/ping) */
#tabScore .tsRow.tsHead .tsNum { display: flex; flex-direction: column; align-items: center; gap: 1px; line-height: 1.05; }
#tabScore .tsRow.tsHead .tsIco { font-size: 13px; }
#tabScore .tsCol { color: #8a7a68; font-size: 9px; text-transform: uppercase; letter-spacing: .3px; }
#tabScore .tsRow:not(.tsHead):nth-child(even) { background: rgba(255,255,255,.03); }
#tabScore .tsRow.me { background: rgba(255,207,63,.14); outline: 1px solid rgba(255,207,63,.4); }
#tabScore .tsRow.dead { opacity: .5; }
#tabScore .tsRow.dead .tsName { text-decoration: line-through; }
#tabScore .tsRow.elim { opacity: .35; }
#tabScore .tsRank { text-align: center; color: #8a7a68; font-variant-numeric: tabular-nums; }
#tabScore .tsName { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#tabScore .tsName .dot { display:inline-block; width:9px; height:9px; border-radius:50%; margin-right:6px; vertical-align:middle; }
#tabScore .tsNum { text-align: center; font-variant-numeric: tabular-nums; white-space: nowrap; }
#tabScore .tsNum.ping { color: #7fe0ff; }
#tabScore .tsNum small { color: #8a7a68; font-size: 10px; }
#tabScore .tsMore { text-align: center; color: #8a7a68; margin-top: 6px; font-size: 12px; }
/* your active buffs (skills + gear), one compact strip under the standings */
#tabScore .tsBuffs {
  margin-top: 8px; padding-top: 7px; border-top: 1px solid #3a2817;
  font-size: 11.5px; line-height: 1.5; color: #d4c4a4; text-align: left;
}
#tabScore .tsBuffs .tsBuffTitle { color: #9d8b6c; font-size: 10px; text-transform: uppercase; letter-spacing: .4px; display: block; margin-bottom: 2px; }
#tabScore .tsBuffs .chip { display: inline-block; background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.1); border-radius: 7px; padding: 0 6px; margin: 1px 3px 1px 0; white-space: nowrap; }
/* the "hold TAB" nudge under the countdown banner */
#banner .sub.tabHint { font-size: 14px; color: #9fe8ff; opacity: .85; margin-top: 10px; }

/* Big segmented HP + mana bars for you (or the mage you're spectating) */
#selfBars {
  position: absolute; top: 56px; left: 50%; transform: translateX(-50%);
  width: min(560px, 80vw); display: flex; flex-direction: column; gap: 5px;
}
#selfBars .sbName {
  text-align: center; font-size: 14px; font-weight: 700; color: #fff;
  text-shadow: 0 1px 3px #000; letter-spacing: .3px;
}
#selfBars .sbNums { color: #ffcf3f; font-weight: 600; margin-left: 8px; font-size: 13px; }
#selfBars .sbBar {
  position: relative; height: 22px; border-radius: 7px; overflow: hidden;
  background: rgba(10,7,16,.72); border: 1px solid rgba(255,255,255,.2);
  box-shadow: 0 2px 8px rgba(0,0,0,.45);
}
#selfBars .sbBar.mana { height: 15px; }
#selfBars .sbFill { height: 100%; width: 0; }
#selfBars .sbBar.mana .sbFill { background: #4aa3ff; }
/* tiny XP strip under the mana bar — fills toward the next player level */
#selfBars .sbBar.xp { height: 5px; border-radius: 3px; }
#selfBars .sbBar.xp .sbFill { background: linear-gradient(90deg, #b98a2c, #ffd94a); }
#selfBars .sbLvl { color: #ffd94a; font-size: 12.5px; margin-right: 2px; }
#selfBars .sbTicks { position: absolute; inset: 0; pointer-events: none; }

/* passive + status chips directly under the mana bar: a centered box whose
   chips pack from the left to the right (and wrap downward when they overflow) */
#statusBar {
  display: flex; gap: 5px; justify-content: flex-start; flex-wrap: wrap;
  align-self: center; max-width: 100%; margin-top: 4px; pointer-events: auto;
}
#statusBar .chip {
  width: 27px; height: 27px; display: flex; align-items: center; justify-content: center;
  font-size: 15px; border-radius: 7px; cursor: help;
  background: rgba(10,7,16,.72); border: 1px solid rgba(255,255,255,.2);
}
#statusBar .chip.status { border-color: rgba(255,140,90,.55); }
#statusBar .chip.buff { border-color: rgba(120,220,255,.55); background: rgba(20,30,44,.72); }
#statusBar .chip.aegis { border-color: rgba(150,205,255,.9); box-shadow: 0 0 8px rgba(140,200,255,.45); animation: lavaPulse .9s ease-in-out infinite; }
#statusBar .chip.lava { border-color: #ff5a2f; background: rgba(60,14,6,.85); box-shadow: 0 0 10px rgba(255,90,47,.6); animation: lavaPulse .7s ease-in-out infinite; }
@keyframes lavaPulse { 0%,100% { box-shadow: 0 0 6px rgba(255,90,47,.5); } 50% { box-shadow: 0 0 14px rgba(255,90,47,.9); } }
#selfBars .sbShield { color: #7fd8ff; font-weight: 700; margin: 0 4px; }
#fxTip {
  position: fixed; transform: translateX(-50%); z-index: 30; max-width: 230px;
  background: rgba(10,7,16,.96); border: 1px solid #4a3320; color: #fff;
  font-size: 12px; line-height: 1.35; padding: 6px 9px; border-radius: 8px;
  pointer-events: none; text-align: center; box-shadow: 0 4px 14px rgba(0,0,0,.55);
}

/* main-menu language picker */
#langPick { display: flex; align-items: center; gap: 8px; justify-content: center; margin: 4px 0; flex-wrap: wrap; }
#langBtns, #charEdLangBtns { display: flex; gap: 6px; flex-wrap: wrap; }
.langBtn {
  width: auto; margin: 0; background: rgba(10,7,16,.5); border: 1px solid #4a3320; color: #dac7a2;
  border-radius: 8px; padding: 6px 11px; font-size: 12px; cursor: pointer;
}
.langBtn.active { border-color: #ffcf3f; color: #fff; box-shadow: 0 0 8px rgba(255,207,63,.3); }

/* Spell bar centered along the bottom-middle (desktop), hotkeys shown */
#spellbar {
  position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%);
  display: flex; gap: 8px;
  padding: 7px; border-radius: 13px;
  background: rgba(10,7,16,.4); backdrop-filter: blur(3px);
  /* #hud is pointer-events:none; opt back in for the chip hover tooltips and
     the shop-phase "level up!" click-through (combat clicks still reach the
     window handler by bubbling, so held-button casting is unchanged). */
  pointer-events: auto;
}
/* Rich hover tooltip for a hotbar spell chip (built in main.js hotbarTipHTML).
   Anchored above the hovered slot (JS sets left/top; transform lifts it up). */
#spellbarTip {
  position: fixed; transform: translate(-50%, -100%); z-index: 46; pointer-events: none;
  width: max-content; max-width: 300px; text-align: left;
  background: rgba(10,7,16,.96); border: 1px solid #4a3320; border-radius: 10px;
  padding: 8px 11px; box-shadow: 0 6px 18px rgba(0,0,0,.55);
}
#spellbarTip.hidden { display: none; }
#spellbarTip .hbHead { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
#spellbarTip .hbHead b { color: var(--el, #ffcf3f); font-size: 14px; }
#spellbarTip .hbKey {
  font-size: 11px; font-weight: 800; color: #ffdb5c; letter-spacing: .3px;
  padding: 1px 5px; border-radius: 5px; background: rgba(6,4,2,.6); border: 1px solid #4a3320;
}
#spellbarTip .hbLv { font-size: 11px; color: #c7b596; margin-left: auto; }
#spellbarTip .hbDesc { margin-top: 5px; font-size: 12px; line-height: 1.42; color: #e8dcc4; overflow-wrap: break-word; }
/* stack counter on the ⚡ charged status chip (1-3) */
.chip.status .chgN {
  font-size: 9px; font-weight: 800; color: #ffe14a; vertical-align: super;
  margin-left: -1px; text-shadow: 0 1px 1px #000;
}
.slot {
  position: relative; width: 58px; height: 58px;
  background: rgba(10,7,16,.66); border: 2px solid #4a3320; border-radius: 10px;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.slot .key {
  position: absolute; top: 2px; left: 3px; z-index: 3;
  font-size: 12px; font-weight: 800; color: #ffdb5c;
  text-shadow: 0 1px 2px #000, 0 0 3px #000; letter-spacing: .3px;
  padding: 0 3px; border-radius: 4px; background: rgba(6,4,2,.5);
}
.slot .key.long { /* spelled-out labels ("space bar") shrink to fit the chip */
  font-size: 9px; letter-spacing: 0; white-space: nowrap;
}
/* Controller-button chips (swapped in while a gamepad is the live input device).
   Xbox face buttons keep their signature colours; triggers/bumpers stay gold. */
.slot .key.pad { min-width: 14px; text-align: center; letter-spacing: 0; }
.slot .key.pad-a { color: #6cc04a; } /* A — green  */
.slot .key.pad-b { color: #e5453b; } /* B — red    */
.slot .key.pad-x { color: #3b7cf0; } /* X — blue   */
.slot .key.pad-y { color: #f2c521; } /* Y — yellow */
.slot .icon { font-size: 30px; line-height: 1; }
/* Pixel-art spell icons (CraftPix magic packs 671189 & 781160). Rendered crisp
   (no smoothing) and sized to sit inside the 58px slot clear of the corner
   badges; the nomana/silenced filters above still grey them like the emoji. */
.slot .icon img.pixIco {
  width: 36px; height: 36px; display: block;
  image-rendering: pixelated;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.6));
}
.slot .cover {
  position: absolute; inset: 0; background: rgba(0,0,0,.6);
  display: flex; align-items: center; justify-content: center;
  font-size: 19px; font-weight: 700; color: #fff;
}
.slot.active { border-color: var(--accent); box-shadow: 0 0 12px rgba(255,122,47,.4); }
/* Meditate slot pulsing green while the channel is active */
.slot.meditating { border-color: #8fe6b0; animation: medPulse 1.1s ease-in-out infinite alternate; }
@keyframes medPulse { from { box-shadow: 0 0 6px rgba(143,230,176,.35); } to { box-shadow: 0 0 16px rgba(143,230,176,.75); } }
/* Out of mana: pulse the Meditate chip in mana-blue so the player knows where to
   recover (suppressed while already channeling — that state uses medPulse above). */
.slot.needMed { border-color: #8fd8ff; animation: medNeedPulse 0.85s ease-in-out infinite alternate; }
@keyframes medNeedPulse { from { box-shadow: 0 0 7px rgba(143,216,255,.45); } to { box-shadow: 0 0 20px rgba(143,216,255,.95); } }
.slot .elbadge {
  position: absolute; bottom: 1px; right: 2px;
  font-size: 15px; line-height: 1; text-shadow: 0 1px 2px #000;
}
/* mana cost printed on the slot; slot dims when you can't afford it */
.slot .mana {
  position: absolute; bottom: 2px; left: 3px; z-index: 3;
  font-size: 11px; font-weight: 800; color: #8fd8ff;
  text-shadow: 0 1px 2px #000, 0 0 3px #000; line-height: 1;
  padding: 0 3px; border-radius: 4px; background: rgba(6,4,2,.5);
}
.slot.nomana .icon, .slot.nomana .elbadge { filter: grayscale(1) brightness(.55); opacity: .5; }
.slot.nomana .mana { color: #ff6a6a; }
/* Mana-Leech keystone: Meditate is disabled (damage refills mana instead). The
   chip reads as inert with a blood-red rim so it's clearly not a channel anymore. */
.slot.leech { border-color: #b33; opacity: .75; cursor: default; }
.slot.leech .icon { filter: drop-shadow(0 0 4px rgba(200,20,20,.6)); }
/* Charge pips: arcane Q (Spiral Orb) / W (Seeker Orb) bank 2-3 casts per cooldown.
   A dot per charge, lit ones = banked; sits above the recharge cover so you can
   read "2 of 3 ready" at a glance. */
.slot .charges {
  position: absolute; bottom: 3px; right: 3px; z-index: 4;
  display: flex; gap: 2px; pointer-events: none;
}
.slot .charges.hidden { display: none; }
.slot .charges i {
  width: 5px; height: 5px; border-radius: 50%;
  background: rgba(255,210,74,.22); box-shadow: 0 0 2px rgba(0,0,0,.85);
}
.slot .charges i.on { background: #ffd24a; box-shadow: 0 0 5px rgba(255,200,60,.95); }
/* DEPLETED: a charged slot with zero banked casts (a full Arcane Orbit ring, or a
   spent Q/W bank). Grey + desaturate the art and mute the chip so it visibly reads
   "not usable now"; it re-lights the instant a cast frees up (an orb is lost / a
   charge recharges). The pips stay full-brightness above so you can still count them. */
.slot.depleted .icon, .slot.depleted .elbadge { filter: grayscale(1) brightness(.5); opacity: .45; }
.slot.depleted { border-color: rgba(120,110,130,.35); background: rgba(10,7,16,.4); }
.slot.depleted .charges i.on { background: #cfd2da; box-shadow: 0 0 5px rgba(190,200,220,.85); } /* pips go cool-grey to match the locked state */
/* RECAST ARMED: a free two-cast follow-up is pending on this slot (Sawgale's
   recall, the harpoon yank, the aegis shatter…). The chip shows a lit pip while
   its cooldown is secretly still running, so it must read as LIVE — a breathing
   rim + warm lift, deliberately louder than a resting chip because the window is
   short and easy to miss mid-fight. Pips pulse too, so the "second charge" is
   what draws the eye. */
/* !important is load-bearing: buildSpellbar stamps `el.style.borderColor` INLINE on
   every elemental chip, and an inline style outranks any stylesheet rule, so a plain
   `border-color` here silently never applies. (The same trap already eats
   `.slot.depleted`'s border-color above — verified in a browser, 2026-07-17.) */
.slot.recast { border-color: #ffd24a !important; background: rgba(60,40,10,.55); animation: recastRim 0.9s ease-in-out infinite; }
.slot.recast .icon { filter: drop-shadow(0 0 5px rgba(255,210,74,.9)); }
.slot.recast .charges i.on { animation: recastPip 0.9s ease-in-out infinite; }
@keyframes recastRim {
  0%, 100% { box-shadow: 0 0 4px rgba(255,200,60,.45); }
  50%      { box-shadow: 0 0 11px rgba(255,210,74,.95); }
}
@keyframes recastPip {
  0%, 100% { transform: scale(1);    box-shadow: 0 0 5px rgba(255,200,60,.95); }
  50%      { transform: scale(1.45); box-shadow: 0 0 9px rgba(255,225,120,1); }
}
/* Players who asked for no motion still get the static warm rim, just no pulse. */
@media (prefers-reduced-motion: reduce) {
  .slot.recast, .slot.recast .charges i.on { animation: none; }
  .slot.recast { box-shadow: 0 0 9px rgba(255,210,74,.9); }
}
/* electric-quake SILENCE: every owned spell greys out until it wears off */
.slot.silenced { position: relative; }
.slot.silenced .icon, .slot.silenced .elbadge, .slot.silenced .mana, .slot.silenced .key {
  filter: grayscale(1) brightness(.5); opacity: .45;
}
.slot.silenced::before {
  content: "🔇"; position: absolute; inset: 0; z-index: 3;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; opacity: .8; pointer-events: none;
  text-shadow: 0 1px 3px #000;
}
#spellbar.silenced { animation: silencePulse 0.6s ease-in-out infinite alternate; }
@keyframes silencePulse { from { filter: none; } to { filter: brightness(1.15) drop-shadow(0 0 6px rgba(255,225,74,.55)); } }
/* Shop-phase "level up!" chevron: a gold banner bobbing above any hotbar slot
   that can take spell points RIGHT NOW (same slotSpendable rule as the shop
   tabs' gold shimmer). Clicking the chip routes to the Spells stall/panel on
   that slot's tab. The chip lifts its overflow clip so the banner can float
   above it — the cooldown cover gets its own radius to keep rounded corners. */
.slot.canlvl { overflow: visible; cursor: pointer; }
.slot.canlvl .cover { border-radius: 8px; }
.slot .lvlup {
  position: absolute; left: 50%; bottom: calc(100% + 5px); z-index: 5;
  width: 30px; height: 20px;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(180deg, rgba(52,29,11,.95), rgba(24,12,6,.95));
  border: 1px solid #a86a2a; border-radius: 6px;
  animation: lvlupBob 1.1s ease-in-out infinite;
}
.slot .lvlup.hidden { display: none; }
.slot .lvlup svg { display: block; width: 16px; height: 12px; }
@keyframes lvlupBob {
  0%, 100% { transform: translate(-50%, 0);    box-shadow: 0 0 8px rgba(255,180,60,.35), 0 2px 6px rgba(0,0,0,.55); }
  50%      { transform: translate(-50%, -4px); box-shadow: 0 0 14px rgba(255,190,70,.65), 0 2px 6px rgba(0,0,0,.55); }
}
/* the hover tooltip's matching call-to-action line */
#spellbarTip .hbLvlUp {
  margin-top: 6px; padding-top: 5px; border-top: 1px solid #3a2817;
  font-size: 11.5px; font-weight: 700; color: #ffd35a;
}

/* ---- Lobby ---- */
h1.small { font-size: 24px; }
/* Compact vertical rhythm so the whole lobby fits without a scrollbar. */
/* ================= Lobby — classic game-room layout =================
   A room-identity bar on top, a WC3-style roster on the left, framed map +
   game-settings + invite cards on the right, and a sticky ready/start bar
   at the bottom. The bronze frame comes from `.overlay > .panel`. */
#lobby .panel { text-align: left; padding: 0; width: min(1040px, 96vw); overflow: hidden; }
.lobbyPanel { display: flex; flex-direction: column; max-height: 92vh; max-height: 92svh; min-height: 0; }
/* Neutralize the base `button { width:100%; margin-top:20px }` for lobby controls
   (the old #lobby button reset is gone). cpArrow keeps its own fixed 36px width. */
#lobby .cpArrow { margin: 0; }
#lobby .tsBtn, #lobby .tpBtn, #lobby #startBtn { width: auto; margin: 0; }

/* --- room identity bar --- */
.lobbyHeader {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 13px 20px 11px; flex: none;
  border-bottom: 1px solid #3a2817;
  background: linear-gradient(180deg, rgba(72,42,16,.55), rgba(25,16,7,0));
}
.lobbyIdent { display: flex; align-items: center; gap: 12px; min-width: 0; }
.lobbyGlyph { font-size: 30px; line-height: 1; flex: none; filter: drop-shadow(0 0 9px rgba(255,140,50,.6)); }
.lobbyTitleWrap { min-width: 0; }
.lobbyTitleWrap h1.small { margin: 0; line-height: 1.05; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lobbySub { margin-top: 4px; }
.modeBadge {
  display: inline-block; font-size: 12px; font-weight: 700; color: #ffcf7a; letter-spacing: .3px;
  background: rgba(255,180,60,.12); border: 1px solid #6a4b23; border-radius: 999px; padding: 2px 11px;
}
.lobbyCount {
  flex: none; text-align: center; line-height: 1.05;
  background: #191007; border: 1px solid #3a2817; border-radius: 11px; padding: 6px 13px;
}
.lobbyCount #lobbyCountNum { display: block; font-size: 20px; font-weight: 800; color: #ffe7c2; }
.lobbyCount .lobbyCountLbl { font-size: 9.5px; text-transform: uppercase; letter-spacing: 1.2px; color: #b7a888; }

/* --- body: roster (left) · side cards (right) --- */
.lobbyBody {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
  gap: 14px; padding: 14px 20px; overflow-y: auto; align-items: start; min-height: 0;
}
.lobbyRoster, .lobbyCard {
  background: #150d06; border: 1px solid #3a2817; border-radius: 12px; padding: 11px 13px; margin: 0;
}
.lobbySide { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.cardHead {
  font-size: 11.5px; font-weight: 800; letter-spacing: .6px; text-transform: uppercase;
  color: #ffcf7a; margin-bottom: 9px; padding-bottom: 6px; border-bottom: 1px solid #2c1e10;
}

/* --- roster list (auto-scrolls when the roster is big) --- */
#lobbyList { display: flex; flex-direction: column; gap: 5px; max-height: 44vh; overflow-y: auto; padding-right: 3px; }
.lobbyRow {
  display: flex; align-items: center; gap: 8px; padding: 6px 9px; min-width: 0;
  background: #1c1208; border: 1px solid #33220f; border-radius: 9px; font-size: 14px;
}
.lobbyRow.me { border-color: #ffcf3f; box-shadow: inset 0 0 0 1px rgba(255,207,63,.22); }
.lobbyRow.host { background: linear-gradient(90deg, rgba(255,180,60,.12), #1c1208 42%); }
.lobbyRow .slotNo { font-size: 11px; font-weight: 800; color: #7a6444; width: 15px; text-align: center; flex: none; }
.lobbyRow .dot { width: 12px; height: 12px; border-radius: 50%; flex: none; box-shadow: 0 0 0 1px rgba(0,0,0,.45); }
.lobbyRow .who { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lobbyRow .crown { font-size: 13px; flex: none; }
/* GunBound-style ready lamp: a glowing green light when readied */
.lobbyRow .lamp { width: 13px; height: 13px; border-radius: 50%; flex: none; border: 1px solid rgba(0,0,0,.45); }
.lobbyRow .lamp.yes { background: radial-gradient(circle at 34% 30%, #c6ffcc, #35c24c); box-shadow: 0 0 9px rgba(80,230,110,.75); }
.lobbyRow .lamp.no  { background: #45351f; box-shadow: inset 0 0 3px rgba(0,0,0,.6); }
.lobbyRow .lamp.spec { background: radial-gradient(circle at 34% 30%, #c9f1ff, #3aa9e0); box-shadow: 0 0 8px rgba(90,190,255,.65); }

/* mage skin picker — same layout & square swatch as the old cursor picker */
#skinPick { display: flex; align-items: center; justify-content: center; gap: 12px; margin: 8px 0 4px; }
#skinPick .cpLabel { font-size: 13px; color: #d4c4a4; }
#skinPick .cpArrow {
  width: auto; margin: 0; padding: 4px 12px; font-size: 16px; border-radius: 8px;
  background: #281b0e; border: 1px solid #5c4526; color: var(--text);
}
#skinPick .cpArrow:hover { background: #3a2a14; }
#skinPreview { width: 34px; height: 34px; border-radius: 8px; border: 1px solid rgba(255,255,255,.45); box-shadow: 0 0 10px rgba(0,0,0,.4) inset; }
/* full-palette swatch grid under the arrow row — capped, self-contained scroll
   so a big palette doesn't stretch the Options panel into a scrollbar. */
.swGrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(26px, 1fr)); gap: 5px;
  margin: 6px 0 2px; max-height: 88px; overflow-y: auto; padding-right: 2px;
}
.swGrid .skinSw {
  width: 24px; height: 24px; padding: 0; border-radius: 6px; cursor: pointer;
  border: 2px solid transparent; box-shadow: 0 0 6px rgba(0,0,0,.4) inset; transition: transform .08s;
}
.swGrid .skinSw:hover { transform: scale(1.14); }
.swGrid .skinSw.sel { border-color: #fff; box-shadow: 0 0 8px rgba(255,255,255,.6); }
/* 💵 premium (locked) swatch: dimmed with a small padlock; still clickable to
   preview + open the paywall. Selected-while-previewing keeps the white ring. */
.swGrid .skinSw.locked { position: relative; opacity: .82; }
.swGrid .skinSw.locked::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: repeating-linear-gradient(45deg, transparent 0 3px, rgba(0,0,0,.28) 3px 6px);
  pointer-events: none;
}
.swGrid .skinSw.locked .swLock {
  position: absolute; right: -3px; top: -5px; font-size: 10px; line-height: 1;
  filter: drop-shadow(0 1px 1px #000); pointer-events: none;
}
.swGrid .skinSw.locked.sel { border-color: #ffd94a; box-shadow: 0 0 8px rgba(255,217,74,.6); }

/* Options split into three tabbed sections (Controls / General / Audio & Video);
   only the active section shows, and each lays its groups out in two columns so
   everything — even with the hotkey remapper open — fits without scrolling. */
.optionsPanel { width: min(680px, 94vw); }
#optionsBody { display: block; }
#optTabs { display: flex; gap: 8px; margin: 0 0 12px; }
.optTab {
  flex: 1; width: auto; margin: 0; padding: 8px 10px; border-radius: 10px;
  background: #191007; border: 2px solid #3a2817; color: var(--text);
  font-size: 13.5px; font-weight: 700; cursor: pointer;
}
.optTab:hover { background: #221808; }
.optTab.active { border-color: var(--accent); background: #2e1f10; box-shadow: 0 0 12px -2px var(--accent); }
.optSection { display: flex; gap: 22px; align-items: flex-start; }
.optSection .optCol { flex: 1 1 0; min-width: 0; }
.optSection .optCol > *:first-child { margin-top: 0; }
.optSection .controls { margin-top: 0; }
#comfortPick .cpLabel { display: block; font-size: 13px; color: #d4c4a4; margin-bottom: 7px; }

/* ---- hotkey remapping + controller hint (main menu) ---- */
/* Display options: window mode + resolution (Options panel, 2nd column) */
#displayPick { margin: 4px 0 10px; }
#displayPick .cpLabel { display: block; font-size: 13px; color: #d4c4a4; margin-bottom: 7px; }
.winModeBtns { display: flex; gap: 6px; flex-wrap: wrap; }
.winModeBtn {
  width: auto; margin: 0; background: rgba(10,7,16,.5); border: 1px solid #4a3320; color: #dac7a2;
  border-radius: 8px; padding: 6px 11px; font-size: 12px; cursor: pointer;
}
.winModeBtn:hover { background: rgba(58,42,20,.6); }
.winModeBtn.active { border-color: #ffcf3f; color: #fff; box-shadow: 0 0 8px rgba(255,207,63,.3); }
.resPick { display: flex; align-items: center; gap: 8px; margin-top: 9px; font-size: 12px; color: #d4c4a4; }
.resPick select {
  width: auto; margin: 0; background: #281b0e; border: 1px solid #5c4526; color: var(--text);
  border-radius: 8px; padding: 5px 9px; font-size: 12px; cursor: pointer;
}
.resPick select:disabled { opacity: .45; cursor: default; }
/* Camera-zoom stop slider (touch mode only — see #zoomPick in setupDisplayOptions) */
#zoomPick input { width: auto; flex: 1; min-width: 80px; max-width: 150px; margin: 0; accent-color: #ffcf3f; cursor: pointer; }
#zoomPctLabel { min-width: 38px; text-align: right; color: var(--text); }

/* Sound options (Options panel, 2nd column): experimental-VFX-sounds checkbox */
#soundPick { margin: 4px 0 10px; }
#soundPick .cpLabel { display: block; font-size: 13px; color: #d4c4a4; margin-bottom: 7px; }
.optCheck { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #dac7a2; cursor: pointer; }
.optCheck input { width: auto; margin: 0; accent-color: #ffcf3f; cursor: pointer; }
/* Aim assist is a touch-only mechanic (assistTarget is gated on the touch
   overlay), so desktop/gamepad players never see the row — a checkbox that
   does nothing would just read as broken. */
body:not(.touchMode) #aimAssistPick { display: none; }
#aimAssistPick { margin: 10px 0 0; }

/* ---- Character editor: live sprite preview + appearance pickers.
   Reuses the Options widgets (.cpLabel/.cpArrow/.swGrid/.winModeBtns); the
   preview is the real magePortrait, upscaled pixelated. */
.charEdPanel { width: min(760px, 94vw); }
#charEdBody { display: flex; gap: 22px; align-items: flex-start; }
#charEdBody .optCol { flex: 1 1 0; min-width: 0; }
.charEdPreviewCol { flex: 0 0 auto !important; display: flex; flex-direction: column; align-items: center; }
#charEdPortraitBox { display: flex; flex-direction: column; align-items: center; gap: 6px; }
#charEdPortraitBox small { font-size: 11px; color: #a89a78; max-width: 260px; text-align: center; }
#charEdPortrait {
  display: flex; align-items: center; justify-content: center;
  width: 264px; height: 264px; border-radius: 10px;
  background: radial-gradient(ellipse at 50% 72%, #2c1c0c 0%, #191007 70%);
  border: 1px solid #5c4526;
}
#charEdSpriteCv {
  /* undo the global `canvas { position: fixed; inset: 0 }` (game canvas) rule */
  position: static; inset: auto; cursor: default;
  width: 240px; height: 240px; image-rendering: pixelated;
}
#charEdRotate { display: flex; gap: 10px; }
#charEdRotate .cpArrow {
  width: auto; margin: 0; padding: 4px 16px; font-size: 16px; border-radius: 8px;
  background: #281b0e; border: 1px solid #5c4526; color: var(--text);
}
#charEdRotate .cpArrow:hover { background: #3a2a14; }
.charEdField { margin: 6px 0; }
.charEdField .cpLabel { display: block; font-size: 13px; color: #d4c4a4; margin-bottom: 4px; }
/* Kill .swGrid's capped-scroll behavior (meant for Options' grid) so no
   scrollbar can ever appear, and shrink the swatches a notch — four palettes
   (incl. the 28-color robe grid) must fit a 720p panel without scrolling. */
#charEditor .swGrid { max-height: none; overflow: visible; padding-right: 0; grid-template-columns: repeat(auto-fill, 22px); gap: 4px; }
#charEditor .swGrid .skinSw { width: 20px; height: 20px; }
/* Hair-style swatches reuse the .skinSw square but carry a centered style
   number (styles have no single color, so the square is tinted with the hair
   color and the number identifies it). */
.swGrid .skinSw.styleSw { display: flex; align-items: center; justify-content: center; }
.styleSw .styleNum {
  font-size: 11px; font-weight: 700; color: #fff; line-height: 1;
  text-shadow: 0 1px 2px rgba(0,0,0,.85); pointer-events: none;
}
.charEdActions { display: flex; gap: 8px; margin-top: 12px; }
.charEdActions button { width: auto; margin: 0; font-size: 13px; padding: 8px 12px; }
/* First-join setup strip (a fresh account's first sign-in): one-time name pick
   + Save & Play. Compact overrides so the panel still fits 720p unscrolled. */
#charEdSetup { margin-top: 10px; border-top: 1px solid #5c4526; padding-top: 8px; }
#charEdSetup .welcomeName { margin: 0 0 6px; }
#charEdSetup .welcomeName input { font-size: 15px; padding: 8px; }
#charEdSetup .welcomeName span { font-size: 12px; color: #d4c4a4; }
#charEdSaveBtn { width: 100%; font-size: 15px; padding: 10px; margin-top: 2px; }
#charEdSetup .status { min-height: 16px; margin: 6px 0 0; }

/* Quit Game (main menu): a quieter ghost button set apart from the play buttons */
#menu .menuBtns .quitBtn { color: #ff9a9a; border-color: #6a2626; }
#menu .menuBtns .quitBtn:hover { background: #3a1414; }

#hotkeyPick { margin: 8px 0 4px; }
#hotkeyPick #hotkeyToggle { margin: 0; font-size: 13px; padding: 8px 10px; }
#hotkeyPanel { margin-top: 8px; }
#hotkeyList {
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px;
  background: #191007; border: 1px solid #3a2817; border-radius: 10px; padding: 10px;
}
.hkRow { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.hkName { font-size: 12px; color: #d4c4a4; }
.hkKey {
  width: auto; margin: 0; padding: 4px 10px; min-width: 46px; font-size: 13px; font-weight: 700;
  border-radius: 7px; background: #281b0e; border: 1px solid #5c4526; color: var(--text);
}
.hkKey:hover { background: #3a2a14; filter: none; }
.hkKey.capturing { border-color: var(--accent); color: #ffcf3f; background: #2a1a10; animation: countPulse .7s ease-in-out infinite; }
#hotkeyReset { margin-top: 8px; font-size: 12px; padding: 6px 10px; }
.hkNote { margin-top: 8px; font-size: 11.5px; color: #9d8b6c; line-height: 1.4; }

/* ---- multikill announcer banner ---- */
#multikill {
  position: absolute; top: 26%; left: 50%; transform: translateX(-50%);
  text-align: center; font-weight: 900; font-size: 46px; letter-spacing: 1px;
  color: #ffcf3f; text-shadow: 0 0 18px rgba(255,120,40,.9), 0 3px 8px #000;
  pointer-events: none; z-index: 8; white-space: nowrap;
}
#multikill .mkSub { font-size: 18px; font-weight: 700; color: #ff8a3d; margin-top: 2px; letter-spacing: 0; }
#multikill .mkWho { color: #fff; text-shadow: 0 0 14px rgba(255,255,255,.55), 0 3px 8px #000; }
#multikill.pop { animation: mkPop .5s cubic-bezier(.2,1.4,.4,1) both; }
@keyframes mkPop { 0% { transform: translateX(-50%) scale(.4); opacity: 0; } 60% { transform: translateX(-50%) scale(1.15); opacity: 1; } 100% { transform: translateX(-50%) scale(1); opacity: 1; } }

/* ---- ⬆️ level-up toast (your own level-ups only) ---- */
#lvlToast {
  position: absolute; top: 36%; left: 50%; transform: translateX(-50%);
  text-align: center; font-weight: 900; font-size: 26px; letter-spacing: .5px;
  color: #ffd94a; text-shadow: 0 0 14px rgba(255,200,60,.85), 0 3px 8px #000;
  pointer-events: none; z-index: 8; white-space: nowrap;
}
#lvlToast.pop { animation: mkPop .5s cubic-bezier(.2,1.4,.4,1) both; }

/* ---- 🛠 server-update pill (deploy drain notice, see {t:"maint"}) ---- */
#maintNote {
  position: fixed; top: 10px; left: 50%; transform: translateX(-50%);
  background: rgba(42, 27, 9, .94); border: 1px solid #b97a2a; border-radius: 999px;
  color: #ffd94a; font-weight: 700; font-size: 13px; padding: 6px 16px;
  text-shadow: 0 1px 3px #000; box-shadow: 0 4px 14px rgba(0,0,0,.5);
  pointer-events: none; z-index: 90; white-space: nowrap;
}

/* lobby game-mode chooser — mode TILES in a responsive grid */
.gmOpt { margin: 6px 0; }
.gmLabel { display: block; font-size: 12px; color: #d4c4a4; margin-bottom: 3px; }
#gmBtns { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 6px; }

/* Lobby map-theme picker: ◀ [big preview] ▶ (fills the Battleground card) */
.themeSel { display: flex; align-items: center; justify-content: center; gap: 10px; }
.themeSel .cpArrow { background: #281c10; color: #e9e0cf; border: 1px solid #5a4629;
  border-radius: 8px; width: 36px; height: 36px; font-size: 17px; cursor: pointer; flex: none; }
.themeSel .cpArrow:hover:not(:disabled) { background: #3a2a14; }
.themeSel .cpArrow:disabled { opacity: .35; cursor: default; }
.themeMid { display: flex; flex-direction: column; align-items: center; gap: 6px; flex: 1; min-width: 0; }
#themePreview { position: static; inset: auto; cursor: default; /* override the global fullscreen canvas rule */
  width: 100%; max-width: 340px; height: auto; aspect-ratio: 240 / 150; border-radius: 10px; border: 1px solid #5a4629;
  background: #0e0b08; box-shadow: 0 4px 16px rgba(0,0,0,.4); image-rendering: auto; }
.themeName { font-size: 14px; font-weight: 700; color: #ffcf7a; letter-spacing: .3px; }
.gmBtn {
  width: auto; margin: 0; background: rgba(10,7,16,.5); border: 1px solid #4a3320;
  color: #dac7a2; border-radius: 9px; padding: 8px 11px; font-size: 11.5px; line-height: 1.3;
  cursor: pointer; text-align: left; transition: border-color .12s, box-shadow .12s, transform .06s;
}
.gmBtn:hover:not(:disabled):not(.active) { border-color: #6a4b23; transform: translateY(-1px); }
.gmBtn.active { border-color: #ffcf3f; color: #fff; background: rgba(255,180,60,.10);
  box-shadow: 0 0 10px rgba(255,207,63,.3), inset 0 0 0 1px rgba(255,207,63,.25); }
.gmBtn:disabled { cursor: default; opacity: .8; }
/* Locked mode (host hasn't played enough matches to host it): dimmed + not-allowed,
   with the "N matches to unlock" hint appended to the label by updateLobby(). */
.gmBtn.locked { cursor: not-allowed; opacity: .5; border-style: dashed; color: #9a8567; }

/* ---- Teams option (host controls) ---------------------------------------- */
#teamCfg { margin: 4px 0 2px; padding: 6px 8px; border: 1px solid #4a3320;
  border-radius: 8px; background: rgba(10,7,16,.35); }
.teamCfgRow { display: flex; align-items: center; gap: 8px; margin: 3px 0; flex-wrap: wrap; }
.teamCfgRow .gmLabel { margin: 0; min-width: 66px; }
#teamSizeBtns, #teamPickBtns { display: flex; gap: 4px; flex-wrap: wrap; }
.tsBtn, .tpBtn { background: rgba(10,7,16,.5); border: 1px solid #4a3320; color: #dac7a2;
  border-radius: 7px; padding: 4px 9px; font-size: 11.5px; cursor: pointer; }
.tsBtn.active, .tpBtn.active { border-color: #ffcf3f; color: #fff; box-shadow: 0 0 7px rgba(255,207,63,.3); }
.tsBtn:disabled, .tpBtn:disabled { cursor: default; opacity: .6; }

/* ---- Team-picker squares (everyone) -------------------------------------- */
#teamPicker { margin: 6px 0 2px; }
.teamSquares { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 8px; }
.teamSquare { border: 2px solid color-mix(in srgb, var(--tc) 55%, #2a2016);
  border-radius: 10px; padding: 7px 8px; background: color-mix(in srgb, var(--tc) 10%, rgba(10,7,16,.55));
  transition: border-color .12s, box-shadow .12s, transform .08s; }
/* The whole colored block is the button: names/slots must never eat the press or
   let a stray drag turn it into a text selection. */
.teamSquares.pick .teamSquare.pickable { cursor: pointer; user-select: none; }
.teamSquares.pick .teamSquare.pickable * { pointer-events: none; }
.teamSquares.pick .teamSquare.pickable:active { transform: translateY(1px); }
.teamSquares.pick .teamSquare.pickable:hover { border-color: var(--tc);
  box-shadow: 0 0 12px color-mix(in srgb, var(--tc) 45%, transparent); transform: translateY(-1px); }
.teamSquare.mine { border-color: #ffcf3f; box-shadow: 0 0 10px rgba(255,207,63,.28); }
.teamSquare.locked { opacity: .5; cursor: not-allowed; }
/* Auto-balance: the server decides teams, so the squares are read-only. Grey them
   out + show a not-allowed cursor so nobody clicks expecting to move (your own
   square stays legible). See renderTeamPicker / joinTeam (server ignores picks). */
.teamSquares.auto .teamSquare { cursor: not-allowed; opacity: .55; filter: saturate(.6); }
.teamSquares.auto .teamSquare.mine { opacity: .9; filter: none; }
.tsHead { display: flex; align-items: center; gap: 6px; font-weight: 700; font-size: 13px;
  color: #f0e6d2; margin-bottom: 5px; }
.tsHead .dot { width: 12px; height: 12px; border-radius: 50%; flex: none; }
.tsHead .tsName { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tsHead .tsCount { font-size: 12px; color: #d4c4a4; font-weight: 600; }
.tsHead .tsFull { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px;
  color: #ff9a7a; border: 1px solid #7a3a28; border-radius: 5px; padding: 1px 4px; }
.tsSlots { display: flex; flex-direction: column; gap: 3px; }
.tsSlot { display: flex; align-items: center; gap: 6px; font-size: 12px; color: #dac7a2;
  padding: 2px 4px; border-radius: 5px; background: rgba(0,0,0,.18); min-height: 20px; }
.tsSlot .dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.tsSlot.bot { opacity: .62; font-style: italic; }
.tsSlot.you { color: #fff; font-weight: 700; background: rgba(255,207,63,.15); }
.tsSlot.empty { opacity: .4; font-style: italic; justify-content: center; }
.teamHint { font-size: 11.5px; color: #b7a888; text-align: center; margin: 6px 2px 0; }
/* survival: eliminated rows + lives count in the scoreboard */
#scoreboard .row.elim { opacity: .4; }
#scoreboard .livesN { color: #ff8a9a; margin-left: 3px; font-size: 11px; }
/* "scan to join" invite card (only shown when an ngrok tunnel is up) */
/* Compact: QR on the left, title + link + copy stacked on the right, so the card
   is short enough to sit beside the map picker without scrolling the lobby. */
#lobbyInvite {
  display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 4px 10px;
  margin: 0; padding: 9px 11px;
  background: #191007; border: 1px solid #3a2817; border-radius: 12px;
}
#lobbyInvite .inviteTitle { grid-column: 1 / -1; font-size: 12.5px; font-weight: 700; color: #ffcf3f; }
#lobbyInvite .qr {
  grid-row: span 2; width: 92px; height: 92px; border-radius: 8px; background: #fff; padding: 5px;
  image-rendering: pixelated;
}
#lobbyInvite .inviteUrl {
  font-size: 12px; color: #7fe0ff; word-break: break-all; text-align: left; text-decoration: none; align-self: end;
}
#lobbyInvite .inviteUrl:hover { text-decoration: underline; }
button.ghost {
  background: transparent; border: 1px solid #5c4526; color: var(--text);
  font-size: 14px; margin-top: 10px;
}
button.ghost:hover { background: #281b0e; }
button.readied { background: linear-gradient(180deg, #5ee06a, #34b84a); color: #05230b; }

/* ---- Main menu (Play / Options / Rank) — compact, never scrolls ---- */
/* The whole menu is auto-scaled to fit short viewports (see fitMenuScale in
   main.js): it sizes to its natural height and JS shrinks it uniformly rather
   than showing a scrollbar or reflowing the buttons. */
#menu .menuHome {
  max-height: none;
  overflow: visible;
  transform-origin: 50% 50%;
  will-change: transform;
}
.menuHome { text-align: center; }
/* Short viewports (phone landscape ~412px tall, or a squat desktop window):
   the single column of tall molten-frame buttons + leaderboard is far taller
   than the screen, so fitMenuScale (main.js) would shrink it past legibility.
   Instead, reflow the secondary buttons into a 2-up grid, keep Quick Gameplay
   as a full-width hero, and drop the tagline + leaderboard — that lands the
   auto-fit scale near 1.0 with everything on-screen and readable. */
@media (max-height: 520px) {
  #menu .menuHome .tag { display: none; }
  #menu .menuHome #profilePanel { display: none; }
  #menu .menuHome .logoH .bmgLogo { height: 50px; }
  #menu .menuHome .logoH { margin: 0 0 2px; }
  #menu .menuBtns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 7px;
    margin-top: 10px;
  }
  #menu .menuBtns > button { margin-top: 0; width: 100%; }
  #menu .menuBtns #playMenuBtn { grid-column: 1 / -1; }
  /* the hero entry of each sub-page (Quick Gameplay / Free Training) spans the row */
  #menu .menuBtns > button.quick { grid-column: 1 / -1; }
  #menu .menuHome .status { margin-top: 6px; }
}
/* sign-in / register screen (the old first-run welcome, repurposed) */
.welcomeName { display: block; text-align: left; margin: 18px 0 4px; }
.welcomeName input { font-size: 18px; padding: 13px; text-align: center; }
#welcome #welcomeBtn { font-size: 19px; padding: 15px; margin-top: 14px; width: 100%; }
.authPanel .welcomeName { margin: 12px 0 2px; }
.authPanel .welcomeName input { font-size: 16px; padding: 11px; }
/* show-password toggle sits inside the password field; pad the input so
   centered text never slides under the eye button */
.pwField { position: relative; display: block; }
.authPanel .pwField input { padding-right: 42px; }
.pwToggle {
  position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
  width: auto; margin: 0; padding: 4px 6px; background: none; border: none;
  font-size: 17px; line-height: 1; cursor: pointer; opacity: .55; filter: grayscale(1);
}
.pwToggle:hover { opacity: 1; filter: none; }
.pwToggle.on { opacity: 1; filter: none; }
.pwHint { display: block; margin: 4px 2px 0; font-size: 12px; color: #8d7a58; text-align: left; }
.pwHint.ok { color: #7ec27e; }
.authTabs { display: flex; gap: 8px; margin: 4px 0 2px; }
.authTabs .authTab {
  flex: 1; margin-top: 0; padding: 9px; font-size: 14px; font-weight: 700;
  background: transparent; border: 1px solid #5c4526; border-radius: 9px; color: var(--text);
}
.authTabs .authTab:hover { background: #281b0e; }
.authTabs .authTab.active {
  background: linear-gradient(180deg, #ff8a3d, #f0641e);
  border-color: transparent; color: #1a0d00;
}
.authOauth { margin-top: 14px; }
.authOr { margin: 0 0 8px; font-size: 12px; color: #8d7a58; text-align: center; }
#authOauthBtns { display: flex; gap: 8px; }
.oauthBtn {
  flex: 1; margin-top: 0; padding: 10px; font-size: 14px; font-weight: 700;
  border: 1px solid #4a3320; border-radius: 9px; background: #191007; color: var(--text);
}
.oauthBtn.google:hover { border-color: #ea4335; }
.oauthBtn.facebook:hover { border-color: #1877f2; }
.oauthBtn.instagram:hover { border-color: #e1306c; }
.authLinks { display: flex; gap: 14px; justify-content: center; margin: 12px 0 0; }
.linkBtn {
  width: auto; margin: 0; padding: 0; background: none; border: none;
  color: #c7b596; font-size: 12px; text-decoration: underline; cursor: pointer; font-weight: 500;
}
.linkBtn:hover { color: #ffcf3f; filter: none; }
.accountRow { margin: 2px 0 0; font-size: 12px; color: #c7b596; display: flex; gap: 10px; justify-content: center; align-items: baseline; }
.accountRow .linkBtn { font-size: 12px; }

/* Report & Feedback (cheater / bug / feedback tickets) */
.reportPanel { width: min(440px, 92vw); text-align: left; }
.reportTypes { display: flex; gap: 8px; margin-top: 12px; }
.reportTypes .reportType {
  flex: 1; margin-top: 0; padding: 9px 4px; font-size: 13px; font-weight: 700;
  background: transparent; border: 1px solid #5c4526; border-radius: 9px; color: var(--text);
}
.reportTypes .reportType.active {
  background: linear-gradient(180deg, #ff8a3d, #f0641e);
  border-color: transparent; color: #1a0d00;
}
.reportPanel textarea {
  width: 100%; padding: 11px 12px; margin-top: 4px; resize: vertical; min-height: 96px;
  background: #191007; border: 1px solid #4a3320; border-radius: 9px;
  color: var(--text); font-size: 14px; outline: none; font-family: inherit;
}
.reportPanel textarea:focus { border-color: var(--accent); }
.shake { animation: shake .38s; border-color: #ff5a5a !important; }
@keyframes shake { 0%,100% { transform: translateX(0); } 20%,60% { transform: translateX(-7px); } 40%,80% { transform: translateX(7px); } }
.menuBtns { margin-top: 16px; }
/* Menu sub-pages (▶ Play / 🎯 Training / ⚙ Options): the same molten plate as the
   home page, but just a titled column of entries — keep the stack narrow and
   centred so a 3-entry page doesn't sprawl to the plate's full width. */
#menu .menuPage:not(#menuHome) .menuBtns { max-width: 340px; margin: 16px auto 4px; }
.lobbyRow.botsRow { opacity: .7; font-style: italic; justify-content: center; }

/* Solo "Start now" — the host force-start button, promoted to a primary CTA when
   you're the only human (starting the match against bots). */
#startBtn.solo {
  display: block; opacity: 1; color: #05231f; font-weight: 800;
  background: linear-gradient(180deg, #3ff0c0, #16b48c); border: none;
  box-shadow: 0 6px 18px -6px rgba(22,180,140,.7);
}
#startBtn.solo:hover { filter: brightness(1.06); }

/* Generic confirm dialog (sits above every other overlay). */
#confirmModal { z-index: 60; }
.confirmPanel { width: min(400px, 92vw); text-align: center; }
.confirmPanel #confirmMsg { margin: 8px 0 18px; color: #e4d6ba; line-height: 1.5; }
/* ---- Plan-card chrome (shared by the first-town build intro) ---- */
.planPanel { width: min(780px, 94vw); }
.planCards { display: flex; gap: 12px; margin: 12px 0 4px; }
.planCard {
  flex: 1; width: auto; margin: 0; padding: 13px 14px; text-align: left;
  background: rgba(20, 12, 30, .55); border: 1px solid rgba(255, 211, 90, .35);
  border-radius: 12px; color: #e4d6ba; font-size: 12.5px; font-weight: 400;
  transition: transform .08s ease, border-color .15s, box-shadow .15s, background .15s;
}
.planCard:hover {
  border-color: #ffd35a; background: rgba(255, 211, 90, .08);
  box-shadow: 0 0 16px rgba(255, 211, 90, .3); transform: translateY(-2px); filter: none;
}
.planCard b { display: block; font-size: 16px; margin-bottom: 6px; color: #ffd35a; }
.planCard span { display: block; line-height: 1.45; color: #cdbfa4; }
.planPanel .confirmBtns button.ghost { margin-top: 8px; }
/* ---- First-town build intro (craft your own vs prebuilt build) ----
   Reuses the planPanel/planCard chrome; the prebuilt picker is a vertical list
   of combined spell+skill archetype cards that SCROLLS INTERNALLY so the
   overlay itself never grows a scrollbar. */
.biPanel { width: min(720px, 94vw); }
#buildIntroList { max-height: min(430px, 58vh); overflow-y: auto; margin: 10px 0 4px; display: flex; flex-direction: column; gap: 8px; text-align: left; padding-right: 2px; }
.biCard {
  width: 100%; margin: 0; padding: 10px 12px; text-align: left;
  background: rgba(20, 12, 30, .55); border: 1px solid rgba(255, 211, 90, .35);
  border-radius: 12px; color: #e4d6ba; font-size: 12.5px; font-weight: 400; cursor: pointer;
}
.biCard:hover { border-color: #ffd35a; background: rgba(255, 211, 90, .08); box-shadow: 0 0 14px rgba(255, 211, 90, .3); }
.biCard b { display: block; font-size: 15px; margin-bottom: 3px; color: #ffd35a; }
.biCard .biDesc { display: block; line-height: 1.45; color: #cdbfa4; }
.biCard .biRow { display: flex; flex-wrap: wrap; gap: 3px 16px; margin-top: 6px; font-size: 12px; color: #e8dcc0; }
.biCard .biRow .lbl { color: #9d8b6c; }
.confirmBtns { display: flex; gap: 10px; justify-content: center; }
.confirmBtns button { flex: 1; }
.menuBtns .ghost { margin-top: 10px; }

/* Sub-page header: ← Back hugs the left, the title stays centred on the plate
   (a 3-column grid, not space-between, so the back plate doesn't push it off). */
#menu .menuPage .browserTop { display: grid; grid-template-columns: 1fr auto 1fr; }
#menu .menuPage .browserTop .menuBack { justify-self: start; }

/* ===== Molten-lava fire skin (CraftPix rpg-game-ui) =========================
   Every overlay (main menu, welcome, pause, options, codex, server browser,
   lobby, shop, confirm) wears the fire UI kit: a dark bronze-framed window
   (border-image) with glowing molten-plate primary buttons. The shared purple
   chrome (fields, borders, rows, tabs, labels) was warmed to ember above so the
   interiors read as one theme with the frame. */

/* Bronze-framed window for every overlay panel */
.overlay > .panel {
  background: transparent;                 /* the frame's fill provides the interior */
  border: 11px solid transparent;
  border-image: url("assets/menu-ui/panel.png") 8 fill / 11px / 0 stretch;
  border-radius: 0;
  box-shadow:
    0 24px 64px rgba(0,0,0,.72),
    0 0 46px -8px rgba(255,120,40,.24);
}

/* Glowing molten plates for the primary calls-to-action across the menus. */
#menu .menuBtns button,
#welcome #welcomeBtn,
#escMenu .menuBtns button,
#lobby #readyBtn,
#shop #shopReadyBtn,
#confirmModal .confirmBtns button,
#renameModal .confirmBtns button {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 100%;
  min-height: 56px;
  padding: 12px 26px 16px;                 /* extra bottom pad lifts the label off the skirt */
  background: url("assets/menu-ui/btn.png") center / 100% 100% no-repeat;
  border: none; border-radius: 0;
  color: #ffe7c2;
  font-size: 16px; font-weight: 800; letter-spacing: .4px;
  text-shadow: 0 1px 2px #000, 0 2px 7px rgba(0,0,0,.75);
  box-shadow: none;
  filter: none;
  transition: filter .12s ease, transform .06s ease;
}
#menu .menuBtns button:hover,
#welcome #welcomeBtn:hover,
#escMenu .menuBtns button:hover,
#lobby #readyBtn:hover,
#shop #shopReadyBtn:hover,
#confirmModal .confirmBtns button:hover,
#renameModal .confirmBtns button:hover {
  background-image: url("assets/menu-ui/btn-hover.png");
  filter: brightness(1.07);
}
#menu .menuBtns button:active,
#welcome #welcomeBtn:active,
#escMenu .menuBtns button:active,
#lobby #readyBtn:active,
#shop #shopReadyBtn:active,
#confirmModal .confirmBtns button:active,
#renameModal .confirmBtns button:active {
  background-image: url("assets/menu-ui/btn-push.png");
  transform: translateY(1px);
}
/* A paywall's Pay button when purchasing isn't live yet — greyed & inert.
   Declared after :hover/:active so it wins on a hovered disabled button. */
#confirmModal .confirmBtns button:disabled {
  filter: grayscale(.7) brightness(.75);
  opacity: .65;
  cursor: default;
  transform: none;
}
/* the vertical menu stacks cap their width and center each plate */
#menu .menuBtns button,
#welcome #welcomeBtn,
#escMenu .menuBtns button {
  max-width: 320px; margin: 11px auto 0; min-height: 60px; padding: 12px 30px 17px; font-size: 17px;
}
/* the "readied" (green) state keeps its meaning even on a molten plate */
#lobby #readyBtn.readied,
#shop #shopReadyBtn.readied {
  background: linear-gradient(180deg, #5ee06a, #34b84a); color: #05230b;
  filter: none;
}
/* ▶ Play — the primary call-to-action — stands a little taller with a warm glow */
#menu .menuBtns #playMenuBtn,
#welcome #welcomeBtn {
  min-height: 66px; font-size: 19px;
}
#menu .menuBtns #playMenuBtn {
  filter: drop-shadow(0 0 12px rgba(255,140,50,.5));
  animation: menuCtaGlow 2.6s ease-in-out infinite;
}
@keyframes menuCtaGlow {
  0%, 100% { filter: drop-shadow(0 0 8px rgba(255,140,50,.35)); }
  50%      { filter: drop-shadow(0 0 16px rgba(255,170,70,.65)); }
}
@media (prefers-reduced-motion: reduce) {
  #menu .menuBtns #playMenuBtn { animation: none; }
}

.browserTop h1 { margin: 0; }

/* host option checkbox */
.opt {
  display: flex; align-items: center; gap: 9px; margin: 12px 0 2px;
  font-size: 13.5px; color: #d4c4a4; cursor: pointer;
}
.opt input { width: 18px; height: 18px; accent-color: var(--accent); margin: 0; flex: none; }
.opt input:disabled { cursor: default; }
/* #hostOpts is a .lobbyCard (frame comes from that); these just tune spacing. */
#hostOpts .opt { margin: 8px 0 2px; }
#hostOpts .gmOpt + .gmOpt, #hostOpts .gmOpt + .opt { margin-top: 10px; padding-top: 9px; border-top: 1px solid #2c1e10; }
.gmOpt { margin: 2px 0; }

/* ---- Lobby sticky action bar (spectate · status · ready/start) ---- */
.lobbyFooter {
  display: flex; align-items: center; gap: 14px; flex: none;
  padding: 11px 20px 13px; border-top: 1px solid #3a2817;
  background: linear-gradient(0deg, rgba(72,42,16,.42), rgba(25,16,7,0));
}
.lobbyFooter #spectateOpt { margin: 0; flex: 0 1 auto; font-size: 12.5px; }
.lobbyFooter #lobbyStatus { flex: 1 1 auto; margin: 0; text-align: center; font-size: 13px; min-width: 0; }
#lobbyActions { display: flex; align-items: center; gap: 10px; flex: none; margin: 0; }
#lobby #startBtn { margin: 0; min-height: 44px; padding: 8px 16px; white-space: nowrap; }
/* Solo host: "Start now" is the only CTA — promote it from ghost to a gold plate. */
#lobby #startBtn.solo {
  background: linear-gradient(180deg, #ffcf3f, #e0952a); color: #2a1806; border: none;
  font-weight: 800; min-height: 52px; padding: 10px 26px; box-shadow: 0 0 13px rgba(255,180,60,.42);
}
#lobby #startBtn.solo:hover { filter: brightness(1.06); }
#lobby #readyBtn { margin: 0; width: auto; min-width: 190px; min-height: 52px; padding: 10px 30px 14px; }
@media (max-width: 720px) {
  .lobbyFooter { flex-wrap: wrap; }
  .lobbyFooter #lobbyStatus { order: 3; flex-basis: 100%; }
  #lobbyActions { flex: 1 1 auto; justify-content: flex-end; }
}

/* ---- Short-screen lobby: shrink to fit so the panel never grows a scrollbar.
   The right column's map preview is the tallest element, so it scales
   CONTINUOUSLY with viewport height (no stair-step gaps between breakpoints);
   the discrete tiers then tighten paddings, gaps and type. Keyed on viewport
   HEIGHT (the binding constraint); placed after the base lobby rules so they win
   by source order. */
#themePreview { max-width: min(340px, 24vh); }
@media (max-height: 900px) {
  .lobbyPanel { max-height: 95vh; max-height: 95svh; }
  .lobbyBody { gap: 11px; padding: 12px 20px; }
  .lobbySide { gap: 10px; }
  .lobbyRoster, .lobbyCard { padding: 9px 12px; }
  #botHint { margin-top: 6px; padding: 6px 10px; font-size: 12px; }
}
@media (max-height: 800px) {
  .lobbyHeader { padding: 10px 20px 9px; }
  .lobbyFooter { padding: 9px 20px 10px; }
  .cardHead { margin-bottom: 7px; padding-bottom: 5px; }
  #lobbyList { max-height: 26vh; }
  #hostOpts .gmOpt + .gmOpt, #hostOpts .gmOpt + .opt { margin-top: 8px; padding-top: 7px; }
}
@media (max-height: 720px) {
  .lobbyPanel { max-height: 96vh; max-height: 96svh; }
  .lobbyGlyph { font-size: 24px; }
  .lobbyTitleWrap h1.small { font-size: 18px; }
  .lobbyBody { gap: 9px; padding: 10px 18px; }
  .lobbySide { gap: 8px; }
  .lobbyRoster, .lobbyCard { padding: 8px 11px; }
  .gmBtn { padding: 6px 9px; font-size: 11px; line-height: 1.25; }
  #gmBtns { gap: 5px; }
  #lobbyList { max-height: 24vh; }
  #lobby #readyBtn { min-height: 46px; min-width: 158px; padding: 8px 24px 12px; }
  #lobby #startBtn.solo { min-height: 46px; padding: 8px 22px; }
  #lobby #startBtn { min-height: 40px; }
  .lobbyFooter #spectateOpt { font-size: 12px; }
}
@media (max-height: 700px) {
  .lobbyPanel { max-height: 97vh; max-height: 97svh; }
  .lobbyHeader { padding: 7px 16px 6px; }
  .lobbyGlyph { font-size: 21px; }
  .lobbyTitleWrap h1.small { font-size: 16px; }
  .lobbyCount { padding: 4px 10px; }
  .lobbyCount #lobbyCountNum { font-size: 17px; }
  .lobbyBody { gap: 8px; padding: 8px 16px; }
  .lobbySide { gap: 7px; }
  .lobbyRoster, .lobbyCard { padding: 7px 10px; }
  .cardHead { margin-bottom: 5px; padding-bottom: 4px; font-size: 11px; }
  .lobbyFooter { padding: 6px 16px 7px; }
  #lobbyList { max-height: 22vh; }
}

/* ---- Shop ---- */
/* Wider + compact so all three stalls' content fits without a scrollbar. */
/* Wide marketplace: expands horizontally and uses multi-column lists so the
   whole shop fits on screen without scrolling. */
.panel.wide { width: min(1040px, 96vw); max-height: 96vh; max-height: 96svh; overflow-y: auto; padding: 14px 20px 12px; }
/* Spells stall: the tree + diorama + detail columns want ROOM — while the spells
   panel is the open one, the shop window expands to nearly the whole screen.
   (Also outranks the townpanel's 880px width below. The dead-shop takes this
   too — its 440px corner card can't hold these stalls — unless it's collapsed
   to the "Hide shop" pill, which must stay button-sized.) */
#shop:not(.deadmode.collapsed) > .panel:has(#panel-spells:not(.hidden)) { width: min(1840px, 97vw); }
/* Skill-tree stall: same near-fullscreen idea, but the web is SQUARE — the tree
   grows to the viewport's HEIGHT (see #skillTree) and the panel width follows
   it, instead of stranding the square in a huge empty panel. Outranks the
   townpanel width below; same dead-shop rule as the spells stall. */
#shop:not(.deadmode.collapsed) > .panel:has(#panel-tree:not(.hidden)) {
  /* square web + the ~280px "Your build / Builds" side panel */
  width: min(1650px, max(880px, calc(92vh + 240px)), 97vw);
}
/* Armory & weaponsmith stalls: wider than the 880px townpanel default so the
   racks flatten (outfits 6-up → 2 rows, staves 5-up → 3 rows) and the panel
   stays scroll-free; the dead-shop corner panel is exempt. */
#shop:not(.deadmode) > .panel:has(#panel-armory:not(.hidden)),
#shop:not(.deadmode) > .panel:has(#panel-weapons:not(.hidden)) { width: min(1160px, 94vw); }
#shop h1.small { margin-bottom: 2px; }
/* countdown + gold share one compact row to reclaim vertical space */
.shopTop { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; margin: 2px 0 6px; }
#shopCountdown {
  text-align: left; font-size: 20px; font-weight: 800; letter-spacing: .5px;
  color: #ffcf3f; text-shadow: 0 2px 10px rgba(255,140,40,.5);
}
#shopCountdown.urgent {
  color: #ff5a5a; animation: countPulse .6s ease-in-out infinite;
}
@keyframes countPulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.14); } }
#goldRow { text-align: right; font-size: 15px; color: #ffcf3f; white-space: nowrap; }
#goldRow b { font-size: 19px; }
#spRow { text-align: right; font-size: 15px; color: #9fd0ff; white-space: nowrap; }
#spRow b { font-size: 19px; }
/* tiny XP readout: "Lv N [▓▓▓░░] xp/need" */
#xpRow { display: flex; align-items: center; gap: 6px; font-size: 12px; color: #cdbf9f; white-space: nowrap; }
#xpRow b { color: #ffd94a; font-size: 13px; }
.xpBar {
  display: inline-block; width: 92px; height: 7px; border-radius: 4px; overflow: hidden;
  background: rgba(10,7,16,.7); border: 1px solid rgba(255,217,74,.35); vertical-align: middle;
}
.xpBar > span { display: block; height: 100%; width: 0; background: linear-gradient(90deg, #b98a2c, #ffd94a); }
.xpBar.mini { width: 54px; height: 5px; }
/* golden "LvN" chip prefixed to mage names (scoreboard / TAB / MVP card) */
.lvTag { color: #ffd94a; font-weight: 800; font-size: .82em; opacity: .95; }
.shopHead { font-size: 12px; letter-spacing: .4px; text-transform: uppercase; color: #9d8b6c; margin: 4px 0 4px; }

/* element picker */
#elementList { display: flex; gap: 8px; margin-bottom: 4px; flex-wrap: wrap; }
.elCard {
  flex: 1 1 92px; min-width: 88px; cursor: pointer;
  padding: 6px 8px; border-radius: 10px; text-align: center;
  background: #191007; border: 2px solid #3a2817; transition: border-color .1s, background .1s;
}
.elCard:hover { background: #221808; }
.elCard.selected { border-color: var(--el, var(--accent)); background: #2e1f10; box-shadow: 0 0 12px -2px var(--el, var(--accent)); }
.elCard .elEmoji { font-size: 24px; }
.elCard .elName { font-size: 12px; font-weight: 700; margin-top: 2px; }
.elCard .elTag { font-size: 10.5px; margin-top: 2px; color: #9d8b6c; }
.elCard .elTag.owned { color: #5ee06a; }
.elCard .elDesc { font-size: 10px; color: #8d7a58; margin-top: 4px; line-height: 1.25; }

#elementBadge {
  position: absolute; bottom: 66px; left: 14px;
  font-size: 12px; font-weight: 700; color: #fff;
  background: rgba(10,7,16,.6); border: 1.5px solid #4a3320; border-radius: 8px;
  padding: 3px 9px; backdrop-filter: blur(3px);
}
#shopList { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 4px; }
#passiveList { display: block; }
.passCards { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
/* element filter tabs for the passive shop */
.passTabs { display: flex; gap: 6px; justify-content: center; margin-bottom: 8px; }
.passTab {
  position: relative; padding: 6px 10px; border-radius: 9px; font-size: 17px; line-height: 1;
  background: rgba(255,255,255,.05); border: 1.5px solid rgba(255,255,255,.12); cursor: pointer;
  transition: transform .08s, border-color .12s, background .12s;
}
.passTab:hover { transform: translateY(-1px); background: rgba(255,255,255,.09); }
.passTab.on { border-color: var(--el); background: color-mix(in srgb, var(--el) 22%, transparent); box-shadow: 0 0 10px -2px var(--el); }
.passOwn {
  position: absolute; top: -5px; right: -5px; min-width: 15px; height: 15px; padding: 0 3px;
  border-radius: 8px; background: var(--el); color: #10121c; font-size: 10px; font-weight: 800;
  display: flex; align-items: center; justify-content: center;
}
.shopCard {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: 7px 10px; background: #191007; border: 1px solid #3a2817; border-radius: 10px;
}
/* per-slot element chooser inside a spell card (compact row under the card) */
.slotEls { flex-basis: 100%; display: flex; gap: 3px; margin-top: 1px; }
.slotEl {
  width: auto; margin: 0; padding: 2px 5px; font-size: 13px; line-height: 1;
  border-radius: 6px; background: #201808; border: 1.5px solid #3a2817; cursor: pointer;
  filter: grayscale(.5) opacity(.7); transition: filter .1s, border-color .1s;
}
.slotEl:hover { filter: none; }
.slotEl.on { border-color: var(--el, var(--accent)); background: #2e1f10; filter: none; box-shadow: 0 0 8px -2px var(--el, var(--accent)); }
.slotEl.none { color: #9d8b6c; font-size: 12px; }
.shopCard .sIcon { font-size: 24px; flex: none; }
.shopCard .sInfo { flex: 1; min-width: 0; }
.shopCard .sName { font-size: 14px; font-weight: 600; }
.shopCard .sDesc { font-size: 11px; color: #9d8b6c; }
.shopCard button {
  width: auto; margin: 0; padding: 7px 12px; font-size: 13px; border-radius: 8px;
  /* purchase = violet (never yellow, so it doesn't read as gold) */
  background: linear-gradient(180deg, #a06bff, #6e3fd6); color: #fff;
}
.shopCard button:disabled { background: #3a2817; color: #7d6a4c; cursor: not-allowed; }
.shopCard .owned { font-size: 13px; font-weight: 700; color: #5ee06a; white-space: nowrap; }
.shopCard.starter .owned { color: #c7b596; }
.shopCard.locked { opacity: .5; filter: grayscale(.5); }
.owned.locked { color: #9d8b6c; font-weight: 700; white-space: nowrap; }
.potSize.locked { opacity: .5; filter: grayscale(.5); }
.shopCard.elemental { border-color: var(--el); }
.shopCard .sIcon { position: relative; }
.shopCard .elchip {
  position: absolute; bottom: -4px; right: -6px;
  font-size: 13px; line-height: 1; text-shadow: 0 1px 2px #000;
}

/* ---- Skill tree (radial, Path-of-Exile-style) ---- */
/* Square SVG: near-fullscreen like the spells panel, but HEIGHT is the binding
   dimension for a square — so the cap is viewport-height-driven (minus the
   panel's chrome) and the tree shrinks to fit short screens without scrolling.
   The classic overlay keeps stalls + ready button (~270px of chrome); town mode
   strips them (they live on #townHud), so it gets the roomier cap below. */
/* skill window = header row (title + search) over a flex row: square web | side panel */
.shopHead.treeHeadRow { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
#treeSearch {
  width: min(300px, 40vw); margin: 0; padding: 6px 11px; font-size: 13px; text-transform: none; letter-spacing: 0;
  background: rgba(10,7,16,.75); border: 1px solid #4a3320; border-radius: 9px; color: var(--text);
}
#treeSearch:focus { outline: none; border-color: #b98cff; box-shadow: 0 0 8px -2px #b98cff; }
#treeSearch::placeholder { color: #8d7a58; }
#treeWrap { display: flex; gap: 10px; align-items: flex-start; justify-content: center; }
#skillTree { position: relative; flex: 0 1 auto; min-width: 0; width: 100%; max-width: min(1280px, calc(96vh - 285px)); margin: 2px 0 0; }
.overlay.townpanel #skillTree { max-width: min(1280px, calc(92vh - 115px)); }
/* side panel: sized to the tree square, its LISTS scroll internally so the
   overlay itself never grows a scrollbar */
#treeSide {
  flex: 1 1 240px; min-width: 218px; max-width: 320px;
  display: flex; flex-direction: column; margin-top: 2px;
  /* hard height cap mirroring the tree square (see #skillTree) so the long
     buff/build lists scroll INSIDE the panel — the overlay itself never scrolls */
  max-height: min(1280px, calc(96vh - 285px));
  background: rgba(10,7,16,.55); border: 1px solid #4a3320; border-radius: 14px; padding: 8px;
}
.overlay.townpanel #treeSide { max-height: min(1280px, calc(92vh - 115px)); }
/* Expanded dead-shop: same square, minus the extra chrome the corner panel
   carries (the "Hide shop" pill + the out-this-round note) so it stays
   scroll-free too. */
.overlay.deadmode #skillTree { max-width: min(1280px, calc(96vh - 335px)); }
.overlay.deadmode #treeSide { max-height: min(1280px, calc(96vh - 335px)); }
@media (max-width: 900px) { #treeWrap { flex-wrap: wrap; } #treeSide { max-width: none; max-height: 40vh; } }
.treeSideTabs { display: flex; gap: 6px; margin-bottom: 6px; }
.treeSideTabs .tsTab {
  flex: 1; width: auto; margin: 0; padding: 6px 4px; font-size: 12px; font-weight: 700; border-radius: 8px;
  background: rgba(255,255,255,.05); border: 1.5px solid rgba(255,255,255,.12); color: var(--text); cursor: pointer;
}
.treeSideTabs .tsTab.on { border-color: #b98cff; background: rgba(160,107,255,.18); box-shadow: 0 0 8px -3px #b98cff; }
.treeSideBody { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; text-align: left; font-size: 12px; line-height: 1.4; padding-right: 2px; overflow-wrap: break-word; }
.treeSideBody h4 { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; color: #9d8b6c; margin: 8px 0 3px; }
.treeSideBody h4:first-child { margin-top: 2px; }
.buffRow { display: flex; gap: 6px; align-items: baseline; padding: 1.5px 0; color: #e8dcc0; }
.buffRow .bIco { flex: none; width: 18px; text-align: center; }
.buffRow.gear b { color: #ffd94a; }
.buffRow small { color: #9d8b6c; }
.buffNone { color: #8d7a58; font-style: italic; padding: 4px 0; }
/* suggested-build cards */
.buildCard {
  border: 1.5px solid #3a2817; border-radius: 10px; background: #191007;
  padding: 7px 9px; margin-bottom: 7px;
}
.buildCard.on { border-color: #ffd94a; box-shadow: 0 0 10px -4px #ffd94a; }
.buildCard .bcName { font-size: 13px; font-weight: 800; }
.buildCard .bcDesc { color: #9d8b6c; margin: 2px 0 5px; }
.buildCard .bcRow { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.buildCard button {
  width: auto; margin: 0; padding: 4px 9px; font-size: 11.5px; border-radius: 7px;
  background: linear-gradient(180deg, #a06bff, #6e3fd6); color: #fff;
}
.buildCard button.ghostBtn { background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.16); }
.buildCard button:disabled { background: #3a2817; color: #7d6a4c; cursor: not-allowed; }
.buildCard .bcCost { font-size: 11px; color: #c7b596; }
.buildCard.spellRef .bcSlots { color: #c7b596; font-size: 11.5px; }
/* Community recommended-build tag — the "proven in battle" cards fed by the
   server's build aggregate (main.js communityBuilds). Used on both the spell
   stall's Spells tab and the first-town prebuilt picker. */
.bcTag {
  font-size: 9.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
  color: #ffd94a; background: rgba(255, 217, 74, .12); border: 1px solid rgba(255, 217, 74, .4);
  border-radius: 6px; padding: 1px 5px; vertical-align: middle;
}
#treeSvg {
  width: 100%; height: auto; aspect-ratio: 1 / 1; display: block; cursor: grab;
  background: radial-gradient(circle at 50% 50%, rgba(120,64,28,.34), rgba(10,7,16,.12) 70%);
  border: 1px solid #4a3320; border-radius: 14px; touch-action: none; /* drag = pan, wheel = zoom */
}
#treeSvg:active { cursor: grabbing; }
/* witch's-knot decor: six element petal circles + double ward ring, drawn
   beneath the edges (renderSkillTree builds them from the ring-center nodes) */
.tknot { fill: none; stroke-width: 6; opacity: .20; pointer-events: none; }
.tknot.glow { stroke-width: 20; opacity: .05; }
.tknot.ward { stroke: #b98cff; stroke-width: 5; opacity: .22; }
.tknot.ward.thin { stroke-width: 2; opacity: .30; }
.treeSearching .tknot { opacity: .05; }
.tedge { stroke: #2a1c3c; stroke-width: 2.2; }
.tedge.on { stroke: #b98cff; stroke-width: 3.2; }
.thub { fill: #d9c7ff; stroke: #fff; stroke-width: 2; }
/* central respec button (replaces the plain hub dot) */
.thubBtn { cursor: pointer; }
.thubBtn text { pointer-events: none; user-select: none; fill: #2a1c3c; font-weight: 800; }
.thubBtn:hover .thub { fill: #fff; filter: drop-shadow(0 0 6px #b98cff); }
.thubBtn.off { cursor: default; opacity: .55; }
.thubBtn.off:hover .thub { fill: #d9c7ff; filter: none; }
.tnode circle { fill: #17101f; stroke: var(--el, #fff); stroke-width: 2.5; transition: filter .08s ease; }
.tnode.notable circle { stroke-width: 3.5; }
.tnode.keystone circle { stroke-width: 4.5; filter: drop-shadow(0 0 5px var(--el, #ff5a7a)); }
.tnode.bridge circle { stroke-dasharray: 4 2.5; }
/* elemental-ring nodes (cat: "ring") — a neutral violet until the rings get
   their own per-element theming; also covers a missing CONFIG.CAT_COLOR entry */
.tnode.ring { --el: #b58cff; }
.tnode text { pointer-events: none; user-select: none; }
.tnode.off circle { opacity: .4; }          /* not yet reachable */
.tnode.off text { opacity: .55; }
.tnode.open { cursor: pointer; }             /* allocatable now */
.tnode.open circle { fill: #241636; filter: drop-shadow(0 0 6px var(--el, #b98cff)); }
.tnode.open:hover circle { filter: drop-shadow(0 0 11px var(--el, #b98cff)); }
.tnode.on circle { fill: var(--el, #b98cff); stroke: #fff; filter: drop-shadow(0 0 7px var(--el, #b98cff)); } /* allocated */
/* Big EARNED nodes (centers/bridges/keystones): never buyable — dim dashed shell
   until the skills around them are all owned, then they flip to a normal .on */
.tnode.auto { cursor: default; }
.tnode.auto circle:not(.tprog) { opacity: .5; stroke-dasharray: 3 3; }
.tnode.auto text { opacity: .6; }
.tnode.auto:hover circle:not(.tprog) { opacity: .8; filter: drop-shadow(0 0 6px var(--el, #b98cff)); }
/* completion arc around a locked earned node (dasharray % is inline, via pathLength) */
.tnode circle.tprog { fill: none; stroke: var(--el, #b98cff); stroke-width: 3; opacity: .9; stroke-linecap: round; }
/* shortest-path preview: click a FAR node once → its cheapest route lights up
   gold; clicking it again buys the whole path (treePlan, src/main.js) */
.tnode.plan circle { stroke: #ffd94a; opacity: 1; fill: #2e2410; filter: drop-shadow(0 0 8px #ffd94a); animation: planPulse 1.1s ease-in-out infinite; }
.tnode.plan text { opacity: 1; }
.tnode.plan { cursor: pointer; }
.tedge.plan { stroke: #ffd94a; stroke-width: 3.4; opacity: .95; }
@keyframes planPulse { 0%,100% { opacity: 1; } 50% { opacity: .55; } }
/* search: hits glow, the rest of the web fades back */
.treeSearching .tnode.dim circle, .treeSearching .tnode.dim text { opacity: .13; }
.treeSearching .tedge { opacity: .25; }
.tnode.hit circle { filter: drop-shadow(0 0 9px #ffd94a); stroke-width: 4; }
#treePoints { color: #d6b8ff; font-size: 1.15em; }
/* Tip is an absolute overlay pinned to the bottom of the tree, so showing it on
   hover never grows the panel (which would pop a scrollbar). */
#treeTip {
  position: absolute; left: 6px; right: 6px; bottom: 6px; z-index: 3; text-align: left;
  background: rgba(10,7,16,.94); border: 1px solid #4a3320; border-radius: 10px;
  padding: 7px 12px; font-size: 13px; line-height: 1.45; pointer-events: none;
  overflow-wrap: break-word; /* long node descriptions wrap instead of spilling out */
}
/* Hovering a LOWER node flips the tip to the top so it never covers that node. */
#treeTip.top { bottom: auto; top: 6px; }
#treeTip .tstat { display: block; color: #c7b596; font-size: 12px; margin-top: 2px; }

/* ---- Spell tree (radial, per spell slot) ---- */
.spellTabs { display: flex; gap: 6px; margin: 2px 0 8px; }
.spellTab {
  flex: 1; width: auto; margin: 0; padding: 7px 4px 6px; border-radius: 10px; cursor: pointer;
  position: relative; background: #191007; border: 2px solid #3a2817; color: var(--text);
  display: flex; flex-direction: column; align-items: center; gap: 1px; line-height: 1;
}
.spellTab .stIcon { font-size: 21px; }
.spellTab .stIcon img.pixIco { width: 24px; height: 24px; display: inline-block; vertical-align: middle; image-rendering: pixelated; }
.spellTab .stKey { font-size: 10px; font-weight: 700; color: #c7b596; letter-spacing: .5px; }
.spellTab:hover { background: #221808; }
.spellTab.sel { border-color: var(--el, var(--accent)); background: #2e1f10; box-shadow: 0 0 11px -2px var(--el, var(--accent)); }
.spellTab.locked { opacity: .55; }
.spellTab .stBadge {
  position: absolute; top: -5px; right: -4px; font-size: 12px; line-height: 1;
  text-shadow: 0 1px 2px #000; filter: drop-shadow(0 0 3px rgba(0,0,0,.6));
}
/* spendable slot: the tab shimmers gold so unspent 💠 never sits behind a tab you
   forgot to open. Gold border on the unselected tabs (the selected one keeps its
   element border as the "you're here" marker); the pulsing halo lands on all. */
.spellTab.canspend { animation: spellTabSpend 1.15s ease-in-out infinite; }
.spellTab.canspend:not(.sel) { border-color: #ffd35a; }
@keyframes spellTabSpend {
  0%, 100% { filter: drop-shadow(0 0 2px rgba(255,211,90,.55)); }
  50%      { filter: drop-shadow(0 0 10px rgba(255,211,90,.95)); }
}
/* preview (left) + tree (right) share one centered row; the preview column
   stretches to whatever height the tree's accordion takes this tab */
#spellTreeRow { display: flex; gap: 10px; align-items: stretch; justify-content: center; }
#spellPreview {
  position: relative; flex: 0 0 clamp(148px, 13vw, 260px); min-height: 190px;
  border: 1px solid #4a3320; border-radius: 14px; overflow: hidden;
  background:
    radial-gradient(120% 90% at 50% 30%, rgba(120, 64, 28, .18), transparent 70%),
    linear-gradient(180deg, #17100a, #0b0710);
}
#spellPreview canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* tight layouts (phones): the diorama SHRINKS but stays on the tree's left —
   touch players have no hover, so it's their main "what does this spell do"
   answer. Only truly tiny widths drop it so the tree keeps usable columns. */
@media (max-width: 760px) { #spellPreview { flex-basis: clamp(96px, 17vw, 148px); min-height: 128px; } }
@media (max-width: 480px) { #spellPreview { display: none; } }
#spellTree { position: relative; width: 100%; max-width: 1560px; min-width: 0; }

/* ---- Right-hand detail window (description + exact stats of the hovered pick) ----
   Matches the diorama's card: bronze frame + dark molten fill, laid out as a
   stack of sections. Fed by spellShowDetail / spellHoverDetail (main.js). */
/* The panel is a positioned frame; its content lives in an ABSOLUTE inner
   scroller (.sdBody) so hover-swapping the content NEVER changes the panel's box
   height. (If it did, the shop panel would grow and the centered overlay would
   re-center mid-click, shifting the tree out from under the cursor — the skill
   tree dodges this by using a position:absolute tip.) */
#spellDetail {
  position: relative; flex: 0 0 clamp(232px, 18vw, 350px); min-width: 0; align-self: stretch; overflow: hidden;
  border: 1px solid #4a3320; border-radius: 14px;
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(120,64,28,.16), transparent 70%),
    linear-gradient(180deg, #17100a, #0b0710);
}
#spellDetail:empty { display: none; }
.sdBody {
  position: absolute; inset: 0; overflow-y: auto; overflow-x: hidden; padding: 11px 13px;
  text-align: left; font-size: 13px; line-height: 1.4; color: var(--text);
  display: flex; flex-direction: column; gap: 8px;
}
.sdHead { display: flex; align-items: center; gap: 9px; }
.sdIcon { font-size: 26px; line-height: 1; flex: none; }
.sdIcon img.pixIco { width: 30px; height: 30px; image-rendering: pixelated; display: block; }
.sdIcon .em { filter: drop-shadow(0 1px 2px #000); }
.sdTitle { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.sdTitle b { font-size: 15px; font-weight: 800; color: #ffe6b0; line-height: 1.1; }
.sdType { font-size: 11px; letter-spacing: .3px; color: #b79a6f; }
.sdDesc { color: #d6c4a2; font-size: 12.5px; }
.sdStats { display: flex; flex-direction: column; gap: 3px; border-top: 1px solid rgba(74,51,32,.5); padding-top: 6px; }
.sdStat { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.sdStat > span { color: #9d8b6c; font-size: 11.5px; text-transform: uppercase; letter-spacing: .4px; }
.sdStat > b { color: #f0dcae; font-weight: 700; white-space: nowrap; }
.sdStat i { color: #9d8b6c; font-style: normal; font-size: 10.5px; margin-left: 4px; font-weight: 500; }
.sdStat b.up { color: #7de08a; }
.sdNext {
  border: 1px solid #3a2817; border-radius: 9px; padding: 6px 8px;
  background: rgba(10,7,16,.5); display: flex; flex-direction: column; gap: 3px;
}
.sdNextHd { font-size: 11px; font-weight: 700; color: #9fd0ff; letter-spacing: .3px; }
.sdNextNote { font-size: 11.5px; color: #c7b596; }
.sdNext.maxed { color: #ffd35a; font-weight: 800; text-align: center; border-color: #6b4e18; }
.sdStatus { font-size: 12px; color: #c7b596; border-top: 1px solid rgba(74,51,32,.5); padding-top: 6px; }
/* The side panel supersedes the little bottom hover tooltip everywhere — the
   dead-shop used to fall back to it, but the spells stall now opens as a full
   window there too, so the panel is always present. */
#spellTip { display: none; }
/* narrower shops: the detail drops below the tree full-width (the diorama
   shrinks at 760px, see above) so the tree keeps usable width. The tree's basis
   must drop to 0 here: with wrap on, its default `width:100%` basis overflows
   the line and the whole tree wrapped UNDER the preview — phone landscape
   (~890px) showed the diorama alone on its own centered row. flex:1 keeps
   preview + tree side by side; only the full-width detail wraps. */
@media (max-width: 900px) {
  #spellTreeRow { flex-wrap: wrap; }
  #spellTree { flex: 1 1 0; width: auto; }
  #spellDetail { flex: 1 1 100%; order: 3; align-self: auto; height: 220px; }
}
#spellTreeSvg {
  width: 100%; height: auto; aspect-ratio: 640 / 268; display: block; cursor: grab;
  background: linear-gradient(180deg, rgba(120,64,28,.26), rgba(10,7,16,.10));
  border: 1px solid #4a3320; border-radius: 14px; touch-action: none; /* drag = pan, wheel = zoom */
  /* Near-fullscreen shop: the tree grows with the panel, but its HEIGHT is
     clamped to what the viewport can hold (head + tabs + guide + actions ≈
     350px of chrome) so the panel never pops a scrollbar. When the clamp bites
     a tall accordion tree, preserveAspectRatio="meet" letterboxes it. */
  max-height: max(240px, calc(92vh - 350px));
}
#spellTreeSvg:active { cursor: grabbing; }
.sedge { stroke: #2a1c3c; stroke-width: 3; fill: none; }
.sedge.on { stroke: var(--el, #b98cff); stroke-width: 4.5; opacity: .9; }
.sedge.vert { stroke-width: 2.5; }
/* element branch rails: dim base + a lit stretch over the invested levels */
.srail { stroke: #241733; stroke-width: 3; stroke-linecap: round; }
.srail.on { stroke: var(--el, #b98cff); stroke-width: 4; opacity: .85; stroke-linecap: round; }
.shub circle { fill: #17101f; stroke: var(--el, #fff); stroke-width: 3; }
.shub text { pointer-events: none; user-select: none; }
.shub .shubKey { fill: #ffe6b0; font-weight: 800; letter-spacing: .5px; paint-order: stroke; stroke: #0a0603; stroke-width: 3.5px; stroke-linejoin: round; }
.shub.locked circle { opacity: .5; }
.snode circle { fill: #17101f; stroke: var(--el, #fff); stroke-width: 2.5; transition: filter .08s ease; }
.snode text { pointer-events: none; user-select: none; }
.snode.off circle { opacity: .38; }              /* spell not usable yet */
.snode.off text { opacity: .5; }
.snode.open { cursor: pointer; }
.snode.open circle { fill: #241636; filter: drop-shadow(0 0 6px var(--el, #b98cff)); }
.snode.open:hover circle { filter: drop-shadow(0 0 11px var(--el, #b98cff)); }
.snode.on circle { fill: var(--el, #b98cff); stroke: #fff; filter: drop-shadow(0 0 8px var(--el, #b98cff)); }
/* invested-level bubble on an equipped element node (its share of the slot's 10-level pool) */
.snode .snodeLvlBg { fill: #0d0815; stroke: var(--el, #fff); stroke-width: 1.5; opacity: 1 !important; stroke-dasharray: none !important; }
.snode .snodeLvl { fill: #fff; font-weight: 700; opacity: 1 !important; }
/* level icons along a branch (one per level of the slot's shared 10-level pool) */
.snode .hitArea { fill: transparent !important; stroke: none !important; filter: none !important; }
.snode.lvl.open circle:not(.hitArea) { animation: lvlPulse 1.3s ease-in-out infinite; }
@keyframes lvlPulse {
  0%, 100% { filter: drop-shadow(0 0 3px var(--el, #b98cff)); }
  50%      { filter: drop-shadow(0 0 11px var(--el, #b98cff)); }
}
.snode.lvl.reach { cursor: pointer; }               /* buyable in one multi-level jump */
.snode.lvl.reach circle:not(.hitArea) { fill: #1d1229; opacity: .55; }
.snode.lvl.reach text { opacity: .6; }
.snode.lvl.reach:hover circle:not(.hitArea) { opacity: .85; filter: drop-shadow(0 0 7px var(--el, #b98cff)); }
.snode.lvl.mile circle:not(.hitArea) { stroke-width: 3.5; } /* milestone levels (5 & 10) */
/* ---- Path fork (the primary element's branch splits into a branch per path) ---- */
/* faint tinted band framing the opened branch, so the expansion reads at a glance */
.sforkband { fill: color-mix(in srgb, var(--el, #b98cff) 9%, transparent);
  stroke: color-mix(in srgb, var(--el, #b98cff) 34%, transparent); stroke-width: 1.5; }
/* pulsing selection ring around the active path node */
.spathring { fill: none; stroke: var(--el, #b98cff); stroke-width: 2; opacity: .8;
  animation: sringPulse 1.6s ease-in-out infinite; }
@keyframes sringPulse { 0%,100% { opacity: .4; r: 20px; } 50% { opacity: .85; r: 22px; } }
/* the curved limb from the element anchor out to each path node */
.spathbranch { stroke: #2a1c3c; stroke-width: 3.5; fill: none; stroke-linecap: round; }
.spathbranch.on { stroke: var(--el, #b98cff); stroke-width: 5; opacity: .95;
  filter: drop-shadow(0 0 6px var(--el, #b98cff)); }
/* the chosen limb + its lit rail pulse with flowing energy toward the tips */
.spathbranch.flow, .srail.flow { stroke-dasharray: 2 12; animation: sflow 0.9s linear infinite; }
@keyframes sflow { to { stroke-dashoffset: -14; } }
/* the path node (a spell-variant bud on the branch) */
.spathnode { cursor: pointer; }
.spathnode circle { fill: #17101f; stroke: var(--el, #b98cff); stroke-width: 2.5;
  opacity: .5; transition: transform .18s cubic-bezier(.34,1.56,.64,1), filter .18s ease, opacity .18s ease;
  transform-box: fill-box; transform-origin: center; }
.spathnode text { transition: transform .18s cubic-bezier(.34,1.56,.64,1);
  transform-box: fill-box; transform-origin: center; pointer-events: none; user-select: none; }
.spathnode:hover circle { opacity: .95; filter: drop-shadow(0 0 9px var(--el, #b98cff)); transform: scale(1.12); }
.spathnode:hover text { transform: scale(1.12); }
/* the ACTIVE path — it snaps onto the main limb, glowing and enlarged */
.spathnode.on circle { fill: var(--el, #b98cff); stroke: #fff; opacity: 1;
  filter: drop-shadow(0 0 11px var(--el, #b98cff)); transform: scale(1.18); }
.spathnode.on text { transform: scale(1.05); }
/* path name label beside each bud */
.spathlabel { fill: #a8946b; font-weight: 700; pointer-events: none; user-select: none;
  letter-spacing: .2px; }
.spathlabel.on { fill: #fff; filter: drop-shadow(0 0 4px var(--el, #b98cff)); }
/* Absolute overlay pinned to the tree's bottom — hovering never grows the panel.
   Hovering a LOWER branch flips it to the top (`.top`) so it never hides the
   branch being read. */
#spellTip {
  position: absolute; left: 6px; right: 6px; bottom: 6px; z-index: 3; text-align: left;
  background: rgba(10,7,16,.94); border: 1px solid #4a3320; border-radius: 10px;
  padding: 7px 12px; font-size: 13px; line-height: 1.4; pointer-events: none;
  overflow-wrap: break-word; /* long spell blurbs wrap instead of spilling out */
}
#spellTip.top { bottom: auto; top: 6px; }
#spellTip .stat { display: block; color: #c7b596; font-size: 12px; margin-top: 2px; }
.spellActions { margin-top: 6px; }
.spellActRow {
  display: flex; align-items: center; gap: 10px;
  background: #191007; border: 1px solid #3a2817; border-radius: 10px; padding: 7px 12px;
}
.spellActRow .sIcon { font-size: 24px; flex: none; }
.spellActRow .sIcon img.pixIco { width: 30px; height: 30px; display: inline-block; vertical-align: middle; image-rendering: pixelated; }
.spellActRow .sInfo { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
/* Charge pips in the shop preview: a lit dot per banked cast (arcane Spiral/Seeker
   Orb). Grows as you invest more arcane depth — a live capacity preview. */
.spellActRow .sCharges { display: flex; gap: 3px; flex: none; align-items: center; padding: 0 2px; }
.spellActRow .sCharges i {
  width: 7px; height: 7px; border-radius: 50%;
  background: #c9a6ff; box-shadow: 0 0 5px rgba(180,120,255,.95);
}
.spellActRow .sName { font-weight: 700; white-space: nowrap; }
.spellActRow .sDesc { font-size: 12px; color: #c7b596; overflow-wrap: break-word; } /* wrap long i18n blurbs rather than clip them with an ellipsis */
.spellActRow .buyBtn { width: auto; margin: 0; padding: 9px 15px; font-size: 13px; border-radius: 9px; white-space: nowrap; flex: none; }
.spellActRow .owned { font-size: 13px; color: #5ee06a; font-weight: 700; white-space: nowrap; }
.spellActRow .owned.locked { color: #c7b596; }
.spellActRow .owned.maxed { color: #ffcf3f; }
.respecRow { margin-top: 6px; justify-content: space-between; }
/* (Path selection moved onto the tree — the primary branch forks into path
   branches; see .spathnode / .spathbranch above.) */
/* Shop action buttons sit tight to the content — no big top margin. */
#shop #shopReadyBtn { margin-top: 8px; padding: 11px; }
.panel.wide { padding-bottom: 10px; }
.respecBtn {
  width: auto; margin: 0; padding: 8px 13px; font-size: 13px; font-weight: 700; white-space: nowrap;
  background: linear-gradient(180deg, #ff9a6b, #d8632f); color: #2b1005;
}
.respecBtn:disabled { background: #3a2817; color: #7d6a4c; }
.respecNote { font-size: 11px; color: #a8946b; text-align: right; }

/* ---- Marketplace stalls / panels ---- */
/* Horizontal pills (icon beside label) keep the stall row short. */
#stalls { display: flex; gap: 8px; margin: 4px 0 8px; }
.stall {
  flex: 1; width: auto; margin: 0; padding: 8px 10px; border-radius: 10px;
  background: #191007; border: 2px solid #3a2817; color: var(--text);
  display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 8px; font-size: 13px; font-weight: 600;
}
.stall .npc { font-size: 22px; line-height: 1; }
/* Skill-tree stall wears a carved rune glyph (an inline SVG) instead of an emoji. */
.stall .npc-rune { display: inline-flex; }
.stall .npc-rune svg { width: 26px; height: 26px; color: #ffd35a; filter: drop-shadow(0 0 5px rgba(255,193,60,.55)); }
.stall.active .npc-rune svg { color: #ffe39a; }
.stall:hover { background: #221808; }
.stall.active { border-color: var(--accent); background: #2e1f10; box-shadow: 0 0 12px -2px var(--accent); }
/* "you can spend something here" nudge: the closed stall blinks + wears a badge */
.stall { position: relative; }
.stall.nudge { border-color: #ffd35a; animation: stallNudge 1.1s ease-in-out infinite; }
@keyframes stallNudge {
  0%, 100% { box-shadow: 0 0 4px -2px rgba(255, 211, 90, .35); }
  50%      { box-shadow: 0 0 16px 1px rgba(255, 211, 90, .85); }
}
.notifDot {
  position: absolute; top: -7px; right: -6px; min-width: 18px; height: 18px; padding: 0 4px;
  border-radius: 9px; background: linear-gradient(180deg, #ffe14a, #ff9f2f); color: #3a2004;
  font-size: 11px; font-weight: 800; line-height: 18px; text-align: center;
  box-shadow: 0 0 8px rgba(255, 193, 60, .8); pointer-events: none;
  animation: notifPop 1.1s ease-in-out infinite;
}
@keyframes notifPop { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.18); } }
/* clickable hint line under the stalls naming what's waiting in the other tab */
#spendHint {
  display: block; width: 100%; margin: -2px 0 8px; padding: 6px 10px;
  background: rgba(255, 211, 90, .08); border: 1px dashed #b98a2e; border-radius: 9px;
  color: #ffd98a; font-size: 12.5px; font-weight: 600; text-align: center; cursor: pointer;
}
#spendHint:hover { background: rgba(255, 211, 90, .16); }
#spendHint.hidden { display: none; }
.mkPanel { min-height: 40px; }

/* ---- Grimoire (📖 Spells & Skills): a read-only main-menu reference ---- */
/* Header tag is compact; the card grid is a self-contained scroll region so the
   panel itself never scrolls (see menus-no-scrollbars). */
#codex .panel.wide { text-align: left; overflow: hidden; }
#codex .tag { margin: 2px 0 8px; font-size: 12.5px; }
#codexTabs { display: flex; gap: 8px; margin: 0 0 10px; }
.codexTab {
  flex: 1; width: auto; margin: 0; padding: 9px 10px; border-radius: 10px;
  background: #191007; border: 2px solid #3a2817; color: var(--text);
  font-size: 14px; font-weight: 700;
}
.codexTab:hover { background: #221808; }
.codexTab.active { border-color: var(--accent); background: #2e1f10; box-shadow: 0 0 12px -2px var(--accent); }
.codexBody {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(205px, 1fr));
  gap: 10px; align-items: start;
  max-height: min(560px, 66vh); overflow-y: auto;
  margin: 0 -4px; padding: 0 4px 2px;
}
.cxCard {
  background: #191007; border: 1px solid #3a2817; border-left: 3px solid var(--el, var(--accent));
  border-radius: 10px; padding: 9px 11px 4px;
}
.cxHead { display: flex; align-items: center; gap: 8px; }
.cxHead .cxEmoji { font-size: 21px; line-height: 1; }
.cxHead .cxName { font-size: 15px; font-weight: 800; color: var(--el, #fff); }
.cxSub { font-size: 10.5px; color: #9d8b6c; margin: 3px 0 7px; line-height: 1.3; }
.cxRow { display: flex; align-items: flex-start; gap: 8px; padding: 5px 0; border-top: 1px solid #281b0e; }
.cxHead + .cxRow, .cxSub + .cxRow { border-top: none; }
.cxRow .cxKey {
  flex: none; min-width: 20px; height: 20px; margin-top: 1px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  background: #281b0e; border: 1px solid #5c4526; border-bottom-width: 2px; border-radius: 5px;
  font-size: 11px; font-weight: 700; color: #d9c8a4;
}
.cxRow .cxKey.none { visibility: hidden; min-width: 0; width: 0; margin: 0; padding: 0; border: none; }
.cxRow .cxIco { flex: none; font-size: 18px; line-height: 1.2; width: 22px; text-align: center; }
.cxRow .cxIco img.pixIco { width: 20px; height: 20px; display: inline-block; vertical-align: middle; image-rendering: pixelated; }
.cxRow .cxTxt { display: flex; flex-direction: column; gap: 1px; }
.cxRow .cxTxt b { font-size: 12.5px; color: #efe4cb; }
.cxRow .cxTxt span { font-size: 11px; color: #a8946b; line-height: 1.32; }

/* ---- Dead-player in-game shop: the Marketplace overlay reused as a small,
   collapsible corner panel while you're out of the live round. It does NOT dim
   the arena (you keep spectating behind it), so `.overlay` fills/pointer-events
   are neutralized here and only the panel itself is interactive. ---- */
.overlay.deadmode {
  position: fixed; inset: auto auto 92px 12px;
  display: block; align-items: initial; justify-content: initial;
  background: none; padding: 0; z-index: 8; pointer-events: none;
  width: min(440px, 94vw); max-height: 64vh;
}
.overlay.deadmode .panel {
  pointer-events: auto; margin: 0; width: 100%; max-height: 64vh;
  padding: 12px 16px 12px; border-color: #5a3a1e;
  box-shadow: 0 12px 40px rgba(0,0,0,.7), 0 0 0 1px rgba(255,193,60,.25);
}
.overlay.deadmode .panel h1 { font-size: 1.05rem; margin: 2px 0 6px; }
/* …except for the two stalls that need a real window: the skill web is a square
   that shrinks to ~150px in the corner card, and the spells stall wants three
   columns. While one is open the dead-shop un-corners into the centered
   near-fullscreen frame the living shop uses (widths above). The backdrop stays
   see-through and click-through, and "Hide shop" still folds it back to the pill
   when you'd rather watch the fight. */
.overlay.deadmode:not(.collapsed):has(#panel-tree:not(.hidden)),
.overlay.deadmode:not(.collapsed):has(#panel-spells:not(.hidden)) {
  inset: 0; width: auto; max-height: none;
  display: flex; align-items: center; justify-content: center;
}
.overlay.deadmode:not(.collapsed):has(#panel-tree:not(.hidden)) .panel,
.overlay.deadmode:not(.collapsed):has(#panel-spells:not(.hidden)) .panel { max-height: 96vh; }
.deadNote { font-size: 12px; color: #ffcf8a; margin: 0 0 8px; line-height: 1.35; }
#shopCollapse {
  width: auto; margin: 0 0 6px; padding: 8px 14px; font-size: 13px; font-weight: 700;
  border-radius: 10px; border: 1px solid #6a4a20; background: #241a10; color: #ffdf9a; cursor: pointer;
}
#shopCollapse:hover { background: #2e2214; }
/* Collapsed: only the toggle pill shows — the panel shrinks to a button. */
.overlay.deadmode.collapsed { max-height: none; width: auto; }
.overlay.deadmode.collapsed .panel { padding: 0; background: none; border: none; box-shadow: none; max-height: none; overflow: visible; }
.overlay.deadmode.collapsed .panel > *:not(#shopCollapse) { display: none !important; }

/* ---- Bounty: a coin badge beside the leading mage's name in the scoreboard. */
#scoreboard .bounty { color: #ffd35a; font-weight: 700; margin-left: 5px; font-size: 11px; white-space: nowrap; text-shadow: 0 0 6px rgba(255,193,60,.5); }

/* per-spell level pips + upgrade button inside each shop card */
.shopCard .lvlpips { font-size: 13px; letter-spacing: 1.5px; white-space: nowrap; }
.shopCard .maxed { color: #ffcf3f; }
.buyBtn.upBtn {
  background: linear-gradient(180deg, #7fe0ff, #38a9d8); color: #05202b; white-space: nowrap;
}
.buyBtn.upBtn:disabled { background: #3a2817; color: #7d6a4c; }
/* an AFFORDABLE level-up gently glows — the "spend me" cue inside the open panel */
.spellActRow .buyBtn.upBtn:not(:disabled) { animation: upGlow 1.4s ease-in-out infinite; }
@keyframes upGlow {
  0%, 100% { box-shadow: 0 0 3px rgba(127, 224, 255, .25); }
  50%      { box-shadow: 0 0 12px rgba(127, 224, 255, .8); }
}

#potionList, #runeList { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
/* potion size grid inside each Health/Mana group */
.potGroup { display: block; }
.potHead { display: flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 700; margin-bottom: 8px; }
.potHead .sIcon { font-size: 22px; }
.potHead .sDesc { font-weight: 400; }
.potSizes { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.potSize {
  display: flex; align-items: center; gap: 6px;
  background: #201808; border: 1px solid #3a2817; border-radius: 8px; padding: 6px 8px; font-size: 12px;
}
.potSize .psName { font-weight: 600; min-width: 46px; }
.potSize .psRestore { font-weight: 700; }
.potSize .psCount { margin-left: auto; color: #c7b596; }
.potSize .buyBtn { padding: 5px 9px; font-size: 12px; border-radius: 7px;
  background: linear-gradient(180deg, #a06bff, #6e3fd6); color: #fff; width: auto; margin: 0; }
.potSize .buyBtn:disabled { background: #3a2817; color: #7d6a4c; }
.potSize .owned { font-size: 12px; color: #5ee06a; font-weight: 700; margin-left: auto; }

/* ---- in-game potion bar (right of the spell bar so they never overlap) ---- */
#potionBar {
  position: absolute; left: 364px; bottom: 16px;
  display: flex; gap: 8px; pointer-events: auto; flex-wrap: wrap; max-width: 40vw;
}
.potion {
  position: relative; min-width: 56px; height: 44px; padding: 0 10px;
  display: flex; align-items: center; gap: 6px; cursor: pointer;
  background: rgba(10,7,16,.7); border: 1.5px solid #4a3320; border-radius: 9px;
  font-size: 15px; font-weight: 700; overflow: hidden;
}
.potion .pk { position: absolute; top: 1px; left: 4px; font-size: 9px; color: #ffcf3f; }
.potion .pcount { font-size: 13px; }
.potion .pcd {
  position: absolute; inset: 0; background: rgba(0,0,0,.62);
  display: flex; align-items: center; justify-content: center; font-size: 16px; color: #fff;
}

/* locked (unowned) spell slots: hide the spell icon entirely, show only a lock */
.slot.locked { opacity: .6; background: rgba(10,7,16,.5); }
.slot.locked .icon, .slot.locked .elbadge, .slot.locked .mana, .slot.locked .key { visibility: hidden; }
.slot.locked::after {
  content: "🔒"; position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center; font-size: 24px; opacity: .8;
}

/* ---- Buff chips over mages handled on canvas; HUD buff row ---- */
#banner {
  position: absolute; top: 42%; left: 50%; transform: translate(-50%,-50%);
  text-align: center; font-size: 46px; font-weight: 800;
  text-shadow: 0 4px 24px rgba(0,0,0,.8); pointer-events: none;
}
#banner .sub { font-size: 20px; font-weight: 500; color: #ffcf3f; margin-top: 6px; }

/* Round/run end result GIF (victory / defeat / stage cleared|failed). Sits in the
   upper third so it stacks ABOVE the centered MVP card instead of overlapping it
   (the two show together at matchover/gameover). z-index 4 keeps it below the
   card (z 6) and pointer-events:none so it never intercepts input. The compact
   max-height keeps it clear of the card even on short viewports. */
#resultBanner {
  position: absolute; top: 15%; left: 50%; transform: translate(-50%,-50%);
  z-index: 4; pointer-events: none; text-align: center;
}
#resultBanner img {
  display: block; width: auto; height: auto;
  max-width: min(560px, 80vw); max-height: 24vh;
  filter: drop-shadow(0 8px 30px rgba(0,0,0,.7));
}
#resultBanner.pop img { animation: resultPop .5s cubic-bezier(.2,1.4,.5,1) both; }
@keyframes resultPop { from { transform: scale(.55); opacity: 0; } to { transform: none; opacity: 1; } }

/* Dead/spectator overlay: "you're out — watching X" + a rotating gameplay tip */
#deathTips {
  position: absolute; bottom: 15%; left: 50%; transform: translateX(-50%);
  width: min(520px, 88vw); text-align: center; z-index: 9; pointer-events: none;
  background: rgba(10,7,16,.82); border: 1px solid #4a3320; border-radius: 12px;
  padding: 10px 16px; box-shadow: 0 6px 22px rgba(0,0,0,.6);
  animation: dtFade .4s ease both;
}
/* "Killed by X" — deliberately the loudest thing on screen while you're dead.
   Sits high and centered so it reads instantly, well clear of #deathTips at the
   bottom. Rebuilt only when the killer changes, so the pulse actually runs. */
#killedBy {
  position: absolute; top: 12%; left: 50%; transform: translateX(-50%);
  z-index: 11; pointer-events: none; white-space: nowrap;
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 10px 26px; border-radius: 12px;
  background: linear-gradient(180deg, rgba(126,12,12,.86), rgba(58,4,4,.80));
  border: 2px solid #ff5b3c;
  font-size: clamp(20px, 3.4vw, 34px); font-weight: 900;
  letter-spacing: .6px; text-transform: uppercase; color: #ffe3da;
  text-shadow: 0 0 12px rgba(255,70,40,.8), 0 2px 6px rgba(0,0,0,.95);
  animation: kbIn .28s cubic-bezier(.2,1.5,.4,1) both, kbPulse 1.6s ease-in-out .28s infinite;
}
#killedBy .kbIcon { font-size: 1.15em; filter: drop-shadow(0 0 8px rgba(255,90,40,.85)); }
#killedBy .kbName { color: #fff; text-shadow: 0 0 16px rgba(255,105,70,.95), 0 2px 6px rgba(0,0,0,.95); }
@keyframes kbIn { from { opacity: 0; transform: translateX(-50%) scale(.72); } to { opacity: 1; transform: translateX(-50%) scale(1); } }
@keyframes kbPulse {
  0%, 100% { box-shadow: 0 0 14px rgba(255,70,40,.35), inset 0 0 18px rgba(255,70,40,.12); border-color: #c93a24; }
  50%      { box-shadow: 0 0 34px rgba(255,70,40,.75), inset 0 0 26px rgba(255,70,40,.28); border-color: #ff8a5c; }
}
@media (prefers-reduced-motion: reduce) { #killedBy { animation: kbIn .28s ease both; } }

#deathTips .dtHeadRow { display: flex; align-items: center; justify-content: center; gap: 12px; }
#deathTips .dtHead {
  font-size: 18px; font-weight: 800; color: #ff6a6a;
  text-shadow: 0 2px 8px rgba(0,0,0,.8); letter-spacing: .3px;
}
/* ‹ › switch the death camera between survivors — the banner is pointer-events:none, so re-enable them here */
#deathTips .dtArrow {
  pointer-events: auto; cursor: pointer; flex: none;
  width: 38px; height: 38px; padding: 0; margin: 0; border-radius: 8px;
  border: 1px solid #6a4a2a; background: rgba(40,26,14,.9); color: #ffcf3f;
  font-size: 22px; font-weight: 800; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  transition: background .12s ease, transform .12s ease, border-color .12s ease;
}
#deathTips .dtArrow:hover { background: rgba(74,51,32,.98); border-color: #ffcf3f; transform: scale(1.08); }
#deathTips .dtArrow:active { transform: scale(.92); }
#deathTips .dtTip { margin-top: 6px; font-size: 13px; line-height: 1.4; color: #efe4cb; }
#deathTips .dtLabel { color: #ffcf3f; font-weight: 700; margin-right: 4px; }
#deathTips .dtHint { margin-top: 5px; font-size: 11px; color: #b39c74; letter-spacing: .3px; }

/* Live performance HUD (F3 / ?perf) — sits below the wallet pill (top-left). */
#perfHud {
  position: fixed; top: 46px; left: 8px; z-index: 40; pointer-events: none;
  font: 11px/1.5 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  color: #b6f0c0; background: rgba(6,10,8,.82); border: 1px solid #1f6b3a;
  border-radius: 8px; padding: 7px 10px; white-space: pre; letter-spacing: .2px;
  text-shadow: 0 1px 2px #000; min-width: 168px;
}
#perfHud b { color: #eafff0; font-weight: 700; }
#perfHud .warn { color: #ffcf3f; }
#perfHud .bad { color: #ff6a6a; }
@keyframes dtFade { from { opacity: 0; transform: translateX(-50%) translateY(8px); } to { opacity: 1; transform: translateX(-50%) translateY(0); } }
@media (max-height: 460px) { #deathTips { bottom: 8%; padding: 7px 12px; } #deathTips .dtHead { font-size: 15px; } #deathTips .dtTip { font-size: 12px; } }

/* mid-width screens: 2-column spell grid (3-up needs a roomy window) */
@media (max-width: 900px) {
  #shopList { grid-template-columns: 1fr 1fr; }
  .passCards { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  h1 { font-size: 26px; }
  h1.small { font-size: 21px; }
  .tag { font-size: 13px; margin-bottom: 14px; }
  .panel { padding: 18px 16px 16px; }
  /* 16px inputs stop iOS from auto-zooming the page on focus */
  input { font-size: 16px; }
  button { padding: 14px; font-size: 16px; }
  .createRow button { padding: 0 14px; }
  #phaseInfo { font-size: 12px; padding: 6px 9px; }
  #scoreboard { font-size: 11px; min-width: 108px; padding: 6px 9px; }
  #topbar { padding: 0 10px; }
  #banner { font-size: 30px; }
  #banner .sub { font-size: 15px; }
  /* stack shop/potion cards one-per-row so they're readable on a phone */
  #shopList, #potionList, #runeList, .passCards { grid-template-columns: 1fr; }
  /* the two-column layouts stack on narrow phones */
  #optionsBody { flex-direction: column; gap: 8px; }
  .lobbyBody { grid-template-columns: 1fr; }
  #lobbyList { max-height: 30vh; }
  .elCard { flex-basis: 78px; min-width: 78px; }
  #serverList { max-height: 42vh; }
  #lobbyInvite .qr { width: 156px; height: 156px; }
  #shopCountdown { font-size: 22px; }
  .stall .npc { font-size: 22px; }
}

/* short screens (landscape phones): tighten vertical space, drop extras */
@media (max-height: 520px) {
  .panel { padding: 14px 16px; }
  h1 { font-size: 22px; margin-bottom: 2px; }
  .tag { margin-bottom: 10px; }
  .controls { display: none; }        /* keyboard help isn't worth the height */
  #lobbyInvite .qr { width: 120px; height: 120px; }
  #lobbyInvite { padding: 10px; }
}

/* ---- Kill feed (recent knockouts, top-left under the phase pill) ---- */
#killFeed {
  position: absolute; top: 58px; left: 16px;
  display: flex; flex-direction: column; gap: 5px;
  max-width: 46vw; pointer-events: none;
}
#killFeed .kfRow {
  background: rgba(10,7,16,.66); border: 1px solid #4a3320; border-left: 3px solid #ff6a2f;
  border-radius: 8px; padding: 4px 10px; font-size: 13px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  animation: kfIn .18s ease-out; transition: opacity .8s ease;
}
#killFeed .kfRow.fade { opacity: 0; }
#killFeed .kfIc { margin-right: 6px; }
@keyframes kfIn { from { transform: translateX(-16px); opacity: 0; } to { transform: none; opacity: 1; } }

/* ---- Text chat (Global / Session tabs, collapsible; bottom-left) ---- */
/* Body-level (not inside #hud) so it can layer ABOVE the ready-up lobby & shop
   overlays. z-index 15 sits over those (z-index 10) but under the pause menu
   (#escMenu, 30) and audio controls (40). */
#chatPanel {
  position: fixed; left: 12px; bottom: 12px; z-index: 15;
  width: min(340px, 42vw); pointer-events: auto;
  display: none; flex-direction: column;
  font-size: 13px; touch-action: auto;
}
/* Shown whenever we're in a room (the HUD is up) — lobby, shop and match alike —
   but hidden behind the full-screen modals that cover the arena. Chat is for
   signed-in accounts only (JS sets body.signedIn); guests can't use it. */
body.signedIn:has(#hud:not(.hidden)) #chatPanel { display: flex; }
body:has(#escMenu:not(.hidden)) #chatPanel,
body:has(#options:not(.hidden)) #chatPanel,
body:has(#codex:not(.hidden)) #chatPanel,
body:has(#confirmModal:not(.hidden)) #chatPanel { display: none; }
/* Touch devices hide chat behind EVERY full-screen overlay, not just the modals
   listed above. Chat-while-shopping is deliberate on desktop, where those panels
   are centred and leave the bottom-left corner free — but a phone gives them
   nearly the whole width, so the chat box lands on top of the shop's own
   controls and ate the Q spell slot outright. Deliberately keyed on .touchMode
   rather than a width query so big tablets are covered too. (Specificity must
   out-rank the `display: flex` rule above; the extra :has() does that.) */
body.touchMode:has(#hud:not(.hidden)):has(.overlay:not(.hidden)) #chatPanel { display: none; }
#chatBar {
  display: flex; align-items: stretch;
  background: rgba(10,7,16,.82); border: 1px solid #4a3320; border-bottom: none;
  border-radius: 10px 10px 0 0; padding: 3px 4px 0;
  backdrop-filter: blur(2px);
}
#chatPanel.collapsed #chatBar { border-radius: 10px; border-bottom: 1px solid #4a3320; padding: 3px 4px; }
/* New-message pulse (added briefly by addChatMessage). Glows the always-visible
   bar; when expanded, the body glows too. Warm ember tone to match the UI. */
#chatPanel.chatGlow #chatBar,
#chatPanel.chatGlow #chatBody { animation: chatGlowPulse 1s ease-in-out 2; }
@keyframes chatGlowPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255,150,40,0); border-color: #4a3320; }
  40%      { box-shadow: 0 0 16px 3px rgba(255,150,40,.7); border-color: #ffb347; }
}
.chatTabs { display: flex; gap: 3px; flex: 1; }
.chatTab {
  width: auto; margin: 0; padding: 4px 13px; border: none; border-radius: 7px 7px 0 0;
  background: transparent; color: #b39c74; font-weight: 700; font-size: 12.5px;
  cursor: pointer; position: relative; line-height: 1.3;
}
.chatTab:hover { color: #ffcf3f; }
.chatTab.active { background: rgba(74,51,32,.55); color: #ffcf3f; }
#chatPanel.collapsed .chatTab.active { background: transparent; }
.chatTab .unread {
  position: absolute; top: -2px; right: -1px; min-width: 15px; height: 15px;
  padding: 0 4px; border-radius: 8px; background: #ff6a2f; color: #fff;
  font-size: 10px; line-height: 15px; text-align: center; font-weight: 800;
  box-shadow: 0 1px 3px rgba(0,0,0,.5); box-sizing: border-box;
}
#chatToggle {
  width: auto; margin: 0; padding: 0 9px; background: none; border: none;
  color: #b39c74; font-size: 13px; cursor: pointer; align-self: center;
}
#chatToggle:hover { color: #ffcf3f; }
#chatMute {
  width: auto; margin: 0; padding: 0 4px; background: none; border: none;
  font-size: 12px; line-height: 1; cursor: pointer; align-self: center; opacity: .85;
}
#chatMute:hover { opacity: 1; }
#chatMute.muted { opacity: .55; filter: grayscale(1); }
#chatBody {
  background: rgba(10,7,16,.82); border: 1px solid #4a3320; border-top: none;
  border-radius: 0 0 10px 10px; display: flex; flex-direction: column;
  backdrop-filter: blur(2px);
}
#chatPanel.collapsed #chatBody { display: none; }
#chatLog {
  height: 168px; overflow-y: auto; overflow-x: hidden; padding: 6px 8px;
  display: flex; flex-direction: column; gap: 3px;
  overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #4a3320 transparent;
}
#chatLog::-webkit-scrollbar { width: 7px; }
#chatLog::-webkit-scrollbar-thumb { background: #4a3320; border-radius: 4px; }
#chatLog::-webkit-scrollbar-track { background: transparent; }
.chatMsg { line-height: 1.35; overflow-wrap: anywhere; color: #efe4cb; }
.chatMsg .cn { font-weight: 700; margin-right: 5px; }
.chatMsg.mine { color: #fff; }
.chatSys { color: #9fe8ff; opacity: .72; font-style: italic; font-size: 12px; }
.chatEmpty { color: #7c6a4c; font-style: italic; text-align: center; padding: 8px 0; }
#chatInput {
  margin: 0; border: none; border-top: 1px solid #4a3320;
  border-radius: 0 0 10px 10px; background: rgba(0,0,0,.28);
  color: #fff; padding: 7px 9px; font-size: 13px; width: 100%; box-sizing: border-box;
}
#chatInput:focus { outline: none; background: rgba(0,0,0,.44); }
#chatInput::placeholder { color: #7c6a4c; }
/* Lift clear of the training controls (bottom-left) when they're on screen. */
body:has(#trainCtl:not(.hidden)) #chatPanel { bottom: 62px; }
@media (max-width: 680px), (max-height: 540px) {
  #chatPanel { width: min(260px, 62vw); font-size: 12px; }
  #chatLog { height: 118px; }
}

/* ================= Friends panel (right edge, collapsible) ================= */
/* Shown only for signed-in, non-guest accounts (JS toggles body.signedIn), and
   hidden behind the full-screen menus/modals that own the whole screen. Sits
   just below the top-right audio controls; layers over the menu and lobby
   (empty corner there) but is hidden during the shop/town/match and the Cash
   Shop so it never renders on top of the HUD or town — its z-index (16) sits
   above the in-game HUD (z5) and the .overlay backdrops (z10). */
#friendsPanel {
  position: fixed; top: 52px; right: 12px; z-index: 16;
  width: min(248px, 66vw); pointer-events: auto;
  display: none; flex-direction: column;
  font-size: 13px; touch-action: auto;
}
body.signedIn #friendsPanel { display: flex; }
body:has(#welcome:not(.hidden)) #friendsPanel,
body:has(#charEditor:not(.hidden)) #friendsPanel,
body:has(#options:not(.hidden)) #friendsPanel,
body:has(#codex:not(.hidden)) #friendsPanel,
body:has(#reportOverlay:not(.hidden)) #friendsPanel,
body:has(#escMenu:not(.hidden)) #friendsPanel,
body:has(#confirmModal:not(.hidden)) #friendsPanel,
body:has(#cashShop:not(.hidden)) #friendsPanel,
body:has(#whoOnline:not(.hidden)) #friendsPanel,
/* In-match: #hud is un-hidden for the whole session (town + shop + combat),
   #townHud/#shop cover the specific phases — hide friends over all of them so
   the panel never lands on top of the HUD or the walkable town. */
body:has(#hud:not(.hidden)) #friendsPanel,
body:has(#townHud:not(.hidden)) #friendsPanel,
body:has(#shop:not(.hidden)) #friendsPanel { display: none !important; }

#friendsBar {
  display: flex; align-items: stretch;
  background: rgba(10,7,16,.85); border: 1px solid #4a3320; border-bottom: none;
  border-radius: 10px 10px 0 0; padding: 2px 4px 0;
  backdrop-filter: blur(2px);
}
#friendsPanel.collapsed #friendsBar { border-radius: 10px; border-bottom: 1px solid #4a3320; padding: 3px 4px; }
#friendsToggleBtn {
  flex: 1; display: flex; align-items: center; gap: 6px; width: auto; margin: 0;
  padding: 5px 8px; border: none; background: transparent; color: #ffcf3f;
  font-weight: 800; font-size: 12.5px; cursor: pointer; position: relative; text-align: left;
}
#friendsToggleBtn:hover { color: #ffe08a; }
.fpIcon { font-size: 14px; }
.fpTitle { flex: 1; }
.fpBadge {
  min-width: 17px; height: 17px; padding: 0 5px; border-radius: 9px;
  background: #2c8a3e; color: #eaffe9; font-size: 10.5px; line-height: 17px;
  text-align: center; font-weight: 800; box-sizing: border-box;
  box-shadow: 0 0 0 1px rgba(0,0,0,.35);
}
.fpBadge.hidden { display: none; }
#friendsToggle {
  width: auto; margin: 0; padding: 0 9px; background: none; border: none;
  color: #b39c74; font-size: 13px; cursor: pointer; align-self: center;
}
#friendsToggle:hover { color: #ffcf3f; }

#friendsBody {
  background: rgba(10,7,16,.85); border: 1px solid #4a3320; border-top: none;
  border-radius: 0 0 10px 10px; display: flex; flex-direction: column;
  backdrop-filter: blur(2px); padding: 7px;
}
#friendsPanel.collapsed #friendsBody { display: none; }

.fpAddRow { display: flex; gap: 5px; }
#friendAddInput {
  flex: 1; min-width: 0; margin: 0; border: 1px solid #4a3320; border-radius: 7px;
  background: rgba(0,0,0,.3); color: #fff; padding: 6px 8px; font-size: 12.5px; box-sizing: border-box;
}
#friendAddInput:focus { outline: none; border-color: #7a5a30; background: rgba(0,0,0,.45); }
#friendAddInput::placeholder { color: #7c6a4c; }
#friendAddBtn {
  width: auto; margin: 0; padding: 6px 12px; border: none; border-radius: 7px;
  background: linear-gradient(#c8822e,#9a5a1e); color: #fff; font-weight: 800;
  font-size: 12.5px; cursor: pointer; flex: none;
}
#friendAddBtn:hover { filter: brightness(1.12); }
#friendAddBtn:disabled { opacity: .5; cursor: default; filter: none; }

.fpStatus { margin: 5px 1px 0; font-size: 11.5px; color: #b39c74; line-height: 1.3; }
.fpStatus:empty { display: none; }
.fpStatus.ok { color: #7fe08a; }
.fpStatus.err { color: #ff9a6a; }

#friendsScroll {
  margin-top: 6px; max-height: 46vh; overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #4a3320 transparent;
}
#friendsScroll::-webkit-scrollbar { width: 7px; }
#friendsScroll::-webkit-scrollbar-thumb { background: #4a3320; border-radius: 4px; }
#friendsScroll::-webkit-scrollbar-track { background: transparent; }

.fpSectionHd {
  color: #9a835e; font-size: 10.5px; font-weight: 800; text-transform: uppercase;
  letter-spacing: .04em; padding: 4px 2px 3px;
}
.fpEmpty { color: #7c6a4c; font-style: italic; font-size: 12px; text-align: center; padding: 8px 4px; }

.friendRow { display: flex; align-items: center; gap: 7px; padding: 5px 6px; border-radius: 7px; }
.friendRow:hover { background: rgba(74,51,32,.32); }
.fpDot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: #5c5142; }
.friendRow.online .fpDot { background: #57d873; box-shadow: 0 0 6px #57d873; }
.fpName { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #d9cbaa; font-weight: 600; }
.friendRow.online .fpName { color: #f3ead2; }
.fpTag { font-size: 10.5px; color: #8a7856; flex: none; }

.fpBtns { display: flex; gap: 4px; flex: none; }
.fpMini {
  width: auto; margin: 0; padding: 3px 7px; border: none; border-radius: 6px;
  font-size: 11px; font-weight: 800; cursor: pointer; line-height: 1.2;
}
.fpAccept { background: #2c8a3e; color: #eaffe9; }
.fpAccept:hover { filter: brightness(1.15); }
.fpDecline, .fpRemove { background: rgba(74,51,32,.6); color: #c9b184; }
.fpDecline:hover, .fpRemove:hover { background: #7a3226; color: #ffd9c9; }
.fpRemove { opacity: 0; transition: opacity .12s; font-size: 12px; padding: 2px 7px; }
.friendRow:hover .fpRemove { opacity: 1; }

/* Glow pulse when a friend comes online (row), plus an attention flash on the
   collapsed bar so you notice even when the panel is folded. */
@keyframes fpOnlineGlow {
  0% { background: rgba(60,190,90,.42); box-shadow: inset 0 0 0 1px rgba(90,220,120,.7); }
  100% { background: transparent; box-shadow: none; }
}
.friendRow.justOnline { animation: fpOnlineGlow 2.4s ease-out; }
@keyframes fpBarFlash {
  0%,100% { box-shadow: 0 0 0 rgba(90,220,120,0); }
  50% { box-shadow: 0 0 12px 1px rgba(90,220,120,.85); }
}
#friendsPanel.flash #friendsBar { animation: fpBarFlash 1s ease-in-out 2; border-color: #57d873; }

@media (max-width: 680px), (max-height: 540px) {
  #friendsPanel { width: min(210px, 60vw); font-size: 12px; top: 48px; }
}

/* ---- Who's online: the 🟢 chip's roster overlay ---- */
/* The chip reads as a button now (menu + server browser both open the roster). */
.onlineBtn { cursor: pointer; border-radius: 8px; padding: 3px 8px; display: inline-block; transition: color .1s, background .1s; }
.onlineBtn:hover, .onlineBtn:focus-visible { background: rgba(74,51,32,.42); color: #d7ffd8; outline: none; }
#whoOnlineList {
  max-height: 52vh; overflow-y: auto; margin: 0 -4px; padding: 0 4px;
  scrollbar-width: thin; scrollbar-color: #4a3320 transparent;
}
#whoOnlineList::-webkit-scrollbar { width: 7px; }
#whoOnlineList::-webkit-scrollbar-thumb { background: #4a3320; border-radius: 4px; }
.woRow {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 11px; margin-bottom: 6px;
  background: #191007; border: 1px solid #3a2817; border-radius: 9px;
}
.woRow:hover { border-color: #5c4023; }
.woRow .fpDot { background: #57d873; box-shadow: 0 0 6px #57d873; } /* everyone here is, by definition, online */
.woRow.guest .fpDot { background: #9a835e; box-shadow: none; } /* named, but no account behind them */
.woRow.guest .woName { color: #c3b48c; font-weight: 600; }
.woName { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #f3ead2; font-weight: 700; }
.woTag { font-size: 10.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: #8a7856; flex: none; }
.woTag.you { color: #e0a94a; }
.woWhere { font-size: 11.5px; color: #9a835e; flex: none; }
.woAdd { background: rgba(74,51,32,.6); color: #c9b184; font-size: 12px; }
.woAdd:hover { background: #2c8a3e; color: #eaffe9; }
.woGuests { text-align: center; font-size: 11.5px; color: #7c6a4c; font-style: italic; padding: 6px 4px 2px; }
#whoOnlineStatus.ok { color: #7fe08a; }
#whoOnlineStatus.err { color: #ff9a6a; }

/* ---- End-of-match MVP card (center) ---- */
#mvp {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  z-index: 6; pointer-events: auto;
}
.mvpCard {
  background: rgba(16,11,6,.94); border: 1px solid #6a4a28; border-radius: 16px;
  padding: 18px 26px 20px; text-align: center; min-width: 300px;
  box-shadow: 0 18px 60px rgba(0,0,0,.6), 0 0 0 4px rgba(255,207,63,.08);
  animation: mvpIn .3s cubic-bezier(.2,1.3,.5,1);
}
@keyframes mvpIn { from { transform: scale(.8); opacity: 0; } to { transform: none; opacity: 1; } }
.mvpHead { font-size: 15px; font-weight: 700; color: #ffcf3f; letter-spacing: .5px; }
.mvpBody { display: flex; flex-direction: column; align-items: center; gap: 4px; margin: 10px 0 14px; }
.mvpAv { width: 76px; height: 76px; image-rendering: auto; filter: drop-shadow(0 6px 10px rgba(0,0,0,.5)); }
.mvpName { font-size: 22px; font-weight: 800; }
.mvpStats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.mvpStats > div { display: flex; flex-direction: column; gap: 2px; }
.mvpStats b { font-size: 20px; color: #fff; }
.mvpStats span { font-size: 10px; color: #c7b596; text-transform: uppercase; letter-spacing: .4px; }
.mvpBtns { display: flex; gap: 8px; justify-content: center; margin-top: 14px; }
.mvpBtns:empty { display: none; }
.mvpBtns button {
  pointer-events: auto; padding: 8px 14px; font-size: 13px; border-radius: 9px;
  border: 1px solid #6a4a28; background: rgba(48,34,18,.9); color: #eee; cursor: pointer;
}
.mvpBtns button:hover { background: rgba(64,46,22,.95); }
@media (max-width: 640px) {
  .mvpCard { min-width: 0; width: 88vw; padding: 14px 16px 16px; }
  .mvpStats { gap: 6px; }
  .mvpStats b { font-size: 16px; }
  #killFeed .kfRow { font-size: 11px; }
}
/* Short viewports: the MVP card is a fixed px height, so on a squat window it eats
   most of the screen. Nudge the card down and shrink the end banner so the two
   still stack cleanly above/below instead of colliding. (Kept after the base #mvp
   rule so it wins on equal specificity.) */
@media (max-height: 620px) {
  #mvp { top: 56%; }
  #resultBanner img { max-height: 21vh; }
}

/* ---- Menu profile panel (lifetime stats + leaderboard) ---- */
#profilePanel {
  margin: 10px 0 2px; padding: 10px 12px;
  background: rgba(10,7,16,.5); border: 1px solid #3a2817; border-radius: 10px;
}
.ppMine { display: flex; flex-wrap: wrap; gap: 10px; align-items: baseline; justify-content: center; }
.ppMine .ppLvl { font-weight: 800; color: #ffcf3f; font-size: 15px; }
.ppMine .ppStat { font-size: 12px; color: #c7b596; }
.ppMine .ppStat b { color: #fff; font-size: 14px; margin-right: 3px; }
.ppBoardHead { margin: 10px 0 5px; font-size: 12px; font-weight: 700; color: #ffcf3f; text-align: center; }
.ppBoard { list-style: none; counter-reset: rank; margin: 0; padding: 0; }
.ppBoard li {
  counter-increment: rank; display: flex; justify-content: space-between; gap: 8px;
  padding: 3px 6px; border-radius: 6px; font-size: 13px;
}
.ppBoard li::before { content: counter(rank) "."; color: #8d7a58; margin-right: 6px; }
.ppBoard li.mine { background: rgba(255,207,63,.12); }
.ppBoard .ppName { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ppBoard .ppWins { color: #c7b596; white-space: nowrap; }

/* ---- Invite copy button + bot hint (lobby) ---- */
.copyInvite {
  margin-top: 10px; padding: 9px 14px; font-size: 14px; border-radius: 9px;
  border: 1px solid #6a4a28; background: rgba(48,34,18,.9); color: #eee; cursor: pointer;
}
.copyInvite:hover { background: rgba(64,46,22,.95); }
#botHint {
  margin: 8px 0 0; padding: 8px 12px; font-size: 13px; line-height: 1.35;
  color: #ffd9a8; background: rgba(60,40,16,.5); border: 1px dashed #7a5a30; border-radius: 9px;
}
/* No-gear room banner (everyone sees it, host or not) + the closed-rack note
   the gear stalls show in place of their racks. */
.equipOffNote {
  margin: 8px 0 0; padding: 8px 12px; font-size: 13px; line-height: 1.35;
  color: #e0cb7a; background: rgba(50,44,14,.5); border: 1px dashed #7a6a30; border-radius: 9px;
}
.stallNote.equipOffStall {
  padding: 14px 12px; text-align: center; font-size: 14px;
  color: #e0cb7a; background: rgba(50,44,14,.4); border: 1px dashed #7a6a30; border-radius: 9px;
}

/* ===================== Marketplace town (shop phase) =====================
   The between-rounds shop is a walkable canvas scene (src/town.js). The #shop
   overlay reappears as `.townpanel` — a centered stall panel over the dimmed
   town — and #townHud carries the gold/countdown/ready chrome. Dead-shop and
   Quick keep the classic overlay untouched. */
.overlay.townpanel { background: rgba(6, 4, 14, 0.45); }
/* ✕ close for the open stall panel — the only way out on touch (no ESC key, and
   the 94vw panel leaves almost no backdrop to tap). Sticky inside the scrolling
   panel so it stays pinned; the negative margin keeps it out of the flow. */
#townCloseBtn { display: none; }
.overlay.townpanel #townCloseBtn {
  display: block; position: sticky; top: 4px; z-index: 6;
  width: 38px; height: 38px; margin: 0 0 -38px auto; padding: 0;
  font-size: 18px; line-height: 36px; font-weight: 700; text-align: center;
  border-radius: 10px; border: 1px solid #6a4a20; background: #241a10; color: #ffdf9a; cursor: pointer;
}
.overlay.townpanel #townCloseBtn:hover { background: #2e2214; }
.overlay.townpanel .panel {
  width: min(880px, 94vw); max-height: 92vh; max-height: 92svh; overflow-y: auto;
  border-color: #5a3a1e;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.65), 0 0 0 1px rgba(255, 193, 60, 0.22);
}
/* the town replaces the tab strip + shop-phase chrome; #townHud owns them now */
.overlay.townpanel .panel > h1.small,
.overlay.townpanel .shopTop,
.overlay.townpanel #stalls,
.overlay.townpanel #spendHint,
.overlay.townpanel #shopReadyBtn,
.overlay.townpanel #shopStartBtn,
.overlay.townpanel #shopStatus { display: none !important; }

#townHud {
  position: fixed; top: 10px; left: 50%; transform: translateX(-50%);
  z-index: 7; display: flex; flex-direction: column; align-items: center; gap: 6px;
  pointer-events: none; max-width: 96vw;
}
#townHud .thRow { display: flex; align-items: center; gap: 8px; pointer-events: auto; flex-wrap: wrap; justify-content: center; }
#townHud .thChip {
  background: rgba(16, 10, 6, 0.85); border: 1px solid #5c4526; border-radius: 999px;
  padding: 6px 13px; font-size: 13px; color: var(--text); white-space: nowrap;
}
#townHud .thChip b { color: #ffcf3f; }
#townHud .thChip.xpChip { display: inline-flex; align-items: center; gap: 6px; }
#townHud .thChip.xpChip b { color: #ffd94a; }
#townHud #townCountdown { color: #ffcf3f; font-weight: 700; }
#townHud #townCountdown.urgent { color: #ff5a5a; animation: countPulse 0.6s ease-in-out infinite; }
#townHud button { /* beware the base button { width:100% } */
  width: auto; margin: 0; padding: 7px 16px; font-size: 14px; border-radius: 999px;
}
#townHud .hint { pointer-events: none; font-size: 12px; color: #d4c4a4; border-style: dashed; }

/* First-shop coached tutorial bubble — bottom-center, non-blocking (only the
   card itself takes clicks). Ember-framed to match the fire UI skin. */
#shopCoach {
  position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%);
  z-index: 9; width: min(520px, 92vw); box-sizing: border-box;
  background: linear-gradient(180deg, rgba(30, 16, 8, 0.96), rgba(18, 10, 6, 0.96));
  border: 1px solid #7a5a2c; border-radius: 14px;
  box-shadow: 0 0 0 1px rgba(255, 176, 74, 0.18), 0 10px 30px rgba(0, 0, 0, 0.55), 0 0 26px rgba(255, 138, 46, 0.28);
  padding: 14px 40px 14px 16px; text-align: left; pointer-events: auto;
  animation: coachIn 0.28s ease-out;
}
#shopCoach.hidden { display: none; }
#shopCoachBody { font-size: 14px; line-height: 1.5; color: #f0e4cf; }
#shopCoachBody b { color: #ffcf3f; }
#shopCoachSkip {
  position: absolute; top: 8px; right: 8px; width: 24px; height: 24px;
  margin: 0; padding: 0; border-radius: 999px; line-height: 1;
  background: rgba(0, 0, 0, 0.35); border: 1px solid #5c4526; color: #d4c4a4;
  font-size: 13px; cursor: pointer;
}
#shopCoachSkip:hover { color: #fff; border-color: #7a5a2c; }
@keyframes coachIn { from { opacity: 0; transform: translate(-50%, 14px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* First-ever out-of-mana nudge — a small bubble JS anchors above the Meditate
   chip (it sets left/top to the slot's top-centre; the transform lifts it up and
   centres it). Mana-blue chrome to echo the Meditate glow it points at. */
#manaTip {
  position: fixed; z-index: 45; transform: translate(-50%, -100%); pointer-events: none;
  width: max-content; max-width: 260px; text-align: center;
  background: linear-gradient(180deg, rgba(14, 26, 38, 0.97), rgba(9, 16, 26, 0.97));
  border: 1px solid #4d86b8; border-radius: 12px;
  box-shadow: 0 0 0 1px rgba(143, 216, 255, 0.18), 0 8px 22px rgba(0, 0, 0, 0.55), 0 0 20px rgba(90, 170, 240, 0.3);
  padding: 9px 13px; font-size: 13px; line-height: 1.45; color: #e6f2ff;
  animation: manaTipIn 0.28s ease-out;
}
#manaTip.hidden { display: none; }
#manaTip b { color: #9fdcff; }
#manaTip .mtKey {
  display: inline-block; font-weight: 800; color: #0c141c; letter-spacing: .3px;
  padding: 1px 7px; border-radius: 6px; background: #9fdcff; box-shadow: 0 0 8px rgba(143, 216, 255, 0.7);
}
/* little downward pointer toward the Meditate chip below */
#manaTip::after {
  content: ""; position: absolute; left: 50%; bottom: -7px; transform: translateX(-50%);
  border-left: 7px solid transparent; border-right: 7px solid transparent;
  border-top: 7px solid #0e1a26;
}
@keyframes manaTipIn { from { opacity: 0; transform: translate(-50%, calc(-100% + 10px)); } to { opacity: 1; transform: translate(-50%, -100%); } }

/* town mode: the match HUD stays for phase/scoreboard, but combat chrome goes */
body.townmode #selfBars,
body.townmode #statusBar,
body.townmode #killFeed { display: none !important; }

/* ---- Alchemist / Armory stall panels (town-only) ---- */
#alchBody, #armoryBody { text-align: left; }
.stallNote { font-size: 13px; color: #d4c4a4; margin: 2px 0 10px; line-height: 1.4; }
.stallNote.empty { color: #9d8b6c; font-style: italic; }
.stallRow {
  display: flex; align-items: center; gap: 10px; margin: 8px 0;
  background: rgba(24, 16, 8, 0.55); border: 1px solid #3a2a14; border-radius: 10px;
  padding: 9px 12px;
}
.stallRow .stallIc { font-size: 22px; flex: none; }
.stallRow .stallTxt { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
.stallRow .stallTxt b { font-size: 14px; }
.stallRow .stallTxt span { font-size: 12px; color: #c4b190; }
.stallRow button { width: auto; margin: 0; padding: 8px 14px; font-size: 13px; flex: none; }
.stallRow .ghostBtn { background: transparent; border: 1px solid #5c4526; color: var(--text); }
.stallRow .ghostBtn:hover { background: #281b0e; }
.stallRow.saveRow { background: none; border-style: dashed; }
.stallRow .stallHintTxt { font-size: 13px; color: #c4b190; letter-spacing: 1px; }
/* dashed section rule between shop groups (generic; #outfitShop tweaks its margins) */
.stallDivide { border: none; border-top: 1px dashed #3a2a14; margin: 12px 0 8px; }
/* "Coming soon" WIP rows/panels: dimmed so they read as not-yet-buyable */
.stallRow.csRow { opacity: 0.6; }
.comingSoon {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; padding: 48px 20px; text-align: center; opacity: 0.85;
}
.comingSoon .csIcon { font-size: 46px; filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5)); }
.comingSoon p { margin: 0; font-size: 18px; font-weight: 700; letter-spacing: 0.5px; color: #f5edd8; }
/* Saved-build shelf (armory): 2-up + vh-capped self-contained scroll, so six
   presets can't push the panel past the townpanel's 92vh (menus: no scrollbars
   on the panel itself). */
#armoryBody .presetList {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
  max-height: 24vh; overflow-y: auto;
}
#armoryBody .presetList .stallRow { margin: 0; }
@media (max-width: 900px) { #armoryBody .presetList { grid-template-columns: 1fr; } }
/* Short viewports (≈720p): tighten the stall panels so the armory (the tallest —
   purse + 2 outfit rows + presets + save row) still clears the 92vh cap. */
@media (max-height: 800px) {
  #armoryBody .presetList { max-height: 16vh; }
  #outfitShop .staffCard, #tailorShop .staffCard, #weaponsBody .staffCard { padding: 5px 8px; }
  #armoryBody .stallNote, #weaponsBody .stallNote { margin: 2px 0 6px; }
  #armoryBody .gearHead { margin: 4px 0 5px; }
  #armoryBody .gearHead.sep { padding-top: 6px; margin-top: 7px; }
  #armoryBody .gearIntro { display: none; } /* intro is nice-to-have; drop it when vertical space is tight */
}

/* ---- Weaponsmith stall (weapon skins; own #weaponsBody panel, its own town rack) ---- */
#weaponsBody .staffGrid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; } /* 14 cards → 3 rows in the 1160px panel */
.staffCard { /* base button{width:100%} is fine inside a grid cell */
  display: flex; align-items: center; flex-wrap: wrap; row-gap: 3px; gap: 8px; margin: 0;
  background: rgba(24, 16, 8, 0.55); border: 1px solid #3a2a14; border-radius: 10px;
  padding: 7px 10px; font-size: 12px; cursor: pointer; text-align: left;
}
.staffCard:hover { border-color: var(--stc, #8a6a3a); }
.staffCard img { width: 58px; height: 19px; object-fit: contain; flex: none; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5)); }
.staffCard .staffBranchIc { width: 58px; font-size: 18px; text-align: center; flex: none; }
.staffCard .staffName { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.staffCard .staffStat { flex-basis: 100%; font-size: 10px; line-height: 1.25; color: #a89a78; white-space: normal; }
.staffCard .staffState { flex: none; font-size: 11px; color: #c4b190; }
.staffCard .staffState.buy { color: #ffcf3f; font-weight: 700; }
.staffCard.equipped { border-color: #ffd94a; box-shadow: 0 0 9px rgba(255, 217, 74, 0.3) inset; }
.staffCard.equipped .staffState { color: #ffd94a; }
.staffCard.locked { opacity: 0.5; cursor: default; }
/* Staff LEVEL row (weaponsmith): ◆ pips up the family art ladder + the next
   step's price tags. Sits full-width under the state line; maxed shows a star. */
.staffCard .staffLvlRow { flex-basis: 100%; font-size: 11px; color: #c4b190; display: flex; align-items: center; gap: 5px; }
.staffCard .staffLvlRow .staffPips { color: var(--stc, #ffd94a); letter-spacing: 1px; }
.staffCard .staffLvlRow:not(.maxed):hover { color: #ffd94a; cursor: pointer; }
.staffCard .staffLvlRow.maxed { color: #ffd94a; }
.staffCard .staffLvlRow .priceTag { font-size: 10px; padding: 0 4px; border: 1px solid #5a4a2a; border-radius: 4px; }
.staffCard .staffLvlRow .priceTag.cashTag { color: #8fe89a; border-color: #3f5a2a; }
.staffCard .staffLvlRow .priceTag.broke { opacity: 0.45; }
.staffCard .staffLvlRow .priceTag img { width: 20px; height: 16px; object-fit: contain; object-position: center; filter: none; }
/* ---- Dual price tags: 🪙 gold (this game) vs 💵 cash (account-forever). The
   cash tag's treasure PNG grows with the price (cash1 bill → cash6 briefcase). */
.staffCard .staffState .priceTag {
  display: inline-flex; align-items: center; gap: 3px; margin-left: 5px;
  padding: 2px 7px; border-radius: 8px; font-size: 11px; font-weight: 700;
  background: rgba(40, 28, 12, 0.8); border: 1px solid #5a4420; color: #ffcf3f;
}
.staffCard .staffState .priceTag.cashTag { color: #8fe89a; border-color: #3f5a2a; }
.staffCard .staffState .priceTag:hover { border-color: #ffd94a; }
.staffCard .staffState .priceTag.cashTag:hover { border-color: #8fe89a; }
.staffCard .staffState .priceTag.broke { opacity: 0.45; }
.staffCard .staffState .priceTag img { width: 20px; height: 16px; object-fit: contain; object-position: center; filter: none; } /* fixed box: the treasure PNGs vary 1.03–1.85 aspect, so `contain` letterboxes each into the same footprint & centers it (beats the 58×19 staff-art rule) */
.stallNote.purse { font-size: 14px; margin-bottom: 6px; }
.stallNote.purse .goldBal { color: #ffd94a; font-weight: 700; }
.stallNote.purse .cashBal { color: #8fe89a; font-weight: 700; }
.stallNote.purse .purseHint { display: block; font-size: 11px; color: #9d8b6c; margin-top: 2px; }
@media (max-width: 900px) { #weaponsBody .staffGrid { grid-template-columns: repeat(2, 1fr); } }

/* ---- 💵 Cash Shop: treasure top-up packs (CraftPix Treasure-Shop art). Three
   tiers tuned for the DECOY EFFECT — the mid pack is priced just under the big
   one, so the highlighted BEST-VALUE pack (amber glow + ribbon + bonus starburst
   + struck promo price) reads as the obvious deal. ---- */
#cashShop .panel.wide { width: min(640px, 95vw); }
.csGrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 14px; align-items: stretch; }
.csCard {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 14px 10px 12px; text-align: center;
  background: linear-gradient(#2a1e12, #1b130b);
  border: 2px solid #4a3418; border-radius: 12px;
  box-shadow: 0 4px 14px rgba(0,0,0,.35);
}
/* Best-value pack: amber glow + a slight lift so the eye lands here. */
.csCard.best {
  border-color: #ffb43a;
  background: linear-gradient(#3a2a12, #221708);
  box-shadow: 0 0 0 1px rgba(255,180,58,.5), 0 6px 22px rgba(255,150,40,.3);
  transform: translateY(-4px);
}
.csArt { height: 92px; display: flex; align-items: flex-end; justify-content: center; margin-top: 6px; }
.csArt img { max-height: 92px; max-width: 100%; object-fit: contain; filter: drop-shadow(0 3px 5px rgba(0,0,0,.5)); }
.csCard.best .csArt { height: 106px; }
.csCard.best .csArt img { max-height: 106px; }
.csAmount {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 18px; font-weight: 900; color: #ffe6a0; letter-spacing: .3px;
}
.csAmount .csCoin { width: 20px; height: 16px; object-fit: contain; }
.csCard.best .csAmount { font-size: 20px; color: #ffd66b; }
.csPrice { display: inline-flex; align-items: baseline; gap: 6px; font-size: 15px; font-weight: 800; color: #cbb98f; }
.csPrice b { color: #f5edd8; font-size: 16px; }
.csCard.best .csPrice b { color: #ffe08a; }
.csWas { color: #b46262; font-size: 12px; font-weight: 700; text-decoration: line-through; text-decoration-thickness: 2px; }
.csCard button.csBuy {
  width: 100%; margin-top: 8px; padding: 10px 8px;
  font-size: 15px; font-weight: 800; color: #12240f;
  background: linear-gradient(#8fe89a, #4bbf5f); border: 2px solid #2e7a3e; border-radius: 10px;
  box-shadow: 0 2px 0 #216030, 0 3px 6px rgba(0,0,0,.3);
  cursor: pointer; text-shadow: 0 1px 0 rgba(255,255,255,.3); transition: filter .1s, transform .06s;
}
.csCard button.csBuy:hover { filter: brightness(1.08); }
.csCard button.csBuy:active { transform: translateY(2px); box-shadow: 0 0 0 #216030, 0 1px 3px rgba(0,0,0,.3); }
/* "BEST VALUE" ribbon across the top of the winning card. */
.csRibbon {
  position: absolute; top: -11px; left: 50%; transform: translateX(-50%);
  padding: 3px 12px; font-size: 11px; font-weight: 900; letter-spacing: .6px; white-space: nowrap;
  color: #3a1e04; background: linear-gradient(#ffe07a, #ffab2e);
  border: 1.5px solid #b06a12; border-radius: 20px; box-shadow: 0 2px 6px rgba(0,0,0,.4);
}
/* "+40% BONUS" gold starburst badge (Badges.png) in the top-right corner. */
.csBonus {
  position: absolute; top: -14px; right: -12px; width: 58px; height: 58px; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center; line-height: 1;
  background: url("assets/cashshop/bonus.png") center/contain no-repeat;
  transform: rotate(12deg); filter: drop-shadow(0 2px 3px rgba(0,0,0,.45));
  color: #5a2e00; text-shadow: 0 1px 0 rgba(255,255,255,.35);
}
.csBonus b { font-size: 15px; font-weight: 900; }
.csBonus small { font-size: 8px; font-weight: 800; letter-spacing: .5px; }
@media (max-width: 560px) {
  .csGrid { grid-template-columns: 1fr; gap: 18px; }
  .csCard.best { transform: none; }
  .csBonus { right: 4px; }
}
#renameModal .welcomeName { text-align: left; margin: 4px 0 2px; }
#renameModal #renameInput { width: 100%; }

/* 🧙 editor "Change Name" — premium action button, 🔒 badge when unaffordable. */
#charEdNameActions { margin-top: 8px; }
.premiumBtn { position: relative; display: inline-flex; align-items: center; gap: 6px; }
.premiumBtn.locked { opacity: .88; border-color: #7a5a2a; }
.premiumBtn .btnLock { font-size: 12px; line-height: 1; filter: drop-shadow(0 1px 1px #000); }

/* ---- Gear-slot headers (armory): robes, cloaks & bandanas each get the same
   icon + name + slot-tag + note header so the three racks read as equal siblings,
   not one headline plus afterthoughts. `.sep` rules off each rack after the first. */
.gearIntro { color: #c4b190; }
.gearHead { display: flex; align-items: flex-start; gap: 9px; margin: 8px 0 8px; }
.gearHead.sep { border-top: 1px dashed #3a2a14; padding-top: 10px; margin-top: 12px; }
.gearHeadIc { font-size: 22px; line-height: 1.1; flex: none; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5)); }
.gearHeadTxt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.gearHeadTitle { display: flex; align-items: baseline; gap: 8px; }
.gearHeadTitle b { font-size: 15px; letter-spacing: 0.4px; color: #f5edd8; }
.gearHeadTitle .gearSlot {
  font-size: 10px; text-transform: uppercase; letter-spacing: 1px; color: #b8a274;
  background: rgba(58, 42, 20, 0.6); border: 1px solid #4a381c; border-radius: 6px; padding: 1px 7px;
}
.gearHeadNote { font-size: 12px; color: #a89a78; line-height: 1.35; }

/* ---- Armory outfit (robe) rack; #outfitShop sits between staves & presets ---- */
#outfitShop .staffGrid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; } /* 12 cards (tunic + o1–o11) → 2 rows in the 1160px panel */
.staffCard img.outfitAv { width: 30px; height: 34px; object-fit: contain; image-rendering: pixelated; } /* upright portrait, not a lying staff */
#outfitShop .staffBranchIc { width: 30px; }
@media (max-width: 900px) { #outfitShop .staffGrid { grid-template-columns: repeat(2, 1fr); } }

/* ---- Tailor racks (cloaks + bandanas, inside the armory panel): same card grid
   as the robe rack so all three gear slots line up; cards show the CraftPix pack's
   own 32px icon, chunky-scaled like every other pixel art. */
#tailorShop .staffGrid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; } /* 9 cards (none + c1–c8) → 2 rows, matching the robe rack */
.staffCard img.tailorIc { width: 34px; height: 34px; object-fit: contain; image-rendering: pixelated; }
#tailorShop .staffBranchIc { width: 34px; }
#tailorShop .stallDivide { border: none; border-top: 1px dashed #3a2a14; margin: 12px 0 8px; }
@media (max-width: 900px) { #tailorShop .staffGrid { grid-template-columns: repeat(2, 1fr); } }

/* ---- Fitting room (armory + weaponsmith): square live preview of YOUR mage
   (sticky, left of the rack) wearing the equipped staff + outfit; hovering a
   card tries it on. Same bronze-frame card as the spell diorama. Shared by both
   town stalls, so it's classed — only one stall panel is ever open at a time. -- */
.armoryWrap { display: flex; gap: 14px; align-items: flex-start; }
#armoryBody, #weaponsBody { flex: 1; min-width: 0; }
.armoryPreview {
  flex: none; width: 236px; position: sticky; top: 0;
  padding: 8px; border: 1px solid #4a3320; border-radius: 14px;
  background:
    radial-gradient(120% 90% at 50% 30%, rgba(120, 64, 28, .18), transparent 70%),
    linear-gradient(180deg, #17100a, #0b0710);
}
/* beats the global `canvas { position: fixed; inset: 0 }` for in-panel canvases */
.armoryPrevCv {
  position: static; inset: auto; display: block; width: 100%; aspect-ratio: 1; height: auto;
  cursor: default; border: 1px solid #3a2a14; border-radius: 10px;
  background: radial-gradient(90% 70% at 50% 78%, rgba(120, 84, 40, .22), transparent 65%), rgba(10, 6, 3, 0.55);
  image-rendering: pixelated;
}
.armoryPrevCap { margin-top: 7px; font-size: 12px; line-height: 1.45; color: #d4c4a4; text-align: center; }
.armoryPrevCap b { color: var(--text); }
.armoryPrevHint { margin: 5px 0 0; font-size: 10.5px; color: #9d8b6c; text-align: center; line-height: 1.35; }
/* fitting-room buff readout: the same "Your build" totals as the skill window,
   compacted under the turntable (renderFitBuffs, src/main.js) */
.fitBuffs { margin-top: 7px; border-top: 1px solid #3a2817; padding-top: 5px; text-align: left; font-size: 11px; line-height: 1.4; max-height: 30vh; overflow-y: auto; }
.fitBuffs h4 { font-size: 10px; text-transform: uppercase; letter-spacing: .4px; color: #9d8b6c; margin: 5px 0 2px; }
.fitBuffs h4:first-child { margin-top: 0; }
.fitBuffs .buffRow { padding: 0.5px 0; }
.overlay.deadmode .armoryPreview { display: none; }
@media (max-width: 900px) {
  .armoryWrap { flex-direction: column; align-items: center; }
  .armoryPreview { position: static; width: 200px; }
  #armoryBody, #weaponsBody { width: 100%; }
}

/* ============================================================================
   Touch controls (body.touchMode — overlay built by src/touch.js).
   z-index 6: above the #hud layer (5) so the zones win over the canvas HUD,
   below chat (15) and every real overlay/panel (30+), which stay tappable.
   ========================================================================== */
#touchUi {
  position: fixed; inset: 0; z-index: 6; pointer-events: none;
  user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent;
}
#touchUi.tcHidden { display: none; }
/* Floating-stick capture zone: the left band of the screen (thumb space).
   Stops above the bottom chat bar's row so the collapsed chat stays reachable. */
#tcMoveZone {
  position: absolute; left: 0; top: 12%; bottom: 0; width: 45%;
  pointer-events: auto; touch-action: none;
}
#tcStick { position: absolute; inset: 0; pointer-events: none; }
#tcStick.tcHidden { display: none; }
#tcRing {
  position: absolute; width: 124px; height: 124px; margin: -62px 0 0 -62px;
  border: 2px solid rgba(255,233,194,.30); border-radius: 50%;
  background: rgba(10,7,16,.18);
}
#tcKnob {
  position: absolute; width: 54px; height: 54px; margin: -27px 0 0 -27px;
  border-radius: 50%; background: rgba(255,233,194,.34);
  border: 2px solid rgba(255,233,194,.55);
}
/* Spell cluster: FIRE under the right thumb, secondaries arced around it. */
#tcCluster {
  position: absolute;
  right: calc(8px + env(safe-area-inset-right));
  bottom: calc(10px + env(safe-area-inset-bottom));
  width: 240px; height: 215px; pointer-events: none;
}
.tcBtn {
  position: absolute; pointer-events: auto; touch-action: none;
  display: flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; border-radius: 50%;
  background: rgba(20,14,30,.58); border: 2px solid rgba(255,233,194,.30);
  font-size: 26px; line-height: 1;
  box-shadow: 0 2px 8px rgba(0,0,0,.45);
}
.tcBtn.tcDown { background: rgba(255,233,194,.32); border-color: rgba(255,233,194,.7); }
/* Castability states mirrored from the spellbar (touch.js state updater).
   Locked = not unlocked yet (W/E/R before player level 4/7/10) — near-invisible
   so the live kit reads at a glance. Cooling = dark conic sweep + seconds left.
   No-mana/depleted = drained grey with a mana-blue ring. */
.tcBtn .tcIco { pointer-events: none; }
.tcBtn .tcCd {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font: 700 17px/1 system-ui, sans-serif; color: #ffe9c2; pointer-events: none;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85); z-index: 1;
}
.tcFire .tcCd { font-size: 26px; }
.tcBtn.tcCooling::after {
  content: ""; position: absolute; inset: -2px; border-radius: 50%;
  background: conic-gradient(rgba(8, 6, 14, 0.74) calc(var(--cd, 0) * 1turn), transparent 0);
}
.tcBtn.tcCooling .tcIco { opacity: 0.55; }
.tcBtn.tcLock { opacity: 0.38; }
.tcBtn.tcLock .tcIco { filter: grayscale(1); }
.tcBtn.tcNomana { border-color: rgba(90, 140, 255, 0.65); }
.tcBtn.tcNomana .tcIco { filter: grayscale(1); opacity: 0.5; }
.tcBtn.tcSilenced .tcIco { filter: grayscale(1); opacity: 0.4; }
.tcBtn.tcOn { border-color: rgba(94, 224, 106, 0.8); background: rgba(24, 60, 30, 0.6); }
.tcFire { width: 86px; height: 86px; font-size: 42px; right: 4px; bottom: 4px; }
.tcW    { right: 104px; bottom: 8px;   }
.tcE    { right: 96px;  bottom: 76px;  }
.tcR    { right: 38px;  bottom: 106px; }
.tcDash { right: 170px; bottom: 52px;  }
.tcMed  { right: 152px; bottom: 124px; width: 46px; height: 46px; font-size: 22px; }
/* Pause + standings — right edge, stacked below the #audioCtl pill (top-right,
   ~260px wide) and above the spell cluster (starts ~180px up from the bottom). */
#tcTop {
  position: absolute; top: calc(52px + env(safe-area-inset-top));
  right: calc(8px + env(safe-area-inset-right));
  display: flex; flex-direction: column; gap: 8px; pointer-events: none;
}
.tcSmall {
  pointer-events: auto; touch-action: none;
  width: 42px; height: 42px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center; font-size: 20px;
  background: rgba(20,14,30,.58); border: 1px solid rgba(255,233,194,.30);
}
/* Phone-landscape HUD tuning while touch mode is on.
   Reference viewport: ~823x412 CSS (a 1080p phone in landscape). That is ~200px
   SHORTER and ~270px narrower than a small desktop window, so the desktop HUD
   lands on top of itself here — every rule below resolves a measured overlap. */
/* The bottom row has three tenants: the chat/training column on the left, the
   spellbar, and the touch cluster on the right (575+). At the desktop's 44% the
   spellbar starts at x=191 and collides with both. Recentre it into the gap and
   scale it down so all three fit. */
body.touchMode #spellbar {
  transform: translateX(-50%) scale(.75); transform-origin: bottom center;
  bottom: 8px; left: 51%;
}
/* The audio pill is ~253px wide and owns the top-right corner, so the pause /
   standings buttons had nowhere to go but on top of the scoreboard. Slide the
   pill left and the buttons take the corner, above the scoreboard's row. */
body.touchMode #audioCtl { right: 108px; }
body.touchMode #tcTop {
  top: calc(4px + env(safe-area-inset-top));
  right: calc(8px + env(safe-area-inset-right));
  left: auto; flex-direction: row;
}
/* Scoreboard: desktop type makes this ~277px wide — a third of the screen —
   which the self bars run into. Compact type keeps it clear of them. (It lists
   humans only, so it stays short; full standings live on the 🏆 button.) */
body.touchMode #scoreboard {
  font-size: 10px; min-width: 0; padding: 5px 8px;
}
body.touchMode #scoreboard .sc { font-size: 9px; }
body.touchMode #scoreboard .row { padding: 1px 0; gap: 8px; }
/* Self HP/mana/XP stack. The desktop sizes are tuned for a wide window: 560px
   is over half a phone's width, and the 22/15/5px bars plus the name eat the
   top of the arena. Slim the bars and tighten the type — the arena is the part
   worth seeing, and these read fine small because they're colour-coded. */
body.touchMode #selfBars {
  top: 48px; width: min(340px, 46vw); gap: 3px;
}
body.touchMode #selfBars .sbName { font-size: 11px; }
body.touchMode #selfBars .sbNums { font-size: 10px; margin-left: 5px; }
body.touchMode #selfBars .sbLvl { font-size: 10px; }
body.touchMode #selfBars .sbShield { margin: 0 2px; }
body.touchMode #selfBars .sbBar { height: 12px; border-radius: 5px; }
body.touchMode #selfBars .sbBar.mana { height: 8px; }
body.touchMode #selfBars .sbBar.xp { height: 3px; border-radius: 2px; }
body.touchMode #statusBar { gap: 3px; margin-top: 3px; }
body.touchMode #statusBar .chip {
  width: 20px; height: 20px; font-size: 11px; border-radius: 5px;
}
/* Chat. The desktop panel is 340px wide over a 168px log; at 340px its right
   edge rides over the spellbar, and it parks in the bottom-left — exactly where
   the left thumb reaches for the stick (chat is z15, the touch layer z6, so it
   swallows those touches). The small-window query further down never rescues
   us: a landscape phone is ~1100x620 CSS px, outside both of its bounds. */
/* Width matches the training Loadout/Level Up row below it, so the two share a
   right edge instead of stepping. The spellbar (recentred above) starts past it. */
body.touchMode #chatPanel { width: min(250px, 32vw); font-size: 11px; }
body.touchMode #chatLog { height: 92px; }
body.touchMode .chatTab { padding: 3px 8px; font-size: 10.5px; }
body.touchMode .chatTab .unread {
  min-width: 13px; height: 13px; font-size: 9px; line-height: 13px;
}
body.touchMode #chatToggle { padding: 0 6px; font-size: 11px; }
body.touchMode #chatMute { font-size: 10px; }
body.touchMode #chatInput { padding: 5px 7px; font-size: 11px; }
body.touchMode .chatSys { font-size: 10px; }
/* Keyboard hints make no sense on a phone: the spellbar's Q/E/F/R key chips,
   the hold-TAB standings hint and the death-cam ←/→ hint all go (the touch
   overlay's 🏆 button and the tappable ‹ › arrows already cover those). */
body.touchMode .slot .key,
body.touchMode .tabHint,
body.touchMode .dtHint { display: none; }
/* Town HUD on phones. The desktop chip strip is two rows (~107px) hung from
   the top-center — at a 412px-tall viewport that lands EXACTLY on the Guild
   Hall / weapons stalls, and its row swallowed the taps meant for them
   (feedback: "can't reach the skill shop, things in front of it"). Compact
   the chips, let every non-button tap fall through to the canvas, and park
   the ready/force-start buttons in the free bottom-right corner instead. */
/* De-transform the strip: a transformed ancestor becomes the containing block
   for position:fixed, which would pin the ready buttons to the strip instead
   of the viewport corner. Stretch + flex-center replaces translateX(-50%). */
body.touchMode #townHud {
  /* bottom-center, just above the spellbar: the top band belongs to the
     Guild Hall / weapons stalls and the audio pill, and every top placement
     tried covered one of them at 412px tall */
  top: auto; bottom: 64px; gap: 3px; left: 0; right: 0; transform: none; max-width: none;
}
body.touchMode #townHud .thRow { pointer-events: none; gap: 4px; }
body.touchMode #townHud .thChip { font-size: 11px; padding: 3px 9px; }
body.touchMode #townHud .hint { font-size: 10.5px; padding: 2px 8px; }
body.touchMode #townHud button {
  pointer-events: auto; position: fixed;
  right: calc(10px + env(safe-area-inset-right));
  font-size: 13px; padding: 9px 15px;
}
body.touchMode #townReadyBtn { bottom: calc(12px + env(safe-area-inset-bottom)); }
body.touchMode #townStartBtn { bottom: calc(60px + env(safe-area-inset-bottom)); }
