:root {
  --gold: #f6c46a;
  --lumber: #6cd28a;
  --ally: #5aa9ff;
  --enemy: #ff6b6b;
  --panel: rgba(20, 22, 30, 0.78);
  --panel-edge: rgba(255, 255, 255, 0.08);
  --text: #e7ecf4;
  --muted: #9aa3b2;
  --good: #88e09f;
  --bad: #ff7676;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  background: #0a0d14;
  color: var(--text);
  font-family: -apple-system, "Segoe UI", Roboto, system-ui, sans-serif;
}
/* Lobby / admin / units / room pages should scroll AND allow text
   selection. The GAME page itself sets `body.game-page` (game.html)
   to opt into the kiosk-style locked overflow + no-select, since the
   pointer-up / drag handlers run the entire viewport and selecting
   text there interferes with click-to-build / drag-to-pan. */
body.game-page {
  overflow: hidden;
  user-select: none;
}

#game {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  display: block;
  /* Mobile: disable browser-level touch gestures (pinch-zoom-page,
     swipe-to-go-back, overscroll bounce) on the 3D canvas so our
     own touch handlers get clean events. The HUD overlay still
     responds to taps because pointer-events: auto is set on its
     children. */
  touch-action: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

#loading {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #0a0d14;
  z-index: 100;
  font-size: 22px;
  color: var(--muted);
  letter-spacing: 0.08em;
  transition: opacity 600ms ease;
}
#loading.hidden { opacity: 0; pointer-events: none; }

#hud {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 10;
}

#hud > * { pointer-events: auto; }

/* ---- Top bar ---- */
#top-bar {
  position: absolute;
  top: 12px;
  left: 12px;
  right: 12px;
  display: flex;
  gap: 14px;
  align-items: center;
  padding: 10px 14px;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 12px;
  backdrop-filter: blur(8px);
}

.resource {
  font-weight: 700;
  font-size: 16px;
  letter-spacing: 0.02em;
  display: flex;
  align-items: center;
  gap: 6px;
}
.ricon {
  width: 20px;
  height: 20px;
  object-fit: contain;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4));
}
.resource.kills .ricon { width: 16px; height: 16px; }
.resource.gold   { color: var(--gold); }
.resource.lumber { color: var(--lumber); }
/* Legendary point pill: gold star next to the count. When the player
   has spent their point the whole chip dims so they know they can't
   afford another legendary unless one of theirs is destroyed. */
.resource.legendary-pt {
  color: #ffd24a;
  text-shadow: 0 0 8px rgba(255, 210, 74, 0.45), 0 1px 0 #000;
}
.resource.legendary-pt .lp-star {
  font-size: 18px;
  line-height: 1;
  filter: drop-shadow(0 0 4px rgba(255, 210, 74, 0.6));
}
.resource.legendary-pt.spent {
  color: #6a5a3a;
  text-shadow: none;
  opacity: 0.55;
}
.resource.legendary-pt.spent .lp-star {
  filter: none;
  opacity: 0.7;
}
/* Quick pulse animation triggered by effects.flashTopBarGold() when an
   income burst or kill bounty lands. The element scales briefly and
   the text-shadow glows to draw the eye to "+N landed". */
@keyframes gold-flash-kf {
  0%   { transform: scale(1.0); text-shadow: 0 0 0 rgba(255,210,74,0); }
  35%  { transform: scale(1.30); text-shadow: 0 0 14px rgba(255,210,74,0.9), 0 1px 0 #000; }
  100% { transform: scale(1.0); text-shadow: 0 0 0 rgba(255,210,74,0); }
}
#gold.gold-flash { animation: gold-flash-kf 600ms ease-out; display: inline-block; transform-origin: center; }
/* Match clock — monospace + amber so it doesn't compete with the
   resource counts but is still glanceable. */
.resource.match-time {
  color: #ffd9a8;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 2px 8px;
  margin-left: 4px;
  border-left: 1px solid var(--panel-edge);
}
.resource.match-time::before {
  content: "⏱";
  margin-right: 4px;
  opacity: 0.7;
}
.resource.income { color: var(--muted); font-weight: 500; font-size: 13px; }

.hud-btn {
  margin-left: 6px;
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  border: 1px solid var(--panel-edge);
  border-radius: 6px;
  cursor: pointer;
}
.hud-btn:hover { background: rgba(255, 255, 255, 0.10); }
/* Anchor-as-hud-btn (the Lobbies link) — kill default underline/color. */
a.hud-btn { text-decoration: none; display: inline-block; }
a.hud-btn:visited { color: var(--text); }
/* Viewer count: subtle amber tint so it reads as info, not action */
.hud-btn.viewer-count {
  background: rgba(255, 177, 74, 0.10);
  border-color: rgba(255, 177, 74, 0.35);
  color: #ffd9a8;
  cursor: default;
  letter-spacing: 0.04em;
}
.hud-btn.viewer-count:hover { background: rgba(255, 177, 74, 0.10); }
.hud-btn.cant  { opacity: 0.45; cursor: not-allowed; }
.hud-btn.warn  { color: var(--bad); border-color: rgba(255, 118, 118, 0.4); }
.hud-btn.armed { background: rgba(255, 118, 118, 0.18); border-color: rgba(255, 118, 118, 0.7); }
.audio-ctl {
  display: inline-flex;
  align-items: center;
  gap: 0;
  margin-left: 6px;
  position: relative;
}
.audio-ctl .hud-btn { margin-left: 0; padding: 4px 8px; }
/* Slider is hidden until the user hovers the audio control or pins it
   open by clicking the icon (toggles .audio-ctl.open). */
.audio-ctl input[type="range"] {
  width: 0;
  margin-left: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;
  accent-color: #f1d27a;
  cursor: pointer;
  transition: width 160ms ease, opacity 120ms ease, margin-left 160ms ease;
}
.audio-ctl:hover input[type="range"],
.audio-ctl.open  input[type="range"] {
  width: 90px;
  margin-left: 6px;
  opacity: 1;
  pointer-events: auto;
}
.resource.kills  { gap: 4px; font-size: 13px; }
.resource.kills .kills-ally  { color: var(--ally); }
.resource.kills .kills-enemy { color: var(--enemy); }
.resource.kills .kills-sep   { color: var(--muted); font-weight: 500; }

.spacer { flex: 1; }

/* Old top-bar .castle / .hpbar styles removed — castle HP now renders
   as a floating world-space bar above each castle mesh (see entities.js
   Castle constructor). One bar per castle, which matters with the
   2v2 / 3v3 multi-castle layout. */

/* ---- Build panel ---- */
#build-panel {
  position: absolute;
  bottom: 12px;
  left: 12px;
  width: calc(50vw - 18px);   /* leaves the right half for the minimap */
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 12px;
  backdrop-filter: blur(8px);
  /* Symmetric padding — the chevron floats top-right and the
     building grid wraps naturally so cards don't slide under it. */
  padding: 10px 12px;
  transition: padding 0.18s ease, max-height 0.18s ease;
}
/* Reserve the top-right corner of the building grid for the
   chevron so the leftmost / wrap-end cards stay clear of it. */
#build-panel > #buildings { padding-right: 38px; }
/* Collapsed: panel becomes a chunky bar with the chevron centered
   and a hint of label space. 36px tall = big enough to click on
   touch screens, never bleeds off the bottom. */
#build-panel.collapsed {
  padding: 4px 12px;
  min-height: 28px;
}
#build-panel.collapsed #buildings,
#build-panel.collapsed #hint { display: none; }

/* Generic panel-collapse chevron — shared by build-panel + minimap.
   Sits ABOVE the panel's outline so it's never clipped, with a wide
   tap target. Auto-flips when the panel is collapsed. */
.panel-collapse {
  position: absolute;
  top: 6px;
  right: 8px;
  width: 32px;
  height: 26px;
  border: 1px solid var(--panel-edge);
  border-radius: 8px;
  background: rgba(0,0,0,0.45);
  color: #e6eaf2;
  font-size: 16px;
  line-height: 22px;
  text-align: center;
  cursor: pointer;
  user-select: none;
  z-index: 25;                /* over the building cards / minimap */
  transition: transform 0.18s ease, background 0.18s ease;
}
.panel-collapse:hover { background: rgba(255,255,255,0.14); }
.collapsed > .panel-collapse,
.collapsed .panel-collapse { transform: rotate(180deg); }
/* When the panel is collapsed, center the chevron in the now-short
   bar so it's the only visible affordance. */
#build-panel.collapsed > .panel-collapse,
#minimap-panel.collapsed > .panel-collapse {
  top: 50%; transform: translateY(-50%) rotate(180deg);
}

/* ---- Minimap panel ---- */
#minimap-panel {
  position: absolute;
  bottom: 12px;
  right: 12px;
  /* Tactical map panel — shrunk from 50vw to 38vw so the playable
     viewport reclaims the screen real estate. The faction roster
     below the canvas still fits comfortably at 38vw. */
  width: calc(38vw - 18px);
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 12px;
  backdrop-filter: blur(8px);
  /* Symmetric padding — chevron sits ABOVE the title row in its
     own absolute slot so the canvas + title get the full width back. */
  padding: 8px 10px;
  display: flex;
  flex-direction: column;
  gap: 5px;
  transition: padding 0.18s ease;
}
#minimap-panel .mm-title { padding-right: 36px; }
#minimap-panel.collapsed {
  padding: 4px 10px;
  min-height: 28px;
}
#minimap-panel.collapsed #minimap,
#minimap-panel.collapsed .mm-legend,
#minimap-panel.collapsed .mm-factions,
#minimap-panel.collapsed .mm-title { display: none; }
#minimap-panel .mm-title {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
}

/* (Old .mm-stats live row removed — superseded by .mm-factions roster
   which shows per-faction gold/kills/strikes below the minimap.) */
#minimap {
  width: 100%;
  /* Aspect-ratio mirrors the playable field (width 208 / depth 68.31
     ≈ 3.05). Keeps the rectangle's proportions matching the actual
     terrain so dot positions don't visually distort. Updated when
     FIELD.width/depth changed; if you re-tune those constants, this
     ratio must be tweaked to match. */
  aspect-ratio: 208 / 68.31;
  border-radius: 6px;
  border: 1px solid var(--panel-edge);
  background: rgba(0, 0, 0, 0.35);
  display: block;
}
.mm-legend {
  display: flex;
  gap: 14px;
  font-size: 10px;
  color: var(--muted);
  letter-spacing: 0.04em;
}
.mm-legend span { display: inline-flex; align-items: center; gap: 4px; }
.mm-legend .mm-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--ally);
}
.mm-legend .mm-dot.enemy { background: var(--enemy); }

/* -------- Per-faction roster under the minimap --------
   Shows every player + AI in the match with their race emblem, name,
   and a strike (☄) status icon. Lets the player see at a glance who
   still has their Rescue Strike in their pocket vs who's burned it. */
#minimap-panel .mm-factions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px 8px;
  margin-top: 8px;
  padding: 6px 8px 4px;
  border-top: 1px solid var(--panel-edge);
}
.mm-fac {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px;
  min-width: 0;
}
/* Per-faction color drives the row's name tint and emblem border so
   you can match a name on the minimap roster to the dots on the
   tactical map at a glance. Falls back to side-based blue/red if a
   faction was created without a color. */
.mm-fac { --fac-color: #ffb14a; }
.mm-fac .mm-fac-name { color: var(--fac-color); font-weight: 700; }
.mm-fac.you   .mm-fac-name { font-weight: 800; }
.mm-fac.ally  { --fac-color: #cfe2ff; }
.mm-fac.enemy { --fac-color: #ffd3d3; }
.mm-fac .mm-fac-emblem { box-shadow: 0 0 0 1px var(--fac-color), 0 0 4px rgba(0,0,0,0.4); }
.mm-fac .mm-fac-emblem {
  width: 18px; height: 18px;
  flex-shrink: 0;
  border-radius: 4px;
}
.mm-fac .mm-fac-name {
  white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
  flex: 1; min-width: 0;
}
.mm-fac .mm-fac-strike {
  width: 14px;
  text-align: center;
  font-size: 11px;
  flex-shrink: 0;
}
/* Live gold per faction — small amber pill on the right of the row so
   you can sanity-check what the AI is hoarding vs what you have. */
.mm-fac .mm-fac-gold {
  font-size: 10px;
  font-weight: 700;
  color: #ffd24a;
  background: rgba(255, 210, 74, 0.10);
  border: 1px solid rgba(255, 210, 74, 0.30);
  border-radius: 4px;
  padding: 0 4px;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
/* Per-faction kill count — small slate pill so it reads as info, not
   as resource. Same fixed-width digits as the gold chip. */
.mm-fac .mm-fac-kills {
  font-size: 10px;
  font-weight: 700;
  color: #d2dcec;
  background: rgba(210, 220, 236, 0.08);
  border: 1px solid rgba(210, 220, 236, 0.20);
  border-radius: 4px;
  padding: 0 4px;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.mm-fac .mm-fac-strike.available { color: #ffb14a; text-shadow: 0 0 6px rgba(255, 177, 74, 0.5); }
.mm-fac .mm-fac-strike.used      { color: rgba(255,255,255,0.18); text-decoration: line-through; }

/* Race emblem next to dropdown */
.race-emblem {
  width: 22px;
  height: 22px;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.4));
  margin-right: 2px;
}

#race-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}
.race-pick { display: flex; align-items: center; gap: 6px; }
.race-pick label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
}
.race-pick select {
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
  border: 1px solid var(--panel-edge);
  border-radius: 6px;
  padding: 4px 6px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.race-vs { color: var(--muted); font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; }

#race-tab {
  display: flex;
  gap: 6px;
  margin-left: auto;
}
.rtab {
  padding: 4px 12px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: transparent;
  border: 1px solid var(--panel-edge);
  border-radius: 6px;
  color: var(--muted);
  cursor: pointer;
}
.rtab.active { background: rgba(90, 169, 255, 0.18); color: var(--ally); border-color: rgba(90, 169, 255, 0.5); }
#view-label { display: none; }

#buildings {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(74px, 1fr));
  gap: 6px;
}

/* Compact card: thumbnail + name + cost. Detail is in .btip on hover. */
.bcard {
  position: relative;
  padding: 6px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--panel-edge);
  border-radius: 8px;
  cursor: pointer;
  transition: transform 80ms ease, border-color 120ms ease, background 120ms ease;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.bcard:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, 0.2); z-index: 5; }
.bcard.selected { border-color: var(--gold); background: rgba(246, 196, 106, 0.16); }
.bcard.cant { opacity: 0.45; cursor: not-allowed; }
.bcard.done { opacity: 0.55; cursor: default; }

.bcard .thumb {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--panel-edge);
  position: relative;
  overflow: hidden;
}
.bcard .thumb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  z-index: 2;
}
/* Procedural placeholder that shows when the per-building PNG is missing.
   Sits behind the <img>; the img covers it once loaded. */
.bcard .thumb .thumb-glyph {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 36px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.5), 0 0 12px rgba(0, 0, 0, 0.3);
  pointer-events: none;
  z-index: 1;
}

.bcard .name {
  font-size: 11px;
  font-weight: 700;
  text-align: center;
  line-height: 1.15;
  width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bcard .cost {
  font-size: 10px;
  color: var(--gold);
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.bcard .cost .lcost { color: var(--lumber); margin-left: 6px; display: inline-flex; align-items: center; gap: 3px; }
.bcard .cost .lp-cost {
  color: #ffd24a; font-weight: 700;
  text-shadow: 0 0 6px rgba(255, 210, 74, 0.55);
}
.bcard .cicon { width: 12px; height: 12px; object-fit: contain; }

/* Hotkey chip — top-left corner of each card. */
.bcard .hotkey {
  position: absolute;
  top: 4px;
  left: 4px;
  z-index: 3;
  font: 700 10px/1 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  letter-spacing: 0.04em;
  color: var(--text);
  background: rgba(0, 0, 0, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 3px;
  padding: 2px 5px;
  pointer-events: none;
}
.bcard.cant .hotkey { opacity: 0.55; }

/* Corner badge — small dot in the thumb corner. Full label is in tooltip. */
.bcard .badge {
  position: absolute;
  top: 4px;
  right: 4px;
  z-index: 2;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 1px 5px;
  border-radius: 3px;
  pointer-events: none;
}
.bcard .badge.special   { background: rgba(108, 209, 255, 0.85); color: #0a1422; }
.bcard .badge.legendary { background: rgba(246, 196, 106, 0.92); color: #2a1808; }
.bcard.done .badge.legendary::after { content: ""; }

/* Hover tooltip card */
.bcard .btip {
  position: absolute;
  /* Default: tooltip sits ABOVE the card, centered horizontally. For
     left-/right-edge cards in the 4-column grid we anchor the
     left/right edges instead so the popup never clips off-screen. */
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  width: 240px;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 8px;
  padding: 8px 10px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--text);
  pointer-events: none;
  opacity: 0;
  transition: opacity 100ms ease;
  z-index: 100;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}
/* Left-edge column (1st in every row of a 4-col grid): anchor the
   tooltip's LEFT edge to the card so it grows rightward instead of
   trying to extend off the left of the screen. */
.bcard:nth-child(4n+1) .btip {
  left: 0;
  transform: translateX(0);
}
/* Right-edge column (4th in every row): anchor the RIGHT edge so the
   tooltip grows leftward, staying inside the panel. */
.bcard:nth-child(4n) .btip {
  left: auto;
  right: 0;
  transform: translateX(0);
}
.bcard:hover .btip { opacity: 1; }
.bcard .btip .btitle { font-weight: 700; font-size: 12px; margin-bottom: 4px; }
.bcard .btip .bdesc  { color: var(--muted); margin-bottom: 4px; }
.bcard .btip .brow   { color: var(--text); }
.bcard .btip .blab   { color: var(--muted); font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; margin-top: 4px; }
.bcard .btip .brow-chips {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin-top: 4px;
}
.bcard .btip .btchip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 10px;
  line-height: 1.5;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.08);
  white-space: nowrap;
}
.bcard .btip .btchipImg {
  width: 14px;
  height: 14px;
  object-fit: contain;
  flex-shrink: 0;
  image-rendering: -webkit-optimize-contrast;
}
.bcard .btip .btchip.dmg { background: rgba(255,120,90,0.16); border-color: rgba(255,120,90,0.4); color: #ffb09a; }
.bcard .btip .btchip.arm { background: rgba(120,160,255,0.14); border-color: rgba(120,160,255,0.4); color: #b8cdff; }
.bcard .btip .btchip.hp  { background: rgba(120,220,140,0.14); border-color: rgba(120,220,140,0.4); color: #b6f0c8; }
.bcard .btip .btchip.tgt { background: rgba(220,180,90,0.14);  border-color: rgba(220,180,90,0.4);  color: #f0d6a0; }

#hint {
  margin-top: 6px;
  font-size: 11px;
  color: var(--muted);
  letter-spacing: 0.04em;
}

/* ---- Banner ---- */
#banner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 600ms ease;
}
#banner .banner-text {
  font-size: 64px;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-shadow: 0 4px 22px rgba(0, 0, 0, 0.6);
}
#banner .banner-restart {
  padding: 10px 20px;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--panel-edge);
  border-radius: 8px;
  cursor: pointer;
  /* Default: NOT clickable. The banner sits dead-centre with the
     button visible only when .show is added; without this gate, the
     invisible button intercepts ordinary clicks during placement and
     restarts the match. */
  pointer-events: none;
}
#banner.show .banner-restart { pointer-events: auto !important; }
#banner .banner-restart:hover { background: rgba(255, 255, 255, 0.08); }
#banner.show { opacity: 1; }
#banner.win  .banner-text { color: var(--good); }
#banner.lose .banner-text { color: var(--bad); }

/* ---- Post-match summary modal ----------------------------------------
   Fullscreen overlay that lists every faction's stats with a big winner
   banner up top. Shown by ui.showMatchSummary() after endGame() fires.
   Hidden by default (.hidden); .show makes it fade in. */
#match-summary {
  position: fixed; inset: 0;
  background: rgba(13, 14, 18, 0.86);
  backdrop-filter: blur(6px);
  z-index: 30;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none;
  transition: opacity 200ms ease-out;
}
#match-summary.show   { opacity: 1; pointer-events: auto; }
#match-summary.hidden { display: none; }

#match-summary .ms-card {
  background: linear-gradient(160deg, rgba(40,42,52,0.95), rgba(20,22,28,0.98));
  border: 1px solid var(--panel-edge);
  border-radius: 14px;
  padding: 24px 28px 20px;
  width: min(880px, calc(100vw - 40px));
  max-height: calc(100vh - 60px);
  overflow-y: auto;
  box-shadow: 0 8px 40px rgba(0,0,0,0.6), 0 0 0 1px rgba(255,255,255,0.03);
}

#match-summary .ms-header {
  text-align: center; padding-bottom: 18px; border-bottom: 1px solid var(--panel-edge);
  margin-bottom: 18px;
}
#match-summary .ms-result {
  font-size: 42px; font-weight: 900; letter-spacing: 0.12em;
  text-shadow: 0 2px 12px rgba(0,0,0,0.7);
}
#match-summary.win  .ms-result { color: #88e09f; text-shadow: 0 0 24px rgba(136, 224, 159, 0.5); }
#match-summary.lose .ms-result { color: #ff7a7a; text-shadow: 0 0 24px rgba(255, 122, 122, 0.5); }
#match-summary .ms-subtitle {
  color: var(--muted); font-size: 13px; margin-top: 6px; letter-spacing: 0.08em;
}

#match-summary .ms-teams {
  display: grid;
  grid-template-columns: 1fr 70px 1fr;
  gap: 14px;
  align-items: stretch;
}
#match-summary .ms-team {
  background: rgba(0,0,0,0.18);
  border: 1px solid var(--panel-edge);
  border-radius: 10px;
  padding: 12px 14px;
}
#match-summary .ms-team.winner {
  border-color: #88e09f;
  box-shadow: 0 0 24px rgba(136, 224, 159, 0.25), inset 0 0 0 1px rgba(136, 224, 159, 0.18);
}
#match-summary .ms-team-name {
  font-size: 14px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text); padding-bottom: 8px; border-bottom: 1px solid var(--panel-edge);
  margin-bottom: 8px;
}
#match-summary .ms-team.winner .ms-team-name { color: #88e09f; }
#match-summary .ms-team.winner .ms-team-name::after {
  content: " · WINNER"; color: #88e09f; font-weight: 900;
}
#match-summary .ms-team-body { display: flex; flex-direction: column; gap: 8px; }
#match-summary .ms-faction {
  display: grid;
  grid-template-columns: 22px 1fr auto;
  gap: 8px;
  align-items: center;
  padding: 8px 10px;
  background: rgba(0,0,0,0.25);
  border-radius: 6px;
  border-left: 3px solid currentColor;
}
#match-summary .ms-faction .ms-emblem { width: 22px; height: 22px; object-fit: contain; filter: drop-shadow(0 0 4px rgba(0,0,0,0.5)); }
#match-summary .ms-faction .ms-name { font-weight: 700; font-size: 13px; }
#match-summary .ms-faction .ms-race { font-size: 11px; color: var(--muted); margin-top: 2px; }
#match-summary .ms-faction .ms-stats {
  display: flex; flex-direction: column; gap: 2px;
  font-size: 11px; line-height: 1.3; text-align: right;
}
#match-summary .ms-faction .ms-stats .ms-kills   { color: #ff9a9a; font-weight: 700; }
#match-summary .ms-faction .ms-stats .ms-gold    { color: var(--gold); }
#match-summary .ms-faction .ms-stats .ms-income  { color: var(--muted); }

#match-summary .ms-vs {
  display: flex; align-items: center; justify-content: center;
  font-size: 24px; font-weight: 800; color: var(--muted); letter-spacing: 0.1em;
}

#match-summary .ms-footer {
  margin-top: 18px;
  display: flex; align-items: center; justify-content: center;
  gap: 12px;
  padding-top: 14px;
  border-top: 1px solid var(--panel-edge);
}
#match-summary .ms-duration {
  flex: 1; font-size: 12px; color: var(--muted); letter-spacing: 0.08em;
}
#match-summary .ms-btn {
  background: var(--panel); color: var(--text); border: 1px solid var(--panel-edge);
  border-radius: 8px; padding: 8px 18px; font-size: 13px; cursor: pointer;
  font-weight: 700;
}
#match-summary .ms-btn:hover { border-color: var(--text); }
#match-summary .ms-btn.primary { background: #2a6f4a; border-color: #3a9b6a; color: #fff; }
#match-summary .ms-btn.primary:hover { background: #3a9b6a; }

/* ---- Start overlay (pre-match) — centred along the top so it doesn't
   overlap side panels (inspector / research). ---- */
#start-overlay {
  position: absolute;
  top: 80px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 13;
  pointer-events: auto;
  /* Constrain to viewport so the Start button stays reachable on
     short screens (mobile, landscape iPhones). The card itself
     becomes scrollable when its content exceeds the available
     height — overflow-y: auto + -webkit-overflow-scrolling: touch
     enables native rubber-band scrolling. */
  max-height: calc(100vh - 96px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
#start-overlay.hidden { display: none; }

/* Hide the touch D-pad / zoom controls while the start overlay is
   still on screen — the panel was overlapping the picker grid on
   phones. Sibling selector works because #start-overlay and
   #touch-pan are both direct children of #hud. */
#start-overlay:not(.hidden) ~ #touch-pan { display: none !important; }

/* Spectator mode — hide all race / gold / team-size pickers + the
   start button. Show only the spinner + "Joining match…" until the
   game auto-starts and the overlay disappears entirely. */
.spectator-loading { display: none; }
body.spectator-mode #start-overlay .start-pickers,
body.spectator-mode #start-overlay #start-btn,
body.spectator-mode #start-overlay .start-hint,
body.mp-mode #start-overlay .start-pickers,
body.mp-mode #start-overlay #start-btn,
body.mp-mode #start-overlay .start-hint { display: none !important; }
body.spectator-mode #start-overlay .spectator-loading,
body.mp-mode        #start-overlay .spectator-loading {
  display: block;
  padding: 18px 8px 6px;
}
.spectator-loading .spec-spinner {
  width: 36px; height: 36px;
  border-radius: 50%;
  margin: 0 auto 12px;
  border: 3px solid rgba(255, 177, 74, 0.2);
  border-top-color: #ffb14a;
  animation: spec-spin 800ms linear infinite;
}
.spectator-loading .spec-label { color: var(--text); font-weight: 700; font-size: 14px; }
.spectator-loading .spec-sub   { color: var(--muted); font-size: 11px; margin-top: 4px; letter-spacing: 0.06em; text-transform: uppercase; }
@keyframes spec-spin { to { transform: rotate(360deg); } }
#start-overlay .start-card {
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 10px;
  padding: 14px 18px;
  backdrop-filter: blur(8px);
  text-align: center;
  width: 360px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
  position: relative;
}
#start-overlay .start-logo {
  display: block;
  width: 100%;
  height: auto;
  margin: 0 auto 6px;
  filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.55));
}
#start-overlay .start-beta {
  position: absolute;
  top: 12px;
  right: 14px;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.18em;
  color: #2a1808;
  background: var(--gold);
  padding: 2px 8px;
  border-radius: 4px;
  transform: rotate(8deg);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
}
#start-overlay .start-title {
  font-size: 18px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--gold);
  margin-bottom: 4px;
}
#start-overlay .start-sub {
  font-size: 12px;
  color: var(--muted);
  margin-bottom: 12px;
  line-height: 1.4;
}
#start-overlay #start-btn {
  width: 100%;
  padding: 10px 14px;
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: linear-gradient(180deg, #6cd28a, #4cc06f);
  color: #082014;
  border: 1px solid rgba(0, 0, 0, 0.25);
  border-radius: 8px;
  cursor: pointer;
  transition: filter 100ms ease, transform 100ms ease;
}
#start-overlay #start-btn:hover { filter: brightness(1.08); transform: translateY(-1px); }
#start-overlay #start-btn:disabled {
  cursor: progress;
  transform: none;
  filter: none;
  background: linear-gradient(180deg, #4a6a5a, #2e4438);
  color: rgba(255, 255, 255, 0.75);
}
/* Loading-state progress bar that lives inside the Start button. */
#start-overlay .start-bar-label {
  display: block;
  font-size: 12px;
  letter-spacing: 0.06em;
  margin-bottom: 6px;
}
#start-overlay .start-bar-track {
  display: block;
  height: 8px;
  background: rgba(0, 0, 0, 0.35);
  border-radius: 4px;
  overflow: hidden;
}
#start-overlay .start-bar-fill {
  display: block;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, #f6c46a, #ffe184);
  transition: width 180ms ease;
}
#start-overlay .start-bar-pct {
  display: block;
  margin-top: 4px;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
  color: rgba(255, 255, 255, 0.85);
}
#start-overlay .start-hint {
  margin-top: 8px;
  font-size: 10px;
  color: var(--muted);
  letter-spacing: 0.04em;
  line-height: 1.4;
}

/* Pre-match pickers — race + starting resources */
#start-overlay .picker-section {
  margin: 12px 0 8px;
  text-align: left;
}
#start-overlay .picker-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}
#start-overlay .race-picker {
  display: grid;
  /* 5 real races. Random Mix moved out to its own Game Mode row. */
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
}
/* When Random Mix is the active game mode the race picker stays
   visible but greyed out + non-clickable, so the player can still
   see (and snap back to) the race cards they're skipping. */
#start-overlay .picker-section.muted .picker-label { opacity: 0.5; }
#start-overlay .race-picker.muted {
  opacity: 0.42;
  filter: saturate(0.5);
  pointer-events: auto;
}
#start-overlay .race-picker.muted .race-card.selected {
  border-color: var(--panel-edge);
  background: rgba(255, 255, 255, 0.04);
  box-shadow: none;
}

/* Game mode picker — sits ABOVE the race picker, two wide cards
   today (Standard / Random Mix); add more columns as new modes are
   introduced. */
#start-overlay .game-mode-picker {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
}
#start-overlay .gm-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 10px 8px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--panel-edge);
  border-radius: 8px;
  cursor: pointer;
  transition: transform 80ms ease, border-color 100ms ease, background 100ms ease;
}
#start-overlay .gm-card:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, 0.25); }
#start-overlay .gm-card.selected {
  border-color: var(--gold);
  background: rgba(246, 196, 106, 0.14);
  box-shadow: 0 0 0 1px var(--gold) inset;
}
#start-overlay .gm-card .gm-name {
  font-size: 14px;
  font-weight: 800;
  color: var(--gold);
  letter-spacing: 0.04em;
}
#start-overlay .gm-card .gm-sub {
  font-size: 10px;
  color: var(--muted);
  letter-spacing: 0.04em;
}
/* Random Mix variant — purple-tinted to keep its existing visual
   identity now that it's no longer in the race grid. */
#start-overlay .gm-card.random {
  background: linear-gradient(160deg, rgba(140, 80, 200, 0.22), rgba(80, 40, 130, 0.22));
  border-color: rgba(180, 120, 240, 0.45);
}
#start-overlay .gm-card.random:hover { border-color: rgba(220, 160, 255, 0.85); }
#start-overlay .gm-card.random.selected {
  border-color: #c890ff;
  background: linear-gradient(160deg, rgba(180, 120, 240, 0.35), rgba(120, 60, 180, 0.35));
  box-shadow: 0 0 0 1px #c890ff inset;
}
#start-overlay .gm-card.random .gm-name { color: #e0c0ff; }
#start-overlay .race-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 4px 6px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--panel-edge);
  border-radius: 8px;
  cursor: pointer;
  transition: transform 80ms ease, border-color 100ms ease, background 100ms ease;
}
#start-overlay .race-card:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, 0.25); }
#start-overlay .race-card.selected {
  border-color: var(--gold);
  background: rgba(246, 196, 106, 0.14);
  box-shadow: 0 0 0 1px var(--gold) inset;
}
/* "Random Mix" card — purple-tinted to stand out from real races. */
#start-overlay .race-card.random {
  background: linear-gradient(160deg, rgba(140, 80, 200, 0.22), rgba(80, 40, 130, 0.22));
  border-color: rgba(180, 120, 240, 0.45);
}
#start-overlay .race-card.random:hover { border-color: rgba(220, 160, 255, 0.85); }
#start-overlay .race-card.random.selected {
  border-color: #c890ff;
  background: linear-gradient(160deg, rgba(180, 120, 240, 0.35), rgba(120, 60, 180, 0.35));
  box-shadow: 0 0 0 1px #c890ff inset;
}
#start-overlay .race-card.random .race-card-name { color: #e0c0ff; font-weight: 700; }
#start-overlay .race-card img { width: 38px; height: 38px; }
#start-overlay .race-card .race-card-name {
  font-size: 11px;
  font-weight: 700;
  text-align: center;
  line-height: 1.15;
  letter-spacing: 0.02em;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 60px;
}
#start-overlay .gold-picker {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
}
#start-overlay .gold-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 6px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--panel-edge);
  border-radius: 8px;
  cursor: pointer;
  transition: transform 80ms ease, border-color 100ms ease, background 100ms ease;
}
#start-overlay .gold-card:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, 0.25); }
#start-overlay .gold-card.selected {
  border-color: var(--gold);
  background: rgba(246, 196, 106, 0.14);
  box-shadow: 0 0 0 1px var(--gold) inset;
}
#start-overlay .gold-card .gold-amount {
  font-size: 16px;
  font-weight: 800;
  color: var(--gold);
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}
#start-overlay .gold-card .gold-sub {
  font-size: 10px;
  color: var(--muted);
  letter-spacing: 0.04em;
}

/* Team-size picker — 1v1 / 2v2 / 3v3 */
#start-overlay .team-size-picker {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
}
#start-overlay .ts-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 8px 6px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--panel-edge);
  border-radius: 8px;
  cursor: pointer;
  transition: transform 80ms ease, border-color 100ms ease, background 100ms ease;
}
#start-overlay .ts-card:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, 0.25); }
#start-overlay .ts-card.selected {
  border-color: var(--gold);
  background: rgba(246, 196, 106, 0.14);
  box-shadow: 0 0 0 1px var(--gold) inset;
}
#start-overlay .ts-card .ts-label {
  font-size: 15px;
  font-weight: 800;
  color: var(--gold);
  letter-spacing: 0.04em;
}
#start-overlay .ts-card .ts-sub {
  font-size: 9px;
  color: var(--muted);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Build-panel race status (replaces the old dropdowns). */
.race-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.04em;
}
.race-status .race-vs {
  margin: 0 4px;
  color: var(--muted);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.race-status #race-name-you,
.race-status #race-name-enemy { color: var(--text); font-weight: 700; }

/* ---- In-game command console — centered, fades out on close ---- */
#console-overlay {
  position: fixed;
  top: 50%;
  left: 50%;
  width: min(620px, 80vw);
  z-index: 80;
  background: rgba(8, 10, 16, 0.85);
  border: 1px solid rgba(246, 196, 106, 0.75);
  border-radius: 12px;
  padding: 10px 12px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
  font: 600 13px/1.4 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  color: var(--text);
  opacity: 1;
  transform: translate(-50%, -50%);
  transition: opacity 320ms ease, transform 320ms ease;
}
#console-overlay.fading {
  opacity: 0;
  transform: translate(-50%, calc(-50% + 6px));
  pointer-events: none;
}
#console-overlay.hidden { display: none; }
#console-overlay .cn-out {
  max-height: 180px;
  overflow-y: auto;
  margin-bottom: 6px;
  padding: 4px 4px;
  background: rgba(0, 0, 0, 0.30);
  border-radius: 6px;
  font-size: 11px;
}
#console-overlay .cn-out:empty { display: none; }
/* Output lines fade individually so old responses drift away while
   the console is still open. */
#console-overlay .cn-line {
  white-space: pre-wrap;
  padding: 1px 4px;
  opacity: 1;
  transition: opacity 1200ms ease;
}
#console-overlay .cn-line.faded { opacity: 0.25; }
#console-overlay .cn-line.err  { color: var(--bad); }
#console-overlay .cn-line.echo { color: var(--gold); }
#console-overlay .cn-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
#console-overlay .cn-prompt { color: var(--gold); font-weight: 800; }
#console-overlay #cn-input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text);
  font: inherit;
  letter-spacing: 0.02em;
  caret-color: var(--gold);
}

/* ---- Custom confirm modal ---- */
#confirm-modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
}
#confirm-modal.hidden { display: none; }
#confirm-modal .cm-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(8, 10, 16, 0.62);
  backdrop-filter: blur(4px);
}
#confirm-modal .cm-card {
  position: relative;
  width: 360px;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 12px;
  padding: 20px 22px 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.65);
  text-align: left;
  animation: cm-in 160ms ease-out;
}
@keyframes cm-in {
  from { opacity: 0; transform: translateY(6px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0)  scale(1); }
}
#confirm-modal .cm-title {
  font-size: 16px;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--gold);
  margin-bottom: 4px;
}
#confirm-modal .cm-msg {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.5;
  margin-bottom: 16px;
}
#confirm-modal .cm-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}
#confirm-modal .cm-btn {
  padding: 8px 16px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: 6px;
  cursor: pointer;
  border: 1px solid var(--panel-edge);
  transition: filter 100ms ease, transform 80ms ease;
}
#confirm-modal .cm-btn:hover { filter: brightness(1.12); transform: translateY(-1px); }
#confirm-modal .cm-btn-ghost {
  background: rgba(255, 255, 255, 0.04);
  color: var(--text);
}
#confirm-modal .cm-btn-warn {
  background: linear-gradient(180deg, #ff7676, #ff3b3b);
  color: #1a0a0a;
  border: 1px solid rgba(0, 0, 0, 0.25);
}

/* ---- Restart icon button — top-right of the viewport, well clear of the play area. ---- */
#restart-btn {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(20, 22, 30, 0.55);
  border: 1px solid var(--panel-edge);
  color: var(--muted);
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  z-index: 12;
  opacity: 0.6;
}
#restart-btn:hover { opacity: 1; color: var(--text); background: rgba(255, 118, 118, 0.18); border-color: rgba(255, 118, 118, 0.55); }

/* Touch device pan/zoom controls. Hidden until body.touch-mode is
   set (first touchstart in main.js). Sits bottom-left in a small
   D-pad grid with zoom buttons stacked on the right. Buttons fire
   on touchstart and on touchend release — held buttons feed a
   virtual key into the camera so panning is continuous. */
#touch-pan {
  display: none;
  position: absolute;
  left: 12px;
  bottom: 12px;
  z-index: 14;
  width: 168px;
  height: 112px;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  touch-action: none;
}
body.touch-mode #touch-pan { display: block; }
#touch-pan .tp-btn {
  position: absolute;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid rgba(255, 220, 140, 0.45);
  background: rgba(20, 22, 30, 0.55);
  color: rgba(255, 235, 180, 0.95);
  font-size: 18px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 60ms ease, background 80ms ease;
}
#touch-pan .tp-btn:active,
#touch-pan .tp-btn.held {
  background: rgba(246, 196, 106, 0.30);
  transform: scale(0.93);
}
/* D-pad layout: cross at left, zoom column at right. */
#touch-pan .tp-left  { left: 0;   top: 34px; }
#touch-pan .tp-right { left: 68px; top: 34px; }
#touch-pan .tp-up    { left: 34px; top: 0;    }
#touch-pan .tp-down  { left: 34px; top: 68px; }
#touch-pan .tp-zin   { left: 124px; top: 12px; }
#touch-pan .tp-zout  { left: 124px; top: 60px; }

#fps {
  position: absolute;
  top: 16px;
  right: 56px;
  font: 600 11px/1 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  color: var(--muted);
  background: rgba(20, 22, 30, 0.55);
  padding: 4px 8px;
  border-radius: 6px;
  border: 1px solid var(--panel-edge);
  pointer-events: none;
  z-index: 11;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}
#fps.warn { color: #f6c46a; }
#fps.bad  { color: #ff7676; }

/* ---- Research panel ----
   Positioned per-click to sit DIRECTLY ABOVE the research floater
   icon (which itself follows the player's castle in 3D space). The
   anchor logic lives in src/main.js (researchFloater click handler);
   default left/top are fallbacks for when the floater isn't visible
   yet — they place the panel in a sensible top-left slot. */
#research-panel {
  position: absolute;
  top: 80px;
  left: 16px;
  width: 320px;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 10px;
  padding: 14px 16px 12px;
  backdrop-filter: blur(8px);
  font-size: 13px;
  line-height: 1.45;
  /* z-index above the floater so the panel covers it cleanly. */
  z-index: 13;
  /* Use a transform-anchor so we can position by "bottom-center"
     above the floater (set inline). */
  transform-origin: bottom center;
}
#research-panel.hidden { display: none; }
#research-panel .rclose {
  position: absolute;
  top: 6px;
  right: 10px;
  font-size: 18px;
  color: var(--muted);
  cursor: pointer;
}
#research-panel .rclose:hover { color: var(--text); }
#research-panel h3 {
  margin: 0 0 4px;
  font-size: 14px;
  letter-spacing: 0.04em;
}
#research-panel .rsub { margin: 0 0 10px; color: var(--muted); font-size: 11px; }
#research-list { display: flex; flex-direction: column; gap: 6px; }
.rcard {
  padding: 8px 10px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--panel-edge);
  border-radius: 8px;
  cursor: pointer;
  transition: border-color 100ms, background 100ms;
}
.rcard:hover { border-color: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.07); }
.rcard.cant { opacity: 0.45; cursor: not-allowed; }
.rcard.done { border-color: rgba(108, 210, 138, 0.6); background: rgba(108, 210, 138, 0.08); cursor: default; }
.rcard .rname { font-weight: 700; font-size: 13px; }
.rcard .rdesc { font-size: 11px; color: var(--muted); margin-top: 2px; }
.rcard .rcost { margin-top: 4px; font-size: 11px; color: var(--lumber); }
.rcard.done .rcost::before { content: "✔ Researched"; color: var(--good); }
.rcard.done .rcost { color: transparent; }

/* ---- Inspector ---- */
#inspector {
  position: absolute;
  top: 80px;
  right: 16px;
  width: 240px;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 10px;
  padding: 12px 14px 10px;
  backdrop-filter: blur(8px);
  font-size: 13px;
  line-height: 1.45;
}
#inspector.hidden { display: none; }
#inspector .iclose {
  position: absolute;
  top: 6px;
  right: 10px;
  font-size: 18px;
  color: var(--muted);
  cursor: pointer;
  line-height: 1;
}
#inspector .iclose:hover { color: var(--text); }
/* Unit/building/castle icon — floats LEFT and the name + HP-bar
   block flows around it. Previous absolute-top-right placement
   landed on top of the HP-bar total text. Hidden via ui.js
   onerror when the asset 404s. */
#inspector .iicon {
  float: left;
  margin: 0 10px 6px 0;
  width: 56px;
  height: 56px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
  object-fit: contain;
  padding: 2px;
  pointer-events: none;
}
#inspector .iname {
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.02em;
  padding-right: 24px; /* clear the close × in the corner */
}
#inspector .iteam {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  margin-top: 2px;
  margin-bottom: 6px;
}
#inspector .iteam.ally  { color: var(--ally); }
#inspector .iteam.enemy { color: var(--enemy); }
#inspector .istats {
  margin-bottom: 6px;
}
#inspector .ihprow {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}
#inspector .ihpbar {
  flex: 1;
  height: 9px;
  background: rgba(0, 0, 0, 0.45);
  border: 1px solid var(--panel-edge);
  border-radius: 5px;
  overflow: hidden;
}
#inspector .ihpfill {
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, #88e09f, #4cc06f);
  transition: width 200ms ease, background 200ms ease;
}
#inspector .ihpfill.warn { background: linear-gradient(90deg, #f6c46a, #f0a838); }
#inspector .ihpfill.danger { background: linear-gradient(90deg, #ff7676, #ff3b3b); }
#inspector .ihptext {
  font-size: 11px;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
#inspector .iarmor {
  font-size: 11px;
  color: var(--muted);
}
#inspector .iarmor b { color: var(--text); font-weight: 600; }
#inspector .ichipRow {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 2px;
}
#inspector .ichip {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--panel-edge);
  border-radius: 6px;
  cursor: help;
}
#inspector .ichip:hover { background: rgba(255, 255, 255, 0.07); }

/* Effectiveness popup — floats OUTSIDE the inspector to the left so it
   never overlaps the unit's stats block. Solid background + heavy drop
   shadow so it reads as an actual popup, not bleed-through. */
#inspector .ichipPop {
  position: absolute;
  right: calc(100% + 12px);
  top: 0;
  width: 220px;
  padding: 10px 12px;
  background: #181b25;             /* solid, no transparency */
  border: 1px solid #f6c46a;       /* gold accent so it stands out */
  border-radius: 8px;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.65);
  pointer-events: none;
  opacity: 0;
  transform: translateX(8px);
  transition: opacity 120ms ease, transform 120ms ease;
  z-index: 100;
}
#inspector .ichipPop::after {
  /* Caret pointing right INTO the chip */
  content: "";
  position: absolute;
  right: -6px;
  top: 18px;
  border: 6px solid transparent;
  border-left-color: #f6c46a;
}
#inspector .ichip:hover .ichipPop,
#inspector .ichip.pinned .ichipPop {
  opacity: 1;
  transform: translateX(0);
}
#inspector .ichip.pinned {
  border-color: var(--gold);
  background: rgba(246, 196, 106, 0.10);
}
#inspector .ichipPop .ipoph {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin-bottom: 6px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--panel-edge);
}
#inspector .ichipPop .ipoprow {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  padding: 3px 0;
}
#inspector .ichipPop .ipoprow img {
  width: 18px;
  height: 18px;
  object-fit: contain;
  flex-shrink: 0;
}
#inspector .ichipPop .ipoprow .ipoplab {
  flex: 1;
  color: var(--text);
}
#inspector .ichipPop .ipoprow .ipopval {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
#inspector .ichipPop .ipoprow.good .ipopval    { color: var(--good); }
#inspector .ichipPop .ipoprow.bad  .ipopval    { color: var(--bad); }
#inspector .ichipPop .ipoprow.neutral .ipopval { color: var(--muted); }
#inspector .ichipImg {
  width: 32px;
  height: 32px;
  object-fit: contain;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
  flex-shrink: 0;
}
#inspector .ichipBody { flex: 1; min-width: 0; }
#inspector .ichipLab {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  line-height: 1;
}
#inspector .ichipVal {
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.2;
  margin-top: 2px;
}
/* Sub-line beneath the damage-type label — shows raw dmg + range +
   bonus procs (cleave / splash / stun / slow / poison / anti-air).
   Compact, dimmer than the main val so the eye still lands on the
   damage type icon first. */
#inspector .ichipSub {
  font-size: 11px;
  color: var(--muted);
  line-height: 1.25;
  margin-top: 2px;
  white-space: normal;
}
#inspector .ichipSub b {
  color: var(--text);
  font-weight: 700;
}
#inspector .ichipSub.bonus {
  color: #ffb87a;
  font-weight: 600;
  margin-top: 1px;
}

#inspector .iaction {
  margin-top: 8px;
  width: 100%;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  background: rgba(246, 196, 106, 0.16);
  color: var(--gold);
  border: 1px solid rgba(246, 196, 106, 0.55);
  border-radius: 6px;
  cursor: pointer;
}
#inspector .iaction:hover { background: rgba(246, 196, 106, 0.28); }
#inspector .iaction.cant  { opacity: 0.45; cursor: not-allowed; }
#inspector .iaction.done  { background: transparent; color: var(--good); border-color: rgba(108, 210, 138, 0.55); cursor: default; }

/* Demolish button — destructive, separate styling from gold-tinted upgrades. */
#inspector .iaction.idemo {
  margin-top: 10px;
  background: rgba(255, 118, 118, 0.10);
  color: var(--bad);
  border-color: rgba(255, 118, 118, 0.45);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
#inspector .iaction.idemo:hover { background: rgba(255, 118, 118, 0.22); }
#inspector .iaction.idemo .demoRefund {
  font-size: 11px;
  font-weight: 600;
  color: var(--gold);
}

#inspector .iactRow {
  display: grid;
  grid-template-columns: 1fr;
  gap: 6px;
  margin-top: 8px;
}
#inspector .iaction.towerUp {
  text-align: left;
  padding: 6px 10px;
  background: rgba(255, 255, 255, 0.03);
  border-width: 1px;
  border-style: solid;
}
#inspector .iaction.towerUp:hover { background: rgba(255, 255, 255, 0.08); }
#inspector .iaction.towerUp.cant  { opacity: 0.45; cursor: not-allowed; }
#inspector .iaction.towerUp .upName { font-size: 12px; font-weight: 700; color: var(--text); letter-spacing: 0.04em; }
#inspector .iaction.towerUp .upDesc { font-size: 11px; color: var(--muted); margin-top: 2px; line-height: 1.35; }
#inspector .iaction.towerUp .upCost { font-size: 11px; color: var(--gold); margin-top: 4px; }
#inspector .iqueue {
  font-size: 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--panel-edge);
  border-radius: 6px;
  padding: 6px 8px;
  margin-bottom: 6px;
}
#inspector .iqueue .qlab {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 10px;
  color: var(--muted);
  margin-bottom: 2px;
}
#inspector .iqueue .qprog {
  height: 4px;
  background: rgba(0, 0, 0, 0.45);
  border-radius: 2px;
  overflow: hidden;
  margin-top: 4px;
}
#inspector .iqueue .qprog > div {
  height: 100%;
  background: var(--gold);
  width: 0%;
  transition: width 80ms linear;
}
#inspector .iqueue .qprog.mana > div {
  background: linear-gradient(90deg, #6cd1ff, #2f8cff);
}
#inspector .iqueue .qmeta {
  margin-top: 4px;
  font-size: 11px;
  color: var(--text);
}
#inspector .iqueue .qmeta.dim { color: var(--muted); }

/* Buff-aware stat rendering. qbase = the original number (struck
   through, dim) shown next to qbuff = the effective number after
   active buffs (highlighted green). Used inline in damage / dps /
   regen lines whenever a unit is currently being buffed. */
#inspector .iqueue .qbase {
  text-decoration: line-through;
  color: var(--muted);
  margin-right: 4px;
  opacity: 0.7;
}
#inspector .iqueue .qbuff {
  color: #88e09f;
  font-weight: 700;
  text-shadow: 0 0 6px rgba(136, 224, 159, 0.35);
}

/* Buff / debuff tag list (pill chips below the stats block). Green
   for positive auras (champion / legendary buffs), red for crowd
   control (stun, slow). Hover for the source name via the `title`
   attribute. */
#inspector .iqueue .qbuffs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 6px;
}
#inspector .iqueue .qbufftag {
  font-size: 10px;
  font-weight: 700;
  padding: 2px 7px 2px 4px;
  border-radius: 999px;
  letter-spacing: 0.02em;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
#inspector .iqueue .qbufftag .qbufficon {
  width: 14px;
  height: 14px;
  object-fit: contain;
  flex-shrink: 0;
  filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.35));
}
#inspector .iqueue .qbufftag.qbuff-good {
  background: rgba(136, 224, 159, 0.16);
  color: #88e09f;
  border: 1px solid rgba(136, 224, 159, 0.4);
}
#inspector .iqueue .qbufftag.qbuff-bad {
  background: rgba(255, 122, 122, 0.16);
  color: #ff9aa0;
  border: 1px solid rgba(255, 122, 122, 0.4);
}
#inspector .idesc {
  font-size: 11px;
  color: var(--muted);
}

/* ---- Help ---- */
/* #help-toggle now lives inside the top-bar and inherits .hud-btn
   styling. The content panel anchors below the top-bar on the right. */
#help {
  position: absolute;
  top: 56px;
  right: 12px;
  z-index: 11;
}
#help-content {
  width: 360px;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: 10px;
  padding: 12px 14px 14px;
  display: none;
  font-size: 12px;
  line-height: 1.5;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(8px);
  max-height: 80vh;
  overflow-y: auto;
}
#help-content.open { display: block; }
#help-content .help-h {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 12px 0 4px;
  padding-bottom: 2px;
  border-bottom: 1px solid var(--panel-edge);
}
#help-content .help-h:first-child { margin-top: 0; }
#help-content p, #help-content ul { margin: 4px 0; color: var(--muted); }
#help-content ul { padding-left: 18px; }
#help-content li { margin: 3px 0; }
#help-content b, #help-content code { color: var(--text); font-weight: 700; }
#help-content code {
  font: 600 11px/1 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  background: rgba(255, 255, 255, 0.06);
  padding: 1px 5px;
  border-radius: 3px;
}
#help-content a { color: var(--gold); text-decoration: none; }
#help-content a:hover { text-decoration: underline; }

/* ---- Sponsored by FacePrivacy.ai (top strip + footer card) -------
   Top strip is a thin clickable bar above the hero. Footer card is
   a richer pull-out at the bottom with logo + tagline. Both carry
   utm_source=castlefights so referrals can be tracked.            */
.sponsor-strip {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 6px 12px;
  background: linear-gradient(90deg, rgba(90, 169, 255, 0.10), rgba(122, 90, 255, 0.10));
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  color: #d6e0f0;
  font-size: 12px;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.18s ease;
}
.sponsor-strip:hover { background: linear-gradient(90deg, rgba(90, 169, 255, 0.18), rgba(122, 90, 255, 0.18)); }
.sponsor-strip-label { color: var(--muted); letter-spacing: 0.04em; }
.sponsor-strip-logo { height: 18px; vertical-align: middle; opacity: 0.95; }
.sponsor-strip-fallback {
  font-weight: 700; color: #88baff; letter-spacing: 0.02em;
}
.sponsor-strip-arrow { color: #88baff; opacity: 0.85; }

.sponsor-footer {
  margin: 48px auto 28px;
  padding: 0 16px;
  max-width: 980px;
}
.sponsor-footer-card {
  display: flex;
  align-items: center;
  gap: 16px;
  background: linear-gradient(135deg, rgba(90, 169, 255, 0.10), rgba(160, 90, 255, 0.10));
  border: 1px solid rgba(122, 180, 255, 0.30);
  border-radius: 12px;
  padding: 14px 18px;
  color: #e4ebf6;
  text-decoration: none;
  transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.sponsor-footer-card:hover {
  transform: translateY(-1px);
  border-color: rgba(122, 180, 255, 0.55);
  background: linear-gradient(135deg, rgba(90, 169, 255, 0.18), rgba(160, 90, 255, 0.18));
}
.sponsor-footer-logo { width: 56px; height: 56px; object-fit: contain; border-radius: 8px; flex: 0 0 auto; }
.sponsor-footer-body { flex: 1; min-width: 0; }
.sponsor-footer-eyebrow {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.sponsor-footer-title {
  font-size: 18px;
  font-weight: 700;
  color: #c8dcff;
  margin-top: 2px;
}
.sponsor-footer-sub {
  font-size: 12px;
  color: var(--muted);
  margin-top: 4px;
  line-height: 1.4;
}
.sponsor-footer-cta {
  padding: 8px 14px;
  border-radius: 8px;
  background: rgba(122, 180, 255, 0.20);
  border: 1px solid rgba(122, 180, 255, 0.45);
  color: #c8dcff;
  font-weight: 700;
  font-size: 13px;
  flex: 0 0 auto;
  white-space: nowrap;
}

/* Small unit portrait shown on spawn-building cards (lower-right of
   the thumb) so players see at a glance which unit each building
   produces. Hidden gracefully if the portrait file is missing. */
.bcard .thumb { position: relative; }
.bcard-unit {
  position: absolute;
  right: 4px;
  bottom: 4px;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.65);
  border: 1.5px solid rgba(255, 255, 255, 0.55);
  object-fit: cover;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.55);
}

/* Tooltip 'Trains X' line: tiny round portrait of the unit next to
   the text. Kept small (24px) so it docks inline without breaking
   the row's height. Falls back gracefully via onerror in JS. */
.btchip-unit-icon {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.30);
  object-fit: cover;
  vertical-align: middle;
  margin-right: 6px;
}
/* Remove the old build-card portrait badge styling — no longer used. */
.bcard-unit { display: none !important; }

/* ---- World-anchored research icon (above-left of player castle) --
   Half-sized (28×28) so it doesn't dominate the field, and z-index
   kept LOW so it never covers the top-bar / build-panel / minimap
   (which live at z-index: 10+). */
.hud-floater {
  position: absolute;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, rgba(255, 220, 130, 0.95), rgba(180, 140, 60, 0.95));
  color: #2a1f0a;
  border: 1.5px solid rgba(60, 38, 12, 0.85);
  font-size: 15px;
  line-height: 23px;
  text-align: center;
  cursor: pointer;
  z-index: 5;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
  transform: translate(-50%, -50%);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  padding: 0;
}
.hud-floater:hover {
  transform: translate(-50%, -50%) scale(1.15);
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.55), 0 0 0 3px rgba(255, 220, 130, 0.20);
}

/* Starting-resources picker — proper icon images instead of emoji.
   Sized to match the surrounding text height; pulled down a hair
   so the icon baseline aligns with the digits. */
.gold-card .gold-icon {
  width: 18px; height: 18px; vertical-align: -3px; margin-right: 4px;
}
.gold-card .lumber-icon {
  width: 14px; height: 14px; vertical-align: -2px; margin-right: 3px;
}

/* Picker label modifier — small parenthetical hint (e.g. "(random)")
   next to the section heading. */
.picker-label .picker-sub {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--gold);
  text-transform: uppercase;
  margin-left: 6px;
  opacity: 0.85;
}
.picker-hint {
  font-size: 10px;
  color: var(--muted);
  margin-top: 6px;
  font-style: italic;
}

/* ---- On-screen chat feed (CS-style) -----------------------------
   Top-left under the HUD top-bar. Auto-fades 8s after the LAST chat
   activity; new chat re-lights every visible line, then they fade
   together. Never blocks gameplay clicks. Position: fixed so it
   anchors directly to the viewport — doesn't depend on the #hud
   layout (which used to swallow it in some boot orders). */
#chat-feed {
  position: fixed;
  /* Top-bar bottom sits at ~72px (top:12 + height:60). 90px gives a
     comfortable gutter and stays clearly below the bar even on
     narrower screens where the bar wraps slightly. */
  top: 90px;
  left: 14px;
  width: min(460px, 38vw);
  z-index: 9999;              /* on top of every HUD layer */
  pointer-events: none;       /* feed never blocks gameplay clicks */
  display: flex;
  flex-direction: column;
  gap: 4px;
  font: 700 14px/1.35 ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
#chat-feed .cf-line {
  padding: 5px 10px;
  border-radius: 7px;
  background: rgba(8, 10, 16, 0.78);
  border-left: 4px solid rgba(255, 255, 255, 0.45);
  color: #f4f7fb;
  opacity: 1;
  /* 1.2s transition lets the fade-out feel smooth, not a snap-cut. */
  transition: opacity 1.2s ease;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.85), 0 0 4px rgba(0, 0, 0, 0.6);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
  max-width: 100%;
  overflow-wrap: anywhere;
  letter-spacing: 0.2px;
}
#chat-feed .cf-line.cf-faded { opacity: 0; }
/* Tone colours for the left edge — matches the existing console tones. */
#chat-feed .cf-line.cf-tone-team { border-left-color: var(--ally); }
#chat-feed .cf-line.cf-tone-warn { border-left-color: var(--gold); }
#chat-feed .cf-line.cf-tone-info { border-left-color: rgba(255, 255, 255, 0.5); }
#chat-feed .cf-line.cf-tone-err  { border-left-color: var(--bad); color: var(--bad); }

/* ===================================================================
   Mobile (≤740px) — phones in portrait + small phones in landscape.
   Goals: make the build menu actually usable on iPhone (was 50vw =
   ~195px which can't fit 12 cards), shrink the minimap so the build
   menu has room, drop the helper hint to save vertical space.
   The 3D canvas + HUD top-bar already scale naturally; only the build
   + minimap panels need explicit shrinking.
   =================================================================== */
@media (max-width: 740px) {
  /* Minimap moves to a small chip in the bottom-right; the build panel
     takes everything to its left. Per-faction roster is hidden — too
     long for phone width — but is still rendered for tablet+ widths. */
  #minimap-panel {
    width: 140px;
    padding: 4px 6px;
    bottom: 8px;
    right: 8px;
  }
  #minimap-panel .mm-title { font-size: 10px; padding-right: 28px; }
  #minimap-panel canvas    { aspect-ratio: 16 / 9; }
  #minimap-panel .mm-legend { font-size: 9px; gap: 6px; }
  #minimap-panel .mm-factions { display: none; }
  #minimap-panel .panel-collapse {
    width: 24px; height: 20px; font-size: 13px; line-height: 16px;
    top: 4px; right: 4px;
  }

  /* Build panel: full remaining width minus minimap + gutters. */
  #build-panel {
    /* 100vw − minimap (140) − right margin (8) − left margin (12) − gap (8) */
    width: calc(100vw - 140px - 8px - 12px - 8px);
    bottom: 8px;
    left: 12px;
    padding: 6px 8px;
    /* Cap height so the build cards don't push the panel off-screen
       when 12 cards wrap on the narrowest phones. Internal scrolling
       picks up the overflow. */
    max-height: 38vh;
    overflow-y: auto;
  }
  #build-panel > #buildings { padding-right: 30px; }
  #build-panel .panel-collapse {
    width: 24px; height: 20px; font-size: 13px; line-height: 16px;
    top: 4px; right: 4px;
  }
  #buildings {
    grid-template-columns: repeat(auto-fill, minmax(54px, 1fr));
    gap: 4px;
  }
  .bcard {
    padding: 3px 3px 4px;
    gap: 2px;
    font-size: 9.5px;
  }
  .bcard .cost { font-size: 9px; }
  /* Hide the "click a building, then click on the field" hint — it
     wastes a row on portrait phones and the hover .btip is dead on
     touch anyway. */
  #build-panel #hint { display: none; }

  /* Top-bar: shrink resource chips + buttons so they fit on 390px. */
  #top-bar { gap: 4px; padding: 4px 6px; flex-wrap: wrap; }
  #top-bar .resource { font-size: 11px; padding: 2px 6px; }
  #top-bar .resource .ricon { width: 14px; height: 14px; }
  #top-bar .hud-btn { padding: 3px 6px; font-size: 11px; }
  #top-bar #vol-slider { width: 60px; }

  /* Chat feed already auto-shrinks via min(420px, 38vw); but bump
     it down a hair so it never starts directly under the top-bar
     even on wrapped/tall configurations. */
  #chat-feed { top: 96px; left: 8px; width: min(70vw, 360px); font-size: 12px; }

  /* MP debug chip stays in top-left but stays collapsed (already
     defaults to collapsed via main.js); just shrink the font. */
  #mp-debug { font-size: 10px; padding: 3px 6px; max-width: 200px; }

  /* Touch targets — Apple HIG suggests 44px. The hud-btn is small; at
     least make the big buttons (Quit, Help, Strike) generous when
     they wrap to a second top-bar row. */
  #strike-btn, #help-toggle, #quit-btn, #chat-btn, #mute-btn {
    min-width: 30px; min-height: 26px;
  }
}

/* Very narrow phones (≤420px) — give the build panel even more room
   by hiding the minimap canvas entirely behind its collapse chevron
   (still toggle-able by the user). */
@media (max-width: 420px) {
  #minimap-panel { width: 112px; }
  #build-panel {
    width: calc(100vw - 112px - 8px - 12px - 8px);
  }
  #buildings {
    grid-template-columns: repeat(auto-fill, minmax(48px, 1fr));
    gap: 3px;
  }
  .bcard { padding: 2px; font-size: 9px; }
}

/* ===================== Mobile touch-pan repositioning =================
   The default 168×112 D-pad lived at bottom-left, which overlapped the
   build panel on phones. On narrow screens we burst the panel into
   per-button absolute positions so each control sits on the screen
   EDGE rather than in the bottom-left corner where the build panel
   lives. D-pad on the left edge at thumb height; zoom on the right
   edge just above the minimap. The container itself becomes a
   transparent full-screen overlay (pointer-events: none) so field
   clicks pass through everywhere except on the actual buttons. */
@media (max-width: 740px) {
  body.touch-mode #touch-pan {
    /* Full-viewport overlay; each button is absolutely placed below. */
    left: 0; right: 0; top: 0; bottom: 0;
    width: auto; height: auto;
    pointer-events: none;
  }
  body.touch-mode #touch-pan .tp-btn {
    pointer-events: auto;
    width: 42px;
    height: 42px;
    font-size: 16px;
    /* Slight extra contrast so the buttons stay readable against any
       background (sky, grass, building roofs). */
    background: rgba(20, 22, 30, 0.65);
    border-color: rgba(255, 220, 140, 0.55);
  }
  /* D-pad cluster: left edge, vertically centred ~10% up from middle
     so the player's left thumb falls on it naturally without
     covering the centre of the field. */
  body.touch-mode #touch-pan .tp-up    { left: 50px; top: calc(50% - 68px); }
  body.touch-mode #touch-pan .tp-left  { left: 8px;  top: calc(50% - 26px); }
  body.touch-mode #touch-pan .tp-right { left: 92px; top: calc(50% - 26px); }
  body.touch-mode #touch-pan .tp-down  { left: 50px; top: calc(50% + 16px); }
  /* Zoom — right edge, just above the minimap (which sits bottom-right
     at width 140 + 8 margin on phones). Stack vertically. */
  body.touch-mode #touch-pan .tp-zin   {
    left: auto;
    right: 10px;
    top: auto;
    bottom: 200px;
  }
  body.touch-mode #touch-pan .tp-zout  {
    left: auto;
    right: 10px;
    top: auto;
    bottom: 150px;
  }
}

/* ===================== Mobile start overlay tuning =====================
   The default start-card is 360px wide with a top:80 offset. On phones
   the logo + 5 picker sections push the Start button below the fold.
   Compress vertical rhythm + shrink the logo so everything fits, and
   let the card scroll if the user has a really short viewport. */
@media (max-width: 600px), (max-height: 760px) {
  #start-overlay {
    top: 8px;
    max-height: calc(100vh - 16px);
    /* iOS Safari viewport-height calc — use dynamic vh so the toolbar
       collapsing doesn't push the Start button under the address bar. */
    max-height: calc(100dvh - 16px);
  }
  #start-overlay .start-card {
    width: calc(100vw - 24px);
    max-width: 360px;
    padding: 8px 12px 12px;
  }
  #start-overlay .start-logo {
    max-height: 96px;
    width: auto;
    margin: 0 auto 4px;
  }
  #start-overlay .start-beta {
    top: 6px; right: 8px;
    font-size: 9px;
    letter-spacing: 0.12em;
  }
  #start-overlay .picker-section { margin: 8px 0 4px; }
  #start-overlay .picker-label { font-size: 9px; margin-bottom: 4px; }
  #start-overlay .race-card { padding: 5px 2px 4px; }
  #start-overlay .race-card img { width: 30px; height: 30px; }
  #start-overlay .race-card .race-card-name { font-size: 10px; max-width: 56px; }
  #start-overlay .gm-card { padding: 8px 6px; }
  #start-overlay .gm-card .gm-name { font-size: 13px; }
  #start-overlay .gm-card .gm-sub  { font-size: 9px; }
  #start-overlay .ts-card { padding: 6px 4px; }
  #start-overlay .ts-card .ts-label { font-size: 13px; }
  #start-overlay .ts-card .ts-sub   { font-size: 8px; }
  #start-overlay .gold-card { padding: 6px 4px; }
  #start-overlay .gold-card .gold-amount { font-size: 14px; }
  #start-overlay .gold-card .gold-sub { font-size: 9px; }
  #start-overlay #start-btn {
    width: 100%;
    margin-top: 8px;
  }
  #start-overlay .start-hint { font-size: 10px; margin-top: 6px; }
}
