:root {
  --bg: #0b0d11;
  --panel: rgba(22, 25, 31, 0.84);
  --panel-2: rgba(38, 42, 51, 0.9);
  --border: #3a3f4b;
  --text: #e6e6e6;
  --muted: #9aa0aa;
  --blue: #4a90e2;
  --red: #d9534f;
  --gold: #f5c542;
  --green: #5cb85c;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }  /* otherwise display: flex/grid rules below would un-hide elements */
/* Fixed 1920x1080 UI: the page is laid out once in a 1920x1080 frame at the top centre of the window and never
   reflows. In a smaller window the whole frame (and the background with it) is scaled down uniformly by
   --ui-scale, set in common.js; in a bigger window it stays 1920x1080. Content that can't fit scrolls inside
   its own area. */
:root {
  --ui-scale: 1;
  --bottom-bar-height: 192px;  /* height of ui/bottom-bar.png (built at 1920px wide); content stops above it */
  --top-bar-space: 116px;      /* the middle column starts below the top bar; the left pane stays full height */
}
html, body { height: 100%; overflow: hidden; }
/* Scrollable areas still scroll (mouse wheel, touchpad, keyboard) but never show a scroll bar. */
* { scrollbar-width: none; }
*::-webkit-scrollbar { display: none; }
body {
  margin: 0; color: var(--text); font: 14px system-ui, sans-serif;
  background:
    url("/background.jpg") top center / calc(1920px * var(--ui-scale)) calc(1080px * var(--ui-scale)) no-repeat,
    #050608;
}
body > .layout, body > main {
  position: absolute; top: 0; left: 50%; width: 1920px; height: 1080px;
  transform: translateX(-50%) scale(var(--ui-scale)); transform-origin: top center;
}
main { max-width: 1240px; margin: 0 auto; padding: 16px; height: 100%; overflow-y: auto; }
body > main { max-width: none; margin: 0; }

/* Bottom bar across the frame, on top of the background. Everything else is kept above it. */
body > .layout { padding-bottom: var(--bottom-bar-height); }
body > main { height: calc(1080px - var(--bottom-bar-height)); }
body::after {
  content: ""; position: fixed; left: 50%; top: 0; width: 1920px; height: 1080px; pointer-events: none; z-index: 5;
  transform: translateX(-50%) scale(var(--ui-scale)); transform-origin: top center;
  background: url("/ui/bottom-bar.png") center bottom / 1920px var(--bottom-bar-height) no-repeat;
}
h1 { font-size: 20px; margin: 0 0 12px; }
h2 { font-size: 16px; margin: 0 0 10px; }
a { color: #8ec5ff; }
.muted { color: var(--muted); }

header#top {
  display: flex; flex-wrap: wrap; gap: 16px; align-items: center;
  padding: 10px 16px; background: var(--panel); border-bottom: 1px solid var(--border);
}
header#top .brand { font-weight: 700; }
header#top nav { display: flex; gap: 12px; }
header#top nav a { color: var(--muted); text-decoration: none; }
header#top nav a.active, header#top nav a:hover { color: var(--text); }
header#top .who { margin-left: auto; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.coins { color: var(--gold); font-weight: 600; }

.panel {
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 14px; margin-bottom: 14px;
  backdrop-filter: blur(6px);
}
.row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.split { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 14px; align-items: start; }

input, select, button {
  background: var(--panel-2); color: var(--text); border: 1px solid var(--border); border-radius: 4px;
  padding: 6px 10px; font: inherit;
}
input[type=number] { width: 80px; }
button { cursor: pointer; }
button:hover:not(:disabled) { background: #323743; }
button:disabled { opacity: 0.45; cursor: default; }
button.primary { background: #2f6fbd; border-color: #3d82d6; }
button.primary:hover:not(:disabled) { background: #3a7fd0; }
button.gold { background: #8a6d1c; border-color: #b08d2a; }
button.twitch { background: #6441a5; border-color: #7d5bbe; font-size: 16px; padding: 10px 18px; }

.pill { padding: 2px 8px; border-radius: 10px; font-size: 12px; background: var(--panel-2); }
.pill.gold { background: #5a4a14; color: var(--gold); }
.pill.blue { background: #1f3a5c; color: #8ec5ff; }
.pill.green { background: #1f4a2a; color: #8fdc9a; }
.pill.red { background: #5c1f1f; color: #ff8a85; }

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border); }
th { color: var(--muted); font-weight: 500; font-size: 12px; }

.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px; }
.troop-card {
  background: var(--panel-2); border: 2px solid var(--border); border-radius: 6px; padding: 8px;
  text-align: left; display: flex; flex-direction: column; gap: 2px;
}
.troop-card.selected { border-color: var(--gold); }
.troop-card .name { font-weight: 600; }
.troop-card .sub { font-size: 12px; color: var(--muted); }

/* Army health bars in the battle stats panel. */
.bar { height: 12px; background: #2a2e36; border-radius: 3px; overflow: hidden; }
.bar > div { height: 100%; width: 100%; }
.bar .players { background: var(--blue); }
.bar .enemies { background: var(--red); }

.map-wrap { width: 100%; }
.map-wrap canvas { border-radius: 0; flex: none; }

/* Battle pages: title and controls take what they need, the map fills the rest of the viewport. The frame
   is a fixed 1080px tall - 192px of it is the bottom bar art and 116px the top - so the padding is trimmed
   here to give the battlefield back what little is left to give. */
main.battle { display: flex; flex-direction: column; overflow: hidden; padding: 10px 12px; }
main.battle > .split { flex: 1; min-height: 0; grid-template-columns: minmax(0, 1fr) 380px; align-items: stretch; }
/* Positioning frame for the troop bar, which floats over the battlefield rather than sitting under it. */
.stage-col { position: relative; display: flex; flex-direction: column; min-height: 0; min-width: 0; }
main.battle .map-wrap {
  flex: 1; min-height: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; overflow: hidden;
}
main.battle aside { min-height: 0; overflow-y: auto; padding-right: 2px; }

/* The captain's preview: a half-size map with the live numbers beside it, so the fight, the scoreboard and
   the loot controls are all on screen at once. The full 1920x1080 framing is what OBS captures. */
/* Stretch, not start: with `align-items: start` the grid rows size to their content, so .map-wrap only ever
   got the height of whatever the canvas already was - and resize() measures the canvas off the wrapper, so
   it settled small and stayed there. Stretching gives the battlefield the whole column. */
.preview-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(260px, 360px); gap: 14px;
               align-items: stretch; flex: 1; min-height: 0; }
/* Half size, but only for the captain: the viewer's map is the whole point of their page. */
main.battle .split:not(.viewer) .preview-row .map-wrap { max-width: 960px; }
.battle-stats { margin-bottom: 0; display: flex; flex-direction: column; gap: 12px; min-height: 0; }
.stats-top {
  display: flex; flex-direction: column; gap: 12px;
  padding-bottom: 12px; border-bottom: 1px solid var(--border);
}
.stats-armies, .stats-leaders { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.army-head {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 12px; color: var(--muted); margin-bottom: 4px;
}
.army-count { font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text); }
.leader {
  background: var(--panel-2); border-radius: 6px; padding: 8px; display: flex; flex-direction: column; gap: 2px;
}
.leader-title { font-size: 11px; letter-spacing: 0.05em; color: var(--muted); }
.leader-name { font-weight: 600; overflow-wrap: anywhere; }
.leader-value { font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; }
.leader.kills .leader-value { color: #ff7ab8; }
.leader.assists .leader-value { color: #2ec4b6; }
.stats-bottom { overflow-y: auto; min-height: 0; }
.stats-bottom th:nth-child(n+3), .stats-bottom td:nth-child(n+3) {
  text-align: right; width: 34px; font-variant-numeric: tabular-nums;
}

/* Placement roster: names and the actual troops each player put down, not just a count. */
.stats-roster { display: flex; flex-direction: column; gap: 6px; overflow-y: auto; min-height: 0; }
.roster-row {
  display: grid; grid-template-columns: 1fr auto auto; gap: 8px; align-items: center;
  padding: 6px 8px; background: var(--panel-2); border-radius: 6px;
}
.roster-name { font-weight: 600; overflow-wrap: anywhere; }
.roster-troops { display: flex; gap: 4px; flex-wrap: wrap; justify-content: flex-end; }
.roster-icon { width: 26px; height: 26px; border-radius: 5px; }
.roster-count { font-variant-numeric: tabular-nums; min-width: 16px; text-align: right; }

/* The viewer's canvas carries its own roster, drawn the same way the stream draws it, so the page gives
   the whole row to the map instead of putting a second roster beside it. The side panel goes entirely:
   everything a viewer does while placing lives in the strip across the bottom of the map. */
.split.viewer .preview-row { grid-template-columns: minmax(0, 1fr); }
main.battle > .split.viewer { grid-template-columns: minmax(0, 1fr); }
.split.viewer aside { display: none; }

/* Out of flow, so the map keeps the whole column. Pinned to all three edges rather than measured: the
   canvas fills its container now, so the bar spans the battlefield by definition. */
.troop-bar {
  position: absolute; z-index: 3; left: 0; right: 0; bottom: 0;
  background: rgba(10, 11, 15, 0.92); border: 1px solid var(--border); border-top-color: #4a3a12;
  border-radius: 10px 10px 0 0; padding: 10px 12px;
  display: flex; flex-direction: column; gap: 8px;
}
/* Bottom-right of the battlefield. The two offsets are set by battle.html, because what has to be cleared
   changes: the roster panel is only drawn for viewers, and the troop bar's height changes when collapsed. */
.map-controls {
  position: absolute; z-index: 4;
  right: calc(var(--panel-width, 0px) + 12px);
  bottom: calc(var(--bar-height, 0px) + 12px);
  display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px;
  background: rgba(10, 11, 15, 0.86); border: 1px solid var(--border); border-radius: 8px;
}
.map-controls button { padding: 2px 9px; font-size: 14px; line-height: 1.2; }
.zoom-row { display: flex; align-items: center; gap: 6px; }
#zoomLevel { min-width: 42px; text-align: center; font-size: 12px; color: var(--muted);
             font-variant-numeric: tabular-nums; }
.dpad { display: grid; grid-template-columns: repeat(3, 30px); grid-template-rows: repeat(3, 26px); gap: 3px; }
.dpad button { padding: 0; font-size: 11px; }
#panUp { grid-area: 1 / 2; }
#panLeft { grid-area: 2 / 1; }
#focusTroops { grid-area: 2 / 2; font-size: 14px; color: var(--gold); }
#panRight { grid-area: 2 / 3; }
#panDown { grid-area: 3 / 2; }
.map-controls .reset { font-size: 11px; padding: 3px 10px; }

.troop-bar-head { display: flex; align-items: center; gap: 12px; }
.troop-bar-head .muted { flex: 1; }
.bar-toggle { padding: 2px 10px; line-height: 1.1; font-size: 12px; }
/* Collapsed, the bar keeps only its one-line header and the map takes back the height. */
.troop-bar.collapsed .troop-strip { display: none; }
/* One row of every class, scrolling sideways rather than wrapping, so the bar keeps a fixed height. */
.troop-strip { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; }
.troop-tile {
  position: relative; flex: 0 0 auto; width: 84px; padding: 8px 6px 6px;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  background: var(--panel-2); border: 2px solid var(--border); border-radius: 8px; cursor: pointer;
}
.troop-tile:hover:not(:disabled) { border-color: #6a7284; }
.troop-tile.selected { border-color: var(--gold); background: #3a3118; }
.troop-tile:disabled { cursor: default; opacity: 1; }          /* handled per state below, not blanket-dimmed */
.troop-tile .tile-icon { width: 44px; height: 44px; border-radius: 6px; }
.tile-name { font-size: 12px; font-weight: 600; }
.tile-level { font-size: 11px; color: var(--muted); }
.tile-status { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.tile-status.ready { color: #8fdc9a; }
/* Locked: greyed right down with the padlock, so what is left to earn reads at a glance. */
.troop-tile.locked { opacity: 0.55; }
.troop-tile.locked .tile-icon { filter: grayscale(1) brightness(0.6); }
.troop-tile.locked .tile-status { font-size: 15px; }
/* On cooldown: still clearly yours, just not yet. */
.troop-tile.cooling .tile-icon { filter: grayscale(0.7) brightness(0.75); }
.tile-count {
  position: absolute; top: 4px; right: 4px; min-width: 18px; padding: 0 4px; border-radius: 9px;
  background: var(--blue); color: #fff; font-size: 11px; font-weight: 700; text-align: center;
}

/* After the fight the rewards take the preview's place, so the tables can sit side by side instead of
   stacking down a narrow column. */
.results-pane { flex: 1; min-height: 0; overflow-y: auto; margin-bottom: 0; }
.result-detail { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 18px; }
canvas { display: block; border-radius: 6px; background: #000; }
.big-timer { font-size: 28px; font-weight: 700; font-variant-numeric: tabular-nums; }
/* Testing controls, only rendered under DS_FAST. Marked out so they are never mistaken for game UI. */
.debug-row {
  margin-top: 10px; padding-top: 10px; border-top: 1px dashed #6a5a2a; gap: 6px;
}
.debug-row button { font-size: 12px; padding: 4px 8px; }

.mode-choice { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.mode-choice button { padding: 20px; text-align: left; display: flex; flex-direction: column; gap: 6px; }
.mode-choice button strong { font-size: 18px; }

/* --- app layout with left menu --- */
.layout { display: grid; grid-template-columns: 220px minmax(0, 1fr); overflow: hidden; }
.layout > main {
  width: 100%; max-width: 1240px; margin: var(--top-bar-space) 0 0; height: calc(100% - var(--top-bar-space));
}
main.wide, .layout > main.wide { max-width: none; }  /* battle stage pages use the full width */
.sidebar {
  padding: 16px 12px; display: flex; flex-direction: column; gap: 16px; height: 100%; overflow-y: auto; min-height: 0;
}
.sidebar .brand { display: block; padding: 0 8px; }
.sidebar .brand img { display: block; width: 100%; max-width: 160px; height: auto; margin: 0 auto; }
.page-logo { display: block; width: 150px; height: auto; margin-bottom: 12px; }
.page-logo.small { width: 100px; }
.nav-links { display: flex; flex-direction: column; gap: 8px; }
.nav-button { position: relative; display: block; width: 100%; line-height: 0; }
.nav-button img { display: block; width: 100%; height: auto; }
.nav-button .nav-on { position: absolute; inset: 0; opacity: 0; transition: opacity 0.12s; }
.nav-button:hover .nav-on, .nav-button:focus-visible .nav-on, .nav-button.active .nav-on { opacity: 1; }

/* --- top bar: coins, avatar, name and role, notifications and settings (built from ui/topbar-*.png) --- */
.topbar { position: absolute; top: 12px; right: 20px; z-index: 6; height: 96px; display: flex; align-items: center; }
/* Back layer: stretches with its contents (long streamer names); its right end tucks under the front layer. */
.topbar-back {
  position: relative; z-index: 1; height: 82px; display: flex; align-items: center; gap: 14px;
  border-style: solid; border-width: 0 51px; border-image: url("/ui/topbar-back.png") 0 72 fill / 0 51px stretch;
  padding: 0 185px 0 0; margin-right: -200px;  /* content stays ~36px clear of the front layer's slanted edge */
}
.topbar-front { position: relative; z-index: 2; flex: none; width: 496px; height: 96px; }
.tb-front-art { position: absolute; inset: 0; width: 100%; height: 100%; }
.topbar .tb-icon, .topbar .tb-icon:hover:not(:disabled) {
  position: relative; display: block; flex: none; padding: 0; border: 0; background: none; cursor: pointer; line-height: 0;
}
.tb-icon .tb-off, .tb-icon .tb-on { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; }
.tb-icon .tb-on { opacity: 0; transition: opacity 0.12s; }
.tb-icon:hover .tb-on, .tb-icon:focus-visible .tb-on, .topbar.settings-open .tb-gear .tb-on { opacity: 1; }
.topbar .tb-coin { width: 67px; height: 67px; }
.tb-coins {
  font: 700 26px Georgia, "Times New Roman", serif; color: #f0c24b; white-space: nowrap;
  text-shadow: 0 2px 3px #000, 0 0 12px rgba(240, 170, 60, 0.35);
}
.tb-divider { width: 2px; height: 52px; background: linear-gradient(transparent, #8a6a3a, transparent); }
.topbar .tb-avatar { width: 78px; height: 78px; }
/* The ring's centre is see-through: the picture sits behind it, so any size or shape of logo fits. */
.tb-avatar-pic {
  position: absolute; left: 50%; top: 50%; z-index: 0; width: 50px; height: 50px; transform: translate(-50%, -50%);
  border-radius: 50%; object-fit: cover;
}
.tb-avatar-initial {
  display: grid; place-items: center; line-height: 1; background: radial-gradient(circle at 35% 30%, #3d6fb0, #142742);
  color: #f4e4c1; font: 700 24px Georgia, "Times New Roman", serif;
}
.tb-who { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0; }
.tb-name {
  max-width: 420px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 700 22px Georgia, "Times New Roman", serif; color: #f4e4c1; text-shadow: 0 2px 3px #000;
}
.tb-role {
  padding: 2px 12px; border-radius: 999px; font-size: 14px; font-weight: 600; color: #f7d88b;
  background: linear-gradient(180deg, #7a5a14, #4a360b); border: 1px solid #b48a2c; box-shadow: inset 0 1px 0 rgba(255, 230, 160, 0.25);
}
.tb-role.viewer { color: #bcd9ff; background: linear-gradient(180deg, #24466e, #142a45); border-color: #3f6ea6; }
.topbar .tb-bell { position: absolute; left: 287px; top: 7px; width: 83px; height: 83px; }
.tb-settings { position: absolute; left: 372px; top: 8px; width: 82px; height: 82px; }
.topbar .tb-gear { width: 100%; height: 100%; }
.tb-menu {
  display: none; position: absolute; top: 86px; right: 0; min-width: 160px; flex-direction: column; gap: 4px; padding: 8px;
  background: rgba(16, 17, 22, 0.96); border: 1px solid #8a6a3a; border-radius: 8px; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.6);
}
.topbar.settings-open .tb-menu { display: flex; }
.tb-menu button { text-align: left; }

/* --- viewer battle slots --- */
/* Real-money tile: green so it reads as money rather than gold, and clearly apart from the scroll offers. */
.shop-slot.premium { border-color: #2f8f5b; background: radial-gradient(circle at 50% 30%, #123021, #0b1a12); }
.shop-slot.premium .slot-name { color: #8ef0b4; }
button.money {
  background: linear-gradient(180deg, #2fa563, #1d7344); color: #eafff2; border: 1px solid #3fd98a;
  font-weight: 700; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
button.money:hover:not(:disabled) { background: linear-gradient(180deg, #37bd72, #23854f); }

/* --- shop: square tiles, four across, with the reroll tile filling the eighth cell --- */
.shop-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; max-width: 1180px; }
.shop-slot {
  position: relative; aspect-ratio: 1; overflow: hidden; border-radius: 10px; display: flex;
  flex-direction: column; justify-content: flex-end; padding: 12px;
  border: 2px solid color-mix(in srgb, var(--class) 60%, #0d0b0b);
  box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.7), 0 6px 14px rgba(0, 0, 0, 0.5);
}
/* The art sits in its own layer so locked tiles grey the background without touching the icons on top. */
.shop-slot .slot-art {
  position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; object-fit: cover;
  object-position: center 30%;
}
/* The "Locked" pill and the full-colour badge already say it's locked, so the art only needs muting. Full
   greyscale at 0.55 brightness, under the gradient below, left the tile a black rectangle. */
.shop-slot.locked .slot-art { filter: grayscale(0.7) brightness(0.8); }
.shop-slot .slot-art.icon-only { object-fit: contain; padding: 18%; background: color-mix(in srgb, var(--class) 45%, #0d0b0b); }
.shop-slot::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  /* Clear over the painting, going solid only where the name and button need contrast behind them. */
  background: linear-gradient(180deg, rgba(8, 9, 12, 0) 32%, rgba(8, 9, 12, 0.55) 58%, rgba(8, 9, 12, 0.92) 82%);
}
.shop-slot > * { position: relative; z-index: 2; }
.slot-top { position: absolute; top: 10px; left: 10px; right: 10px; display: flex; align-items: flex-start; gap: 8px; }
.slot-badge { width: 44px; height: 44px; border-radius: 8px; border: 2px solid rgba(255, 255, 255, 0.25); }
.slot-tag {
  margin-left: auto; padding: 3px 9px; border-radius: 999px; font-size: 12px; font-weight: 600;
  background: rgba(8, 9, 12, 0.8); color: #c9d1dc;
}
.slot-name { font: 700 19px Georgia, "Times New Roman", serif; color: #f4e4c1; text-shadow: 0 2px 4px #000; }
.slot-amount { display: flex; align-items: center; gap: 6px; margin: 2px 0 8px; }
.slot-amount img { width: 26px; height: 26px; }
.slot-amount strong { font-size: 20px; color: #fff; text-shadow: 0 2px 4px #000; }
.slot-amount .muted { font-size: 12px; margin-left: auto; }
.shop-slot button { width: 100%; }
/* The reroll tile shares the square shape but has no art behind it. */
.shop-slot.reroll { justify-content: center; align-items: center; text-align: center; gap: 6px;
  border-color: #8a6a3a; background: radial-gradient(circle at 50% 35%, #2a2418, #14120d); }
.shop-slot.reroll .clock { font: 700 30px Georgia, "Times New Roman", serif; color: #f5c542; }

.slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; margin-bottom: 18px; }
/* Click through to the live stream: the battle plays out there, not in the viewer's own page. */
.watch-link {
  display: inline-block; margin-top: 6px; padding: 5px 12px; border-radius: 6px;
  font-weight: 600; font-size: 13px; text-decoration: none; color: #f0e9ff; background: #6441a5;
}
.watch-link:hover { background: #7a57c2; }
.slot {
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 16px; backdrop-filter: blur(6px);
  min-height: 200px; display: flex; flex-direction: column; gap: 8px;
}
.slot.empty, .slot.locked { border-style: dashed; align-items: center; justify-content: center; text-align: center; }
.slot.locked { opacity: 0.45; filter: grayscale(1); }
.slot-label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
.slot .captain-name { font-size: 18px; font-weight: 700; overflow-wrap: anywhere; }
.slot .actions { margin-top: auto; display: flex; gap: 8px; }
.slot-icon { font-size: 30px; }
.slot { position: relative; }
.slot-remove {
  position: absolute; top: 8px; right: 8px; width: 30px; height: 30px; padding: 0; border-radius: 50%;
  background: transparent; border: 1px solid transparent; color: #ff5c57; font-size: 16px; font-weight: 700; line-height: 1;
}
.slot-remove:hover:not(:disabled) { background: #5c1f1f; border-color: #8a2e2e; }
.slot-remove:disabled { color: var(--muted); }

/* --- character and shop tiles --- */
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); gap: 14px; }
.tile {
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 16px; display: flex;
  flex-direction: column; gap: 10px; backdrop-filter: blur(6px);
}
.tile.locked { opacity: 0.65; }
.tile-head { display: flex; align-items: center; gap: 12px; }
.badge {
  width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center; flex: none;
  font-weight: 700; font-size: 20px; background: var(--blue); color: #fff; border: 2px solid #111;
}
.tile.locked .badge { background: #555; }
.badge-icon { width: 56px; height: auto; border-radius: 8px; flex: none; }
.tile.locked .badge-icon { filter: grayscale(1) brightness(0.8); }
.troop-card .card-icon { width: 40px; height: auto; border-radius: 6px; margin-bottom: 4px; }
.troop-card:disabled .card-icon { filter: grayscale(1); }
.tile-title { font-size: 17px; font-weight: 700; }
.stats { display: grid; grid-template-columns: 1fr auto; gap: 3px 12px; font-size: 13px; }
.stats .up { color: #8fdc9a; margin-left: 6px; }
.tile .actions { margin-top: auto; display: flex; flex-wrap: wrap; gap: 8px; }

/* --- character cards (Characters and Shop pages) --- */
.tiles:has(.class-card) { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; }
.class-card {
  --class: #8a6d1c;
  position: relative; padding: 0; gap: 0; overflow: hidden; border-radius: 12px; backdrop-filter: none;
  border: 2px solid color-mix(in srgb, var(--class) 75%, #3b2a12);
  background: linear-gradient(180deg, color-mix(in srgb, var(--class) 28%, #0d0b0b) 0%, #111217 55%, #0c0d10 100%);
  box-shadow:
    0 0 0 1px #000,
    inset 0 0 0 1px rgba(255, 214, 150, 0.22),
    inset 0 0 22px color-mix(in srgb, var(--class) 40%, transparent),
    0 0 16px color-mix(in srgb, var(--class) 30%, transparent),
    0 8px 20px rgba(0, 0, 0, 0.55);
}
/* Small gold studs in the corners. */
.class-card::after {
  content: ""; position: absolute; inset: 5px; pointer-events: none; z-index: 2;
  background:
    radial-gradient(circle, #f0c872 0 2px, #7a5418 3px, transparent 4px) top left / 12px 12px no-repeat,
    radial-gradient(circle, #f0c872 0 2px, #7a5418 3px, transparent 4px) top right / 12px 12px no-repeat,
    radial-gradient(circle, #f0c872 0 2px, #7a5418 3px, transparent 4px) bottom left / 12px 12px no-repeat,
    radial-gradient(circle, #f0c872 0 2px, #7a5418 3px, transparent 4px) bottom right / 12px 12px no-repeat;
}
.card-hero {
  position: relative; height: 190px; overflow: hidden;
  background: radial-gradient(circle at 50% 40%, color-mix(in srgb, var(--class) 55%, #000) 0%, #0d0d10 80%);
  border-bottom: 1px solid color-mix(in srgb, var(--class) 45%, transparent);
}
.card-hero-art {
  display: block; width: 100%; height: 100%; object-fit: cover; object-position: center 65%;
  -webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
}
.card-hero-icon {
  position: absolute; left: 50%; top: 50%; width: 110px; height: auto; transform: translate(-50%, -50%);
  border-radius: 12px; box-shadow: 0 0 24px color-mix(in srgb, var(--class) 60%, transparent);
}
.card-body { display: flex; flex-direction: column; gap: 10px; flex: 1; padding: 10px 18px 18px; }
.card-title-row { display: flex; align-items: center; gap: 10px; }
.card-name {
  font: 700 21px Georgia, "Times New Roman", serif; color: #f4e4c1; letter-spacing: 0.01em;
  text-shadow: 0 2px 3px #000, 0 0 10px color-mix(in srgb, var(--class) 40%, transparent);
}
.card-subtitle { color: #e6dccb; font-size: 14px; }
.card-title-row > * { white-space: nowrap; }
.card-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* Status pill sits on the banner's top-right corner, leaving the title row for the name and level. */
.card-status { position: absolute; top: 12px; right: 14px; z-index: 3; }
.card-status .pill {
  padding: 3px 10px; font-size: 13px; white-space: nowrap; border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.6);
}
.class-card .stats > .muted { white-space: nowrap; }
.card-blurb { color: #d2cbbf; line-height: 1.4; }
.class-card .stats { font-size: 14px; gap: 4px 12px; color: #ece6db; }
.class-card .stats .muted { color: #cfc6b8; }
.card-scrolls { color: #cfc6b8; }
.card-scrolls strong { color: #f1ebe1; }
.class-card .actions { margin-top: auto; padding-top: 6px; }
.class-card .actions > button, .class-card .actions > a { flex: 1; }
.class-card .actions a > button { width: 100%; }
.class-card .actions { flex-wrap: nowrap; gap: 6px; }
.class-card .actions button { padding: 10px 8px; border-radius: 6px; font-weight: 600; font-size: 13px; white-space: nowrap; }
.class-card .actions button.primary {
  background: linear-gradient(180deg, #3d86dc 0%, #1f5aa8 100%); border-color: #5b9ce8; color: #fff;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
}
.class-card .actions button.gold { background: linear-gradient(180deg, #a8812a 0%, #6e5217 100%); border-color: #c99d3a; }
.class-card.locked { opacity: 1; border-color: color-mix(in srgb, var(--class) 30%, #3a3a3a); }
.class-card.locked .card-hero-art, .class-card.locked .card-hero-icon { filter: grayscale(0.7) brightness(0.85); }
.class-card.locked .card-body > :not(.actions) { opacity: 0.8; }

/* Painted frame (ui/frame-<class>.png, 600px wide): drawn over the card and stretched from its corners. The side
   rails are plain, so only they stretch; corners and the top/bottom gems keep their shape. */
.class-card.framed {
  border: 0; border-radius: 0; background: none; box-shadow: none; overflow: visible; isolation: isolate;
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.6));
}
.class-card.framed::before {  /* card background, kept inside the frame so it never shows past the corners */
  content: ""; position: absolute; inset: 16px 14px 18px; z-index: -1; border-radius: 4px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--class) 30%, #0d0b0b) 0%, #111217 55%, #0c0d10 100%);
}
.class-card.framed::after {
  inset: 0; z-index: 4; background: none;
  border-style: solid; border-width: 86px 38px 80px;
  border-image: var(--frame) 181 79 167 / 86px 38px 80px stretch;
}
.class-card.framed .card-hero { margin: 16px 14px 0; border-bottom: 0; }
.class-card.framed .card-body { padding: 8px 28px 52px; }
/* Framed cards are a little narrower inside, so two buttons side by side need tighter text to fit. */
.class-card.framed .actions > button, .class-card.framed .actions > a { min-width: 0; }
.class-card.framed .actions button { font-size: 12px; padding: 9px 4px; }
.class-card.framed .card-status { top: 42px; right: 40px; }

/* --- modal --- */
.modal-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); display: grid; place-items: center; z-index: 20; padding: 16px; }
.modal { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 18px; width: min(580px, 100%); max-height: 80vh; overflow: auto; }

.toast {
  position: fixed; bottom: calc(var(--bottom-bar-height) * var(--ui-scale) + 16px); left: 50%; transform: translateX(-50%);
  background: #2d3340; border: 1px solid var(--border); padding: 10px 16px; border-radius: 6px; z-index: 10;
}
.toast.error { background: #5c1f1f; border-color: #8a2e2e; }

/* --- phones --------------------------------------------------------------------------------------
   Below 900px the fixed 1920x1080 frame is abandoned rather than scaled. Scaled, a phone gets the whole
   desktop layout at about a fifth size - 14px text renders under 3px. So here the frame is unwound and
   the page becomes an ordinary responsive document: one column, the left menu moved to a bottom tab bar
   where a thumb can reach it, and the battlefield given the whole screen.

   Keyed off html[data-phone] (set in common.js) as well as the media query, so the canvas code and the
   stylesheet can never disagree about which layout is in force. The breakpoint is PHONE_WIDTH there. */
@media (max-width: 899px) {
  html, body { height: auto; overflow-x: hidden; overflow-y: auto; }
  /* The frame: absolutely positioned, 1920 wide and transform-scaled. All three have to go. */
  body > .layout, body > main {
    position: static; width: 100%; height: auto; min-height: 100vh;
    transform: none; left: auto; padding-bottom: 0;
  }
  body { background-size: cover, auto; background-attachment: fixed; }
  /* The bottom-bar art is a 1920px-wide PNG; at phone width it is just a smear. */
  body::after { display: none; }

  .layout { display: flex; flex-direction: column; overflow: visible; }
  /* body > main is listed explicitly: it is more specific than `main`, and it carries both a fixed
     1080px-based height and the frame positioning. The login page uses a bare <main>, so without this
     it keeps the desktop height and its contents spill off the side. */
  .layout > main, body > main, main {
    max-width: none; width: 100%; margin: 0; height: auto; min-height: 0; overflow: visible;
    /* Clear of the bottom tab bar, plus the home indicator on phones that have one. */
    padding: 12px 12px calc(76px + env(safe-area-inset-bottom));
  }
  /* Pages that centre their own column inline (index.html) must not exceed the screen. */
  main > div[style*="max-width"] { max-width: 100% !important; padding-top: 16px !important; }

  /* The left menu becomes a bottom tab bar: thumb-reachable, and it stops eating 220px of a 390px screen. */
  .sidebar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 8;
    flex-direction: row; align-items: center; justify-content: space-around;
    height: calc(64px + env(safe-area-inset-bottom)); padding: 4px 8px env(safe-area-inset-bottom);
    gap: 0; overflow: visible;
    background: rgba(12, 13, 17, 0.96); border-top: 1px solid var(--border);
  }
  .sidebar .brand { display: none; }          /* the logo is a luxury at this width */
  .nav-links { flex-direction: row; gap: 8px; flex: 1; justify-content: space-around; }
  .nav-button { width: auto; max-width: 110px; }

  /* Top bar: the painted back/front layers are fixed-width art that cannot reflow, so on a phone it
     becomes a plain strip. The gear stays - it is the only way to log out or switch mode. */
  /* renderTopBar() appends the bar as the last child of .layout, which in a flex column puts it under
     the page. order lifts it back to the top without the JS having to know about the phone layout. */
  .topbar { position: static; order: -1; height: auto; width: 100%; margin-bottom: 10px; }
  .topbar-back {
    flex: 1; min-width: 0; height: auto; gap: 8px; padding: 8px 10px; margin: 0;
    border: 1px solid var(--border); border-radius: 8px; background: var(--panel);
  }
  .topbar .tb-coin { width: 34px; height: 34px; }
  .tb-coins { font-size: 17px; }
  .topbar .tb-avatar { width: 40px; height: 40px; }
  .tb-avatar-pic { width: 26px; height: 26px; }
  .tb-name { font-size: 15px; max-width: 34vw; }
  .tb-role { font-size: 11px; padding: 1px 8px; }
  .tb-divider { height: 28px; }
  .topbar-front { width: auto; height: auto; flex: none; margin-left: 8px; }
  .tb-front-art, .topbar .tb-bell { display: none; }
  .tb-settings { position: static; width: 40px; height: 40px; }
  .tb-menu { top: 46px; }

  /* One column everywhere: 300px side panels next to a map do not fit beside anything here. */
  .split, .preview-row, .result-detail, .mode-choice, .stats-armies, .stats-leaders {
    grid-template-columns: minmax(0, 1fr);
  }
  .shop-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .tiles, .tiles:has(.class-card), .slots, .cards {
    grid-template-columns: minmax(0, 1fr);
  }

  h1 { font-size: 22px; }
  .panel { padding: 12px; }
  /* Comfortably tappable: 44px is the smallest target that reliably works with a thumb. */
  button, .watch-link { min-height: 44px; padding: 10px 14px; }
  .bar-toggle, .map-controls button, .slot-remove, .tb-icon { min-height: 0; }

  /* --- the placement screen ---------------------------------------------------------------------
     The map is the page. It takes the viewport less the troop bar and the tab bar; the bar sits across
     the bottom of it, and the d-pad goes because dragging the map replaces it. */
  /* The placement screen fills the screen rather than sizing to its contents: a fixed min-height left a
     short map with a band of empty background under the troop bar. Every level from the page down to the
     canvas flexes, so the map absorbs whatever is left after the bar and the tabs. dvh, not vh, because
     mobile browsers count vh against the largest viewport and hide the bottom behind the address bar. */
  .layout { min-height: 100dvh; }
  main.battle {
    display: flex; flex-direction: column; flex: 1; min-height: 0;
    height: auto; padding: 8px 8px calc(64px + env(safe-area-inset-bottom));
  }
  main.battle > .split { display: flex; flex-direction: column; flex: 1; min-height: 0; }
  main.battle .stage-col { flex: 1; min-height: 0; }
  main.battle .preview-row { flex: 1; min-height: 0; }
  main.battle .map-wrap { flex: 1; min-height: 0; }
  main.battle .split:not(.viewer) .preview-row .map-wrap { max-width: none; }
  /* Touch panning owns the canvas, so the page must not scroll out from under a drag. */
  main.battle .map-wrap canvas { touch-action: none; width: 100%; }

  .troop-bar { padding: 8px; border-radius: 10px 10px 0 0; }
  .troop-tile { width: 70px; padding: 6px 4px 4px; }
  .troop-tile .tile-icon { width: 38px; height: 38px; }
  .tile-name { font-size: 11px; }
  .tile-level, .tile-status { font-size: 10px; }
  .dpad { display: none; }
  .map-controls { right: 8px; padding: 6px; gap: 4px; }
  .map-controls .reset { font-size: 10px; }

  /* Tables are the one thing allowed to scroll sideways rather than be crushed. */
  .stats-bottom, .results-pane table { overflow-x: auto; }
  .modal { max-height: 88vh; }
  .toast { bottom: calc(76px + env(safe-area-inset-bottom)); width: min(92vw, 420px); text-align: center; }
}

/* Streamers run OBS on a desktop, so the control pane is not laid out for a phone. Rather than let it
   look broken, it says so. Shown only where the page asks for it (see battle.html, lobby.html). */
.desktop-only-note { display: none; }
@media (max-width: 899px) {
  .desktop-only-note {
    display: block; margin-bottom: 12px; padding: 12px 14px; border-radius: 8px;
    background: #3a2f12; border: 1px solid #8a6a3a; color: #f4e4c1;
  }
}
