/* Bitmap fonts, local, no internet required. Silkscreen carries UI text; Press Start
   2P carries the ETYMA wordmark and every LETTER TILE (bank tiles, word-display tiles,
   lane keycaps). Both SIL OFL, see the fonts/OFL-*.txt notices.

   Why tiles are Press Start 2P and not Silkscreen: Silkscreen's caps are only 5 art
   pixels tall (ink y 0..625 of a 1000 em) and its advances are proportional (I is 4 px
   wide, M is 8), so a bank tile at 24px drew a 15px-tall glyph and B/8, S/5, O/D and I/1
   were genuinely hard to tell apart at a glance. Press Start 2P is a uniform 7x7 ink box
   in an 8x8 monospaced cell, so the same font-size buys ~40% more glyph height, even
   letter rhythm, and open counters. Playtesters called the old tiles confusing.

   THERE IS NO BOLD IN THIS UI (2026-08-11). Silkscreen Bold doubled every stroke to 2 art
   pixels on a cap only 5 tall, which shuts the counters: its M is two verticals joined by
   a solid 2-row bar across the top, i.e. an H with a high crossbar, and N and W collapse
   into near-solid slabs the same way. Nothing declared that face directly, so it arrived
   by inheritance wherever the weight was 600 or more (the credits wordmark at 900, page
   headers at 700, the incoming-word chips at 600) and the credits logo read as ETYHA. The
   file is deleted, and both remaining faces are declared across the WHOLE 100..900 range
   so a request for any weight matches the one real face exactly. That also settles the
   other half of the problem: an unmatched weight makes the browser SYNTHESISE a bold by
   smearing the glyph off its pixel grid, which is what Press Start (no bold face at all)
   used to suffer from every stray font-weight:700. Size and colour carry emphasis now. */
@font-face { font-family:'Silkscreen'; src:url('fonts/Silkscreen-Regular.ttf') format('truetype'); font-weight:100 900; }
@font-face { font-family:'PressStart'; src:url('fonts/PressStart2P-Regular.ttf') format('truetype'); font-weight:100 900; }

/* Press Start 2P's ink is NOT centred in its monospaced cell. Every cap ENDS at column 6
   of the 8, but only 22 of them START at column 0: I, L, T and Y are inset and start at
   column 1. So the gap after a glyph is 2 art pixels when an I/L/T/Y follows it and 1 art
   pixel otherwise — nothing at tile sizes, a hole at 78px, which is why the wordmark read
   as "E T Y MA" (20px between E-T and T-Y against 10px between Y-M and M-A). Put this on
   each I/L/T/Y in a large Press Start heading to pull it back onto the grid. 0.125em is
   exactly one art pixel at any font-size, so it holds through the logo's clamp(). */
.px-kern { margin-left: -0.125em; }

/* MIXED CASE IS PRESS START'S JOB (2026-08-11). Silkscreen cannot render lowercase at
   all: its a..z are byte-identical COPIES of its A..Z — same outlines, same advances,
   checked against the .ttf — so everything it draws is shouted, and a name like
   "MaRinne" comes out MARINNE. No CSS fixes that; only a different face does. Press
   Start 2P is the only other bundled face and it carries real lowercase (x-height
   0.625em against a 0.875em cap), and conveniently its g/j/p/q/y stop AT the baseline
   instead of dipping below it, so a mixed-case line still fits the box a caps line did.
   So anything whose CASE CARRIES MEANING switches to it: the devlog and credits pages,
   and every name a player or a character owns. The cost is size — the cell is a full 1em
   against Silkscreen's ~0.75em average and the caps are 40% taller — so a switched
   surface runs about a third wider at the same font-size and its container has to have
   the room. Never raise the weight (see the no-bold note above). This class is for a name
   sitting inside a Silkscreen label; a whole surface sets the family in its own rule. */
.px-mixed { font-family: 'PressStart', monospace; font-weight: 400; }
/* The <strong>s that live inside those surfaces — devlog items, playtester names, friend
   rows — come with the UA's 700. The @font-face range above already stops a synthesised
   bold, but these selectors outrank the weights they compete with, so the one real face
   is asked for by name rather than by trusting every engine to read the descriptor. */
.px-mixed strong, .devlog-list strong, .credits-list strong, .credits-thanks strong,
.friend-name strong, .score-number.px-mixed { font-weight: 400; }

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* ── Dark mode (default) ── */
:root {
  /* Pixel palette, generated from PX_PAL in img/px-sprite.js. Regenerate with the
     pxCssVars() helper; tools/pixel-lint.test.js fails if the two drift apart. */
  --px-ink0: #07070a;
  --px-ink1: #111117;
  --px-ink2: #1c1c26;
  --px-ink3: #2a2a38;
  --px-ink4: #3d3d50;
  --px-ash0: #55556b;
  --px-ash1: #7a7a92;
  --px-ash2: #a8a8bd;
  --px-ash3: #d6d6e2;
  --px-ash4: #f2f2f7;
  --px-gold0: #5c4a12;
  --px-gold1: #a8871f;
  --px-gold2: #e0b52e;
  --px-gold3: #ffd84d;
  --px-gold4: #ffefa8;
  --px-blu0: #12303f;
  --px-blu1: #1f5673;
  --px-blu2: #3d8fb5;
  --px-blu3: #76c2e5;
  --px-blu4: #b6e2f5;
  --px-org0: #3a1f14;
  --px-org1: #7a3a22;
  --px-org2: #b25b38;
  --px-org3: #d97b5a;
  --px-org4: #f0a882;
  --px-grn0: #12240d;
  --px-grn1: #2f5a1f;
  --px-grn2: #589a35;
  --px-grn3: #7ec850;
  --px-grn4: #b6e88c;
  --px-red0: #2a1010;
  --px-red1: #6b1f1f;
  --px-red2: #a83636;
  --px-red3: #e05555;
  --px-red4: #f2938c;
  --px-pur0: #221540;
  --px-pur1: #432a75;
  --px-pur2: #6b46b0;
  --px-pur3: #9b74e0;
  --px-pur4: #c9aef2;
  --px-skn0: #5a3326;
  --px-skn1: #8a5a3a;
  --px-skn2: #c98a5a;
  --px-skn3: #f0c8a0;
  --px-skn4: #ffe6cc;
  --bg: var(--px-ink1);
  --surface: var(--px-ink2);
  --surface2: var(--px-ink3);
  --border: var(--px-ink4);
  --border-strong: var(--px-ash0);
  --text: var(--px-ash4);
  --text-muted: var(--px-ash1);
  --text-hint: var(--px-ash0);
  --p1: var(--px-blu3);
  --p1-bg: var(--px-blu0);
  --p1-dark: var(--px-blu4);
  --p2: var(--px-org3);
  --p2-bg: var(--px-org0);
  --p2-dark: var(--px-org4);
  --success-bg: var(--px-grn0);
  --success: var(--px-grn3);
  --error-bg: var(--px-red0);
  --error: var(--px-red3);
  --info-bg: var(--px-blu0);
  --info: var(--px-blu3);
  --radius: 0;
  --radius-lg: 0;
  --px-shadow: var(--px-ink0);
  --px-light: var(--px-ash2);
  /* Area 1's sky, sampled straight out of the painted battlefield (img/scenes/lane_proto_3.jpg)
     at four heights plus its clouds, dark to light like every ramp above. It sits outside the
     pixel palette on purpose, exactly like the painting it is taken from: the palette's blues
     run to a desaturated teal (--px-blu2/3) and this is a saturated royal blue, so no swatch
     could stand in for it. Only Area 1 has a painted battlefield to sample. */
  --sky-proto0: #0a55cc;
  --sky-proto1: #2a7cea;
  --sky-proto2: #54a4f9;
  --sky-proto3: #78bdff;
  --sky-proto4: #e6f3fc;
}

/* ── Pixel utilities ────────────────────────────────────────────────────────
   .px-panel is the standard raised surface: hard 2px border, hard drop shadow, and
   4px corner notches cut with clip-path so the silhouette reads as a pixel bevel
   rather than a curve. */
.px-panel {
  background: var(--surface);
  border: 2px solid var(--border-strong);
  box-shadow: 4px 4px 0 var(--px-shadow);
  clip-path: polygon(4px 0, calc(100% - 4px) 0, 100% 4px, 100% calc(100% - 4px),
                     calc(100% - 4px) 100%, 4px 100%, 0 calc(100% - 4px), 0 4px);
}
/* A 4x4 ordered dither between two colours, the CSS twin of pxDither(). Set
   --px-a and --px-b on the element. */
.px-dither {
  background-image:
    linear-gradient(45deg, var(--px-b) 25%, transparent 25% 75%, var(--px-b) 75%),
    linear-gradient(45deg, var(--px-b) 25%, transparent 25% 75%, var(--px-b) 75%);
  background-size: 4px 4px;
  background-position: 0 0, 2px 2px;
  background-color: var(--px-a);
}
.px-title { font-family:'PressStart', monospace; }
img, .svg-icon svg { image-rendering: pixelated; }
/* Hand-painted character portraits (PNG_AVATARS, emitted by avatarFaceHTML) are the one
   exception: they're painted art whose source is far larger than the box it lands in, so
   nearest-neighbour throws away most of the pixels and the face comes back as noise. They
   get smooth scaling instead — the same exception the painted lane JPEGs take on the
   battle canvas. `contain` because the source is square and the box may not be. */
img.painted-face { image-rendering: auto; object-fit: contain; }

/* ── Themed scrollbars ── replace the default OS/browser bars everywhere with a slim,
   rounded, dark bar that picks up the player's blue accent on hover, so they read as part
   of the game UI. Components with their own bar rules (e.g. .ms-list) stay more specific and
   are unaffected. */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: rgba(0,0,0,0.22); border-radius: 0; }
::-webkit-scrollbar-thumb {
  background: var(--surface2);
  border: 2px solid transparent;
  background-clip: padding-box;
  border-radius: 0;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--p1);
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:active { background: var(--p1); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* ── Light mode override ── */
body {
  font-family: 'Silkscreen', monospace;
  font-size: 16px;
  image-rendering: pixelated;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 2rem 5vw;
  /* Kills the touch DOUBLE-TAP-to-zoom delay/gesture (pinch and pan still work). Tapping a
     letter tile twice in quick succession is normal play, and without this the browser reads
     it as "zoom in here" and throws the view off. */
  touch-action: manipulation;
}

#app {
  width: 90vw;
  max-width: 90vw;
}

/* Header */
.game-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1.5rem;
}
/* Headers shaped "left · ETYMA title · right" (the title is NOT the first child) must
   keep the title dead-centered even when the side button groups differ in width
   (e.g. admin-only buttons on the right of the Atlas). A 3-column grid with equal
   1fr sides guarantees the centre column is centred regardless of side content.
   Admin/game-over headers where the title IS the first child are left alone. */
.game-header:has(> .game-title:not(:first-child)) {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 8px;
}
.game-header:has(> .game-title:not(:first-child)) > :first-child { justify-self: start; }
.game-header:has(> .game-title:not(:first-child)) > .game-title { justify-self: center; text-align: center; }
.game-header:has(> .game-title:not(:first-child)) > :last-child { justify-self: end; }

/* Page headers, NOT the wordmark: admin.js reuses this for "User Management", "Export /
   Import Content" and a dozen more, so it stays on Silkscreen (Press Start at 24px would
   run those past the header). Weight 400 is the whole fix here, since 700 was the Bold
   face that drew ETYMA as ETYHA. Tracking goes to 0 for the same reason 10px did on the
   credits logo: -0.5px is a fraction of the 3px art unit at this size, so it landed every
   glyph between pixels. */
.game-title {
  font-size: 24px;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--px-gold3); /* brand yellow, matches the menu ETYMA logo */
}

.turn-badge {
  font-size: 16px;
  padding: 5px 14px;
  border-radius: 0;
  font-weight: 600;
  font-family: 'Silkscreen', monospace;
}
.p1-badge { background: var(--p1-bg); color: var(--p1-dark); }
.p2-badge { background: var(--p2-bg); color: var(--p2-dark); }

/* Scores */
.scores {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 1.5rem;
}
.score-card {
  background: var(--surface);
  border: 0.5px solid var(--border);
  border-radius: 0;
  padding: 12px 16px;
}
.score-label {
  font-size: 8px;
  font-family: 'Silkscreen', monospace;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 4px;
}
.score-number { font-size: 32px; font-weight: 700; }
.p1-score { color: var(--p1); }
.p2-score { color: var(--p2); }

/* Bank */
.bank-section { margin-bottom: 1.5rem; }
.section-label {
  font-size: 8px;
  font-family: 'Silkscreen', monospace;
  color: var(--px-ash4);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 8px;
}
.section-label span { text-transform: none; font-weight: 400; color: var(--px-grn4); }
.section-label.bank-label { text-align: center; }
.letter-bank { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }

.tile {
  width: 50px;
  height: 56px;
  border-radius: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Press Start 2P at 32px = a 28px cap on a 50x56 tile. Silkscreen bold at 24px was a
     15px cap, which is where the letter confusion came from. line-height 1 keeps the
     glyph optically centred; the score pip is absolutely positioned and unaffected. */
  font-family: 'PressStart', monospace;
  font-size: 32px;
  font-weight: 400;
  line-height: 1;
  cursor: pointer;
  border: 1px solid var(--border-strong);
  background: var(--p1-bg); /* a bit blue — matches the Word Display + lane word tiles */
  transition: transform 0.1s, background 0.1s, border-color 0.1s;
  user-select: none;
  position: relative;
  box-shadow: 0px 2px 0 rgba(0,0,0,0.08);
}
.tile:hover { background: var(--px-blu1); transform: translateY(-3px); }
.tile.selected { background: var(--px-blu1); border-color: var(--p1); color: var(--p1-dark); transform: translateY(-3px); }
/* Letter value. Press Start's digits are 7 art pixels tall where Silkscreen's are 5, so the
   pip gets 40% more height at the same 8px — the only lever available, since the letter
   already owns the middle of the tile. The old --text-hint (ash0, #55556b) on the dark blue
   tile fill was the other half of the problem: too dim to read at a glance. */
.tile-score {
  font-size: 8px;
  font-weight: 400;
  font-family: 'PressStart', monospace;
  line-height: 1;
  position: absolute;
  bottom: 4px;
  right: 5px;
  color: var(--px-ash2);
}
.tile.selected .tile-score { color: var(--p1); }

/* Word builder */
.word-display {
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 10px 14px;
  margin-bottom: 6px;
  min-height: 56px;
  display: flex;
  align-items: center;
  gap: 5px;
  flex-wrap: wrap;
  transition: background 0.12s, border-color 0.12s;
}
/* Mode tint so it's clear whether you're Creating / Transforming / Reforging */
.word-display.b-disp-create  { background: rgba(118,194,229,0.12);  border-color: var(--p1); }
.word-display.b-disp-transform   { background: rgba(217,123,90,0.14);  border-color: var(--p2); }
.word-display.b-disp-reforge { background: rgba(232,198,77,0.14);  border-color: var(--px-gold3); }
.b-armored-warn { font-size: 16px; color: var(--px-ash3); font-weight: 600; }
/* "⚔ Transforming WORD" reminder pinned to the right of the word-display while typing. */
.b-xform-tag { margin-left: 4px; align-self: center; color: var(--p2-dark); font-size: 16px; white-space: nowrap; }
.b-xform-tag.b-xform-reforge { color: var(--px-gold3); }
.b-xform-tag.b-xform-create { color: var(--p1-dark); }
/* Subtle "| Press ␣ Space to Clear" hint appended to the transform tag while typing. */
.b-xform-clear { color: var(--text-hint); font-weight: 400; margin-left: 2px; }
.word-tile {
  width: 40px;
  height: 46px;
  border-radius: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Same tile face as the bank, one step down the ladder for the smaller 40x46 box. */
  font-family: 'PressStart', monospace;
  font-size: 24px;
  font-weight: 400;
  line-height: 1;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  position: relative;
  box-shadow: 0px 2px 0 rgba(0,0,0,0.08);
}
.word-tile.from-target { background: var(--p2-bg); border-color: var(--p2); }
.word-tile.from-bank { background: var(--p1-bg); border-color: var(--p1); }
.word-tile-score {
  font-size: 8px;
  font-family: 'PressStart', monospace;
  line-height: 1;
  position: absolute;
  bottom: 2px;
  right: 3px;
  color: var(--px-ash2);
}
.word-placeholder { font-size: 16px; color: var(--text-hint); font-family: 'Silkscreen', monospace; font-style: italic; }
.word-score-hint { font-size: 16px; font-family: 'Silkscreen', monospace; color: var(--text-muted); margin-bottom: 10px; }

/* Input row */
.input-row { display: flex; gap: 8px; margin-bottom: 1rem; flex-wrap: wrap; }
.word-input {
  flex: 1;
  min-width: 160px;
  padding: 9px 13px;
  border-radius: 0;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
}
.word-input:focus { outline: none; border-color: var(--p1); box-shadow: 0px 0px 0 2px rgba(27,134,187,0.15); }

/* "forthosewhocomeafter" cheat: longest-transform clue strip below the typing field. */
.b-clue-hint { margin-top: 8px; padding: 8px 10px; border-radius: 0;
  background: rgba(255,210,74,0.12); border: 1px solid rgba(255,210,74,0.40);
  font-size: 16px; line-height: 1.45; color: var(--text); }
.b-clue-hint b { color: var(--px-gold3); }

/* Live score overlaid at the far-right INSIDE the battle typing field. */
.b-input-wrap { position: relative; flex: 1; min-width: 160px; display: flex; }
.b-input-wrap .word-input { width: 100%; padding-right: 96px; }
/* In-field overlay: the message sits before the score hint at the input's right edge. */
.b-input-overlay {
  position: absolute;
  right: 13px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: calc(100% - 26px);
  pointer-events: none;
}
.b-input-score {
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
  color: var(--text-muted);
  white-space: nowrap;
  flex: 0 0 auto;
}
/* Render the battle message as a plain inline hint here (no pill) so it reads like the score. */
.b-input-overlay #b-msg {
  margin: 0;
  padding: 0;
  background: none;
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.btn {
  padding: 9px 16px;
  border-radius: 0;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font-size: 16px;
  cursor: pointer;
  font-family: 'Silkscreen', monospace;
  transition: background 0.1s, transform 0.1s;
  white-space: nowrap;
}
.btn:hover { background: var(--surface2); }
.btn:active { transform: scale(0.97); }
.btn.primary { background: var(--px-gold3); color: var(--px-pur0); border-color: var(--px-gold3); }
.btn.primary:hover { background: var(--px-gold2); border-color: var(--px-gold2); }
.btn.danger { background: var(--p2); color: var(--px-ash4); border-color: var(--p2); }
.btn.danger:hover { background: var(--p2-dark); }
.btn:disabled { opacity: 0.4; cursor: default; }

/* ── Compact icon pill buttons (menu top row, Atlas header) ──────────────────
   Icon + label pills with a per-button accent tint (--mb-accent) on the icon;
   the accent also colors the border on hover. mb-on marks a live admin toggle. */
.menu-actions { display: inline-flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.menu-btn {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 16px; padding: 5px 12px;
  border-radius: 0;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  font-family: 'Silkscreen', monospace;
  white-space: nowrap;
  line-height: 1.3;
  transition: background 0.12s, border-color 0.12s, transform 0.12s, box-shadow 0.12s;
}
.menu-btn .svg-icon { width: 15px; height: 15px; vertical-align: -0.1em; color: var(--mb-accent, var(--text-muted)); transition: transform 0.12s; }
.menu-btn:hover { background: var(--surface2); border-color: var(--mb-accent, var(--border-strong)); transform: translateY(-1px); box-shadow: 0px 4px 0 rgba(0,0,0,0.3); }
.menu-btn:hover .svg-icon { transform: scale(1.15); }
.menu-btn:active { transform: translateY(0) scale(0.97); box-shadow: none; }
.menu-btn.mb-on { border-color: var(--mb-accent, var(--p1)); box-shadow: inset 0px 0px 0 2px var(--mb-accent, var(--p1)); }
.menu-btn.mb-blue   { --mb-accent: var(--px-blu3); }
.menu-btn.mb-green  { --mb-accent: var(--px-grn3); }
.menu-btn.mb-gold   { --mb-accent: var(--px-gold3); }
.menu-btn.mb-rose   { --mb-accent: var(--px-red3); }
.menu-btn.mb-violet { --mb-accent: var(--px-pur3); }
.menu-btn.mb-teal   { --mb-accent: var(--px-blu2); }
.menu-btn.mb-slate  { --mb-accent: var(--px-ash2); }

/* Mode tabs */
.mode-tabs { display: flex; gap: 6px; margin-bottom: 12px; }
.mode-tab {
  padding: 6px 16px;
  border-radius: 0;
  font-size: 16px;
  cursor: pointer;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text-muted);
  font-family: 'Silkscreen', monospace;
  transition: all 0.1s;
}
.mode-tab.active { background: var(--px-gold3); color: var(--px-pur0); border-color: var(--px-gold3); }

/* Transform section */
.transform-section {
  background: var(--p2-bg);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 10px 14px;
  margin-bottom: 10px;
}
.transform-label { font-size: 8px; font-family: 'Silkscreen', monospace; color: var(--text-muted); margin-bottom: 4px; text-transform: uppercase; letter-spacing: 0.5px; }
.transform-word { font-size: 16px; font-weight: 700; color: var(--p2-dark); }

/* Messages */
.message {
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
  padding: 8px 12px;
  border-radius: 0;
  margin-bottom: 10px;
}
.msg-error { background: var(--error-bg); color: var(--error); }
.msg-success { background: var(--success-bg); color: var(--success); }
.msg-info { background: var(--info-bg); color: var(--info); }

/* Words section */
.words-section { margin-bottom: 1.5rem; }
.words-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.col-header { font-size: 16px; font-weight: 600; font-family: 'Silkscreen', monospace; margin-bottom: 8px; }
.p1-header { color: var(--p1); }
.p2-header { color: var(--p2); }

.word-chip {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 10px;
  border-radius: 0;
  margin-bottom: 5px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid transparent;
  transition: border-color 0.1s;
}
.word-chip:hover { border-color: var(--border-strong); }
.p1-word { background: var(--p1-bg); color: var(--p1-dark); }
.p2-word { background: var(--p2-bg); color: var(--p2-dark); }
.word-chip.selected-transform { border-color: var(--px-red3) !important; outline: 1px solid var(--px-red3); }
.word-points { font-size: 16px; font-family: 'Silkscreen', monospace; opacity: 0.7; font-weight: 400; }
.no-words { font-size: 16px; font-family: 'Silkscreen', monospace; color: var(--text-hint); font-style: italic; }

/* Footer */
.divider { border: none; border-top: 0.5px solid var(--border); margin: 1rem 0; }
.skip-row { display: flex; align-items: center; gap: 10px; }
.skip-note { font-size: 16px; font-family: 'Silkscreen', monospace; color: var(--text-muted); }

/* Game over */
.game-over { text-align: center; padding: 2rem 1rem; }
.winner-text { font-size: 32px; font-weight: 700; margin-bottom: 0.5rem; }
.final-scores { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 1.5rem 0; }

/* ── Loading ── */
.loading-screen { display:flex; align-items:center; justify-content:center; min-height:70vh; }
.loading-box { display:flex; flex-direction:column; align-items:center; gap:1.6rem; }
/* The wordmark was the last thing still set in Silkscreen Bold, sitting directly above a row
   of Press Start tiles — so the two disagreed, and Silkscreen Bold's closed-up M read as an H
   at 48px. Press Start at 48 is exactly 6x the 8px design grid, so it stays crisp, and weight
   400 keeps the browser from synthesising a bold face that would smear it. */
.loading-logo {
  font-family:'PressStart', monospace;
  font-size:48px; font-weight:400; letter-spacing:2px; line-height:1; color:var(--px-gold3);
  /* Hard stepped gold drop, the pixel way to spell a glow — this was an 18px blur. Same
     two-step bevel the menu wordmark uses. */
  text-shadow:0 2px 0 var(--px-gold1), 0 4px 0 var(--px-gold0);
}
.loading-tiles { display:flex; gap:8px; }
.ld-tile {
  position:relative; width:44px; height:50px; border-radius: 0;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface); border:1px solid var(--border-strong);
  box-shadow: 0px 2px 0 rgba(0,0,0,0.25);
  /* Same tile face as the bank; 24px rather than 32 because the pip sits closer in on a
     44x50 box. See the font note at the top of this file. */
  color:var(--text); font-family: 'PressStart', monospace; font-size:24px; font-weight:400;
  animation: ld-pop 1.5s ease-in-out infinite;
  animation-delay: calc(var(--i) * 0.13s);
}
/* The letter is markup'd as <b>, whose UA font-weight:bolder resolves against the 400 above
   and lands on 700 — a synthesised bold that smears Press Start. The tile's own 400 does not
   reach it, so the child has to say so itself. */
.ld-tile b { font-weight: 400; }
.ld-tile i {
  position:absolute; bottom:3px; right:5px; font-size:8px; font-style:normal;
  font-weight:400; font-family: 'PressStart', monospace; line-height:1; color:var(--px-ash2);
}
@keyframes ld-pop {
  0%, 55%, 100% { transform:translateY(0); border-color:var(--border-strong); color:var(--text); }
  28% { transform:translateY(-9px); border-color:var(--px-gold3); color:var(--px-gold3); box-shadow: 0px 10px 0 rgba(0,0,0,0.3); }
}
.loading-bar { width:220px; height:5px; border-radius: 0; background:var(--surface2); overflow:hidden; }
.loading-bar span {
  display:block; height:100%; width:40%; border-radius: 0;
  background:var(--px-gold3);
  animation: ld-slide 1.2s ease-in-out infinite;
}
@keyframes ld-slide {
  0% { transform:translateX(-110%); }
  100% { transform:translateX(360%); }
}
.loading-text { font-family: 'Silkscreen', monospace; color:var(--text-muted); font-size:16px; letter-spacing:0.3px; }

/* ── Menu ── */
.menu-screen { padding:2rem 0; text-align:center; }
.menu-logo { font-size:32px; font-weight:700; letter-spacing:-1px; margin-bottom:0.5rem; }
.menu-tagline { font-size:16px; color:var(--px-ash4); font-family: 'Silkscreen', monospace; margin-bottom:2rem; }
/* Hidden admin tap-target — looks identical to surrounding text (no pointer/selection cues). */
.secret-tap { cursor: inherit; user-select: none; -webkit-tap-highlight-color: transparent; }
/* Inline themed SVG icon (from svg-assets.js); inherits text color via currentColor. */
.svg-icon { display: inline-block; width: 1.1em; height: 1.1em; line-height: 0; vertical-align: -0.16em; }
.svg-icon svg { width: 100%; height: 100%; display: block; }

/* Player auth (register / login) page */
.auth-wrap { max-width: 380px; margin: 3rem auto; text-align: center; }
.auth-title { font-size: 24px; font-weight: 800; margin-bottom: 6px; }
.auth-sub { font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted); margin-bottom: 1.5rem; }
.auth-form { display: flex; flex-direction: column; gap: 10px; }
.auth-toggle { font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted); margin-top: 1.25rem; }
.auth-legal { font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted); margin-top: 0.85rem; text-align: center; }
.auth-agree { display: flex; align-items: flex-start; gap: 8px; text-align: left; cursor: pointer; }
.auth-agree input[type="checkbox"] { flex: none; margin-top: 2px; width: 16px; height: 16px; cursor: pointer; }
.terms-body { font-family: 'Silkscreen', monospace; font-size: 16px; line-height: 1.55; color: var(--text-muted); }
.menu-legal { font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted); text-align: center; margin-top: 1.25rem; }
.menu-legal .link-btn { color: var(--text-muted); }
.link-btn { background: none; border: none; color: var(--px-gold3); cursor: pointer; font: inherit; text-decoration: underline; padding: 0; }
/* Account-type chips on the menu */
.acct-tag { font-size: 8px; font-weight: 700; font-family: 'Silkscreen', monospace; letter-spacing: 0.5px; padding: 2px 6px; border-radius: 0; background: rgba(0,0,0,0.12); color: var(--text-muted); vertical-align: middle; }
.acct-admin { background: rgba(226,75,74,0.18); color: var(--px-red2); }
.acct-editor { background: rgba(155,89,182,0.18); color: var(--px-pur2); }
.acct-player { background: rgba(58,166,87,0.18); color: var(--px-grn2); }

/* User avatar (menu chip + customizer) */
.menu-avatar { width: 30px; height: 30px; border-radius: 0; overflow: hidden; cursor: pointer; border: 2px solid var(--border-strong); display: inline-flex; flex: 0 0 auto; }
.menu-avatar svg { width: 100%; height: 100%; display: block; }
.av-wrap { max-width: 420px; margin: 2rem auto; display: flex; flex-direction: column; align-items: center; gap: 1.25rem; text-align: center; }
.av-preview { width: 150px; height: 150px; border-radius: 0; overflow: hidden; border: 3px solid var(--area-color, var(--px-blu3)); background: var(--surface); box-shadow: 0px 4px 0 rgba(0,0,0,0.22); }
.av-preview svg { width: 100%; height: 100%; display: block; }
.av-controls { width: 100%; display: flex; flex-direction: column; gap: 8px; }
.av-row { display: flex; align-items: center; gap: 10px; justify-content: center; }
.av-row-label { width: 64px; text-align: right; font: 600 13px sans-serif; color: var(--text-muted); }
.av-row-val { min-width: 44px; font: 600 13px sans-serif; }
.av-actions { display: flex; gap: 10px; justify-content: center; margin-top: 10px; flex-wrap: wrap; }

/* Atlas (compendium) */
.adv-map-footer { display: flex; justify-content: center; align-items: center; gap: 10px; margin-top: 14px; }
/* Footer pills sit next to the 15px star counter, so bump them up from the 12px default. */
.adv-map-footer .menu-btn { font-size: 16px; padding: 7px 15px; }
.adv-map-footer .menu-btn .svg-icon { width: 16px; height: 16px; }
/* Star counter is a regular footer .menu-btn (mb-gold); only the count stays gold. */
.adv-star-counter { color: var(--px-gold3); font-weight: 800; }
/* New-milestone notification: pulse the star counter yellow until it's clicked. */
.adv-star-counter.ms-notify {
  border-color: var(--px-gold3);
  animation: msNotifyPulse 1.1s ease-in-out infinite;
}
@keyframes msNotifyPulse {
  0%, 100% { box-shadow: 0px 0px 0 2px rgba(255,216,77,.0); }
  50%      { box-shadow: 0px 0px 0 2px rgba(255,216,77,.75); background: rgba(255,216,77,.18); }
}

/* Star-locked power-up tile — padlock + required-stars label */
.b-pu-locked { background: rgba(0,0,0,0.25); border-color: var(--border); color: var(--text-muted); cursor: help; }
.b-pu-locked .b-pu-price { color: var(--px-gold3); }

/* Star-milestone rewards overlay (reuses .gc-overlay/.gc-box) */
.gc-box.ms-box { max-width: 800px; text-align: left; }
.ms-head { font-size: 16px; font-weight: 800; color: var(--px-gold3); display: flex; align-items: center; justify-content: center; gap: 7px; }
.ms-box .ms-total {
  width: max-content; margin: 7px auto 0; display: flex; align-items: center; gap: 4px;
  font-size: 16px; font-weight: 800; color: var(--px-gold3);
  background: rgba(255,216,77,.10); border: 1px solid rgba(255,216,77,.35);
  border-radius: 0; padding: 3px 10px;
}
.ms-head .svg-icon, .ms-head svg, .ms-head img { width: 18px; height: 18px; }
.ms-box .ms-total .svg-icon, .ms-box .ms-total svg { width: 12px; height: 12px; }
/* Golden Close button, matching the milestone theme. */
.ms-box .gc-yes { background: var(--px-gold3); border-color: var(--px-gold3); color: var(--px-pur0); font-weight: 700; }
.ms-box .gc-yes:hover { background: var(--px-gold2); border-color: var(--px-gold2); }
.ms-progress {
  position: relative; height: 21px; margin: 10px 0 0; border-radius: 0; overflow: hidden;
  background: rgba(0,0,0,.35); border: 1px solid var(--border);
}
.ms-progress-fill {
  height: 100%; border-radius: 0;
  background: repeating-linear-gradient(45deg, var(--px-gold1) 0 10px, var(--px-gold3) 10px 20px);
  box-shadow: 0px 0px 0 2px rgba(255,216,77,.5);
}
.ms-sub {
  text-align: center; margin: 6px 0 12px;
  font-size: 16px; font-weight: 800; color: var(--px-ash4);
}
.ms-sub strong { font-weight: 800; }
.ms-list { max-height: 52vh; overflow-y: auto; display: flex; flex-direction: column; gap: 7px; padding-right: 6px; scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.18) rgba(0,0,0,.25); }
.ms-list::-webkit-scrollbar { width: 8px; }
.ms-list::-webkit-scrollbar-track { background: rgba(0,0,0,.25); border-radius: 0; }
.ms-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,.16); border-radius: 0; }
.ms-list::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.28); }
.ms-row {
  --ms-c: 200,205,215;
  display: flex; align-items: center; gap: 11px; padding: 8px 11px;
  border: 1px solid var(--border); border-radius: 0; background: rgba(0,0,0,0.14);
  opacity: .55;
}
.ms-row.ms-got { opacity: 1; border-color: rgba(255,216,77,.5); background: rgba(255,216,77,.10); }
.ms-row.ms-next { opacity: .95; border-color: rgba(255,216,77,.8); animation: msNextGlow 1.6s ease-in-out infinite; }
@keyframes msNextGlow {
  0%, 100% { box-shadow: 0px 0px 0 2px rgba(255,216,77,0); }
  50%      { box-shadow: 0px 0px 0 2px rgba(255,216,77,.45); }
}
/* Per-kind badge tint (r,g,b) */
.ms-k-hp      { --ms-c: 255,123,123; }
.ms-k-bank    { --ms-c: 129,199,132; }
.ms-k-wild    { --ms-c: 199,146,234; }
.ms-k-reroll  { --ms-c: 100,181,246; }
.ms-k-reforge { --ms-c: 255,169,77; }
.ms-k-powerup { --ms-c: 255,216,77; }
.ms-k-mystery { --ms-c: 154,160,178; }
.ms-at {
  flex: none; display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  min-width: 46px; font-weight: 800; font-size: 16px; color: var(--px-gold3);
  background: rgba(255,216,77,.10); border: 1px solid rgba(255,216,77,.35);
  border-radius: 0; padding: 3px 8px;
}
.ms-row:not(.ms-got) .ms-at { color: var(--text-muted); background: rgba(0,0,0,.2); border-color: var(--border); }
.ms-at .svg-icon, .ms-at svg, .ms-at img { width: 12px; height: 12px; }
.ms-badge {
  flex: none; width: 34px; height: 34px; border-radius: 0;
  display: inline-flex; align-items: center; justify-content: center;
  color: rgb(var(--ms-c)); background: rgba(var(--ms-c), .13); border: 1px solid rgba(var(--ms-c), .35);
}
.ms-badge .svg-icon, .ms-badge svg { width: 19px; height: 19px; }
.ms-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ms-text strong { font-size: 16px; }
.ms-text span { font-size: 8px; color: var(--text-muted); line-height: 1.35; }
.ms-check {
  flex: none; width: 20px; height: 20px; border-radius: 0;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--px-ink2); background: var(--px-gold3);
}
.ms-check .svg-icon, .ms-check svg { width: 12px; height: 12px; }
.ms-nexttag {
  flex: none; font-size: 8px; font-weight: 800; letter-spacing: .08em; color: var(--px-gold3);
  border: 1px solid rgba(255,216,77,.6); border-radius: 0; padding: 2px 5px;
}
.atlas-wrap { max-width: 920px; margin: 0 auto; padding: 0.5rem 1rem 2.5rem; }
/* The Atlas itself is a card grid, not a prose column, so it gets its own (wider) width:
   five cards plus their gaps and the wrap padding. Scoped to .atlas-page so the pages
   that BORROW .atlas-wrap for prose (Devlog, Credits, Terms, Privacy) keep 920. */
.atlas-wrap.atlas-page { max-width: 1240px; }
.atlas-intro { font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted); text-align: center; margin: 0.5rem 0 1.5rem; }
.atlas-section { margin-bottom: 1.75rem; }
.atlas-section-head { font-size: 16px; font-weight: 800; margin-bottom: 0.75rem; display: flex; align-items: center; gap: 8px; }
.atlas-count { font-size: 16px; font-weight: 700; font-family: 'Silkscreen', monospace; color: var(--text-muted); background: rgba(0,0,0,0.1); border-radius: 0; padding: 2px 8px; }
/* Flex (not grid) so each row is centered — a lone last card sits in the middle. The
   column COUNT is the basis against the wrap width, not a track count: 5 fit a full-width
   .atlas-page row (5x220 + 4x10 = 1140 into 1208 of content) and 6 can't (1370), and the
   basis is the reason a narrower window steps down to 4/3/2 instead of overflowing. */
.atlas-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.atlas-card { flex: 1 1 220px; max-width: 320px; display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 0; background: var(--surface); text-align: left; }
.atlas-card.locked { opacity: 0.5; }
.atlas-icon { flex: 0 0 34px; width: 34px; height: 34px; display: flex; align-items: center; justify-content: center; font-size: 24px; border-radius: 0; background: rgba(0,0,0,0.08); }
.atlas-icon .svg-icon { width: 1.35em; height: 1.35em; }
/* A painted foe head is a bare <img>, so it needs the size the .svg-icon wrapper gave. */
.atlas-icon .painted-face { width: 1.35em; height: 1.35em; }
.atlas-info { display: flex; flex-direction: column; min-width: 0; }
.atlas-info strong { font-size: 16px; }
.atlas-info span { font-size: 8px; font-family: 'Silkscreen', monospace; color: var(--text-muted); line-height: 1.3; }
/* Devlog page (Atlas → 📜 Devlog). The whole page is Press Start, not Silkscreen: it is
   prose, and prose in a caps-only face is a wall of shouting (see the mixed-case note at
   the top). Everything here therefore runs ~a third wider than the same string in
   Silkscreen, and the extra leading is for the taller ink.
   The entry text is then scaled to 70%, since Press Start at a full 16px is a lot of ink
   for paragraphs of prose. It has to be a container `zoom` rather than a font-size,
   because 30% off 16px is 11.2px and the ladder only has rungs at 8 and 16 (the on-grid
   option, 8px, would be half size, not 30% off). Same tool as .battle-bottom's zoom:
   0.95, and the same trade: 16px is exactly 2 CSS px per art pixel and 11.2 is 1.4, so
   the glyph stems are no longer all one width. Zoom expands the box before scaling back,
   so the list still fills the column: ~81 characters a line instead of ~57. */
.devlog-list { list-style: none; padding: 0; margin: 4px 0 0; font-family: 'PressStart', monospace; zoom: 0.7; }
.devlog-list li { position: relative; font-size: 16px; line-height: 1.6; color: var(--text); padding: 8px 0 8px 20px; border-top: 1px solid var(--border); }
.devlog-list li:first-child { border-top: none; }
.devlog-list li::before { content: '▸'; position: absolute; left: 3px; top: 8px; color: var(--p1); }
/* Date pagination: one day per page, with a chip index of every day. */
.devlog-pager { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 0 0 16px; }
.devlog-pager .btn { font-size: 16px; padding: 6px 12px; }
.devlog-at { display: flex; flex-direction: column; align-items: center; gap: 2px; font-family: 'PressStart', monospace; font-size: 16px; color: var(--text); text-align: center; min-width: 0; }
.devlog-count { font-size: 8px; color: var(--text-muted); }
.devlog-index { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; margin: 0 0 24px; }
.devlog-chip { font-family: 'PressStart', monospace; font-size: 8px; padding: 4px 6px; border-radius: 0;
  border: 1px solid var(--border); background: var(--surface); color: var(--text-muted); cursor: pointer; }
.devlog-chip:hover { background: var(--surface2); color: var(--text); }
.devlog-chip.on { background: var(--px-gold3); border-color: var(--px-gold3); color: var(--px-pur0); }
/* The Atlas furniture these two pages borrow (dates, headings, the intro line) carries
   prose too, so it follows the page rather than the Atlas. Scoped, so the Atlas itself —
   which is labels, not sentences — keeps Silkscreen. */
.devlog-wrap .atlas-intro, .devlog-wrap .atlas-section-head, .devlog-wrap .atlas-count,
.credits-wrap .atlas-intro, .credits-wrap .atlas-section-head {
  font-family: 'PressStart', monospace;
}
@media (max-width: 600px) { .atlas-card { flex-basis: 100%; max-width: none; } }

/* Credits page (menu / Atlas → 💛 Credits) */
/* 720px, not 560: the rows are Press Start now (people's names have to keep their case —
   "MaRinne" is not "MARINNE"), and the widest row, "Game Design & Programming" against
   "Eunomatopoeia", measures ~620px at 16px in a 1em cell. At 560 it wrapped. */
.credits-wrap { max-width: 720px; text-align: center; }
/* The wordmark, in the same treatment as .loading-logo and .menu-logo. It used to inherit
   Silkscreen from body at weight 900, which resolved to the Bold face whose M is an H (see
   the font note at the top of this file), and the 12px gold blur then melted what was left
   of the counters. Tracking is 0.125em because that is exactly one art pixel at ANY size;
   the old 10px sat between pixels at 48px, where the art unit is 6px. */
.credits-logo {
  font-family: 'PressStart', monospace;
  font-size: 48px; font-weight: 400; letter-spacing: 0.125em;
  color: var(--px-gold3);
  text-shadow: 0 2px 0 var(--px-gold1), 0 4px 0 var(--px-gold0);
  margin: 0.5rem 0 0.25rem;
}
.credits-version { font-family: 'Silkscreen', monospace; font-size: 16px; font-weight: 700; color: var(--text-muted); margin: -0.25rem 0 1.25rem; letter-spacing: 1px; }
.credits-list { font-family: 'PressStart', monospace; }
.credits-row {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 9px 2px; border-top: 1px solid var(--border);
}
.credits-row:first-child { border-top: none; }
.credits-role { font-size: 16px; color: var(--text-muted); text-align: left; }
.credits-name { font-size: 16px; font-weight: 400; color: var(--text); text-align: right; }
.credits-name a { color: var(--px-gold3); text-decoration: none; }
.credits-name a:hover { text-decoration: underline; }
.credits-thanks { font-family: 'PressStart', monospace; font-size: 16px; line-height: 1.6; color: var(--text); margin: 4px 0 0; }
.credits-thanks strong { color: var(--px-gold3); }
.credits-cats { display: flex; justify-content: center; gap: 12px; margin-top: 10px; }
.credit-cat { width: 34px; height: 32px; }

/* ── Co-op lobby ───────────────────────────────────────────────────────────── */
.pvp-home-cards { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }
.pvp-home-cards .menu-card { flex: 1 1 220px; max-width: 280px; }
.pvp-panel { max-width: 460px; margin: 0 auto; background: var(--surface); border: 1px solid var(--border);
  border-radius: 0; padding: 1.25rem 1.4rem; }
.pvp-panel-title { font-size: 24px; margin: 0 0 1rem; text-align: center; }
.pvp-panel-actions { display: flex; gap: 10px; justify-content: space-between; margin-top: 1.2rem; }
.coop-field { margin-bottom: 1rem; }
.coop-field > label { display: block; font: 700 12px sans-serif; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .5px; margin-bottom: 6px; }
.coop-select { width: 100%; padding: 8px 10px; margin-bottom: 6px; border-radius: 0;
  border: 1px solid var(--border-strong); background: var(--p1-bg); color: var(--text); font-size: 16px; }
.coop-hint { font: 12px sans-serif; color: var(--text-muted); margin-top: 2px; }
.coop-count-row { display: flex; gap: 8px; }
.coop-count-chip { width: 44px; height: 40px; border-radius: 0; border: 1px solid var(--border-strong);
  background: var(--p1-bg); color: var(--text); font-size: 16px; font-weight: 800; cursor: pointer; }
.coop-count-chip.on { background: rgba(255,216,77,.15); color: var(--px-gold3); border-color: var(--px-gold3); }
.coop-role-row { display: flex; gap: 8px; flex-wrap: wrap; }
.coop-role-chip { flex: 1 1 120px; text-align: left; padding: 8px 10px; border-radius: 0;
  border: 1px solid var(--border-strong); background: var(--p1-bg); color: var(--text); cursor: pointer; }
.coop-role-chip.on { border-color: var(--px-gold3); box-shadow: inset 0px 0px 0 2px var(--px-gold3); }
.coop-role-ico { font-size: 16px; margin-right: 4px; }
.coop-role-name { font-weight: 800; }
.coop-role-desc { display: block; font: 11px sans-serif; color: var(--text-muted); margin-top: 3px; }
.coop-seat-list { display: flex; flex-direction: column; gap: 8px; margin: 1rem 0; }
.coop-seat { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-radius: 0;
  background: var(--p1-bg); border: 1px solid var(--border); }
.coop-seat.ready { border-color: var(--px-grn3); box-shadow: inset 0px 0px 0 2px var(--px-grn3); }
.coop-seat-empty { opacity: .55; font-style: italic; }
.coop-seat-name { font-weight: 700; flex: 1; }
.coop-seat-role { font: 12px sans-serif; color: var(--text-muted); }
.coop-seat-ready { font: 700 12px sans-serif; color: var(--px-grn3); }
.pvp-code-box { display: flex; align-items: center; gap: 10px; justify-content: center; margin-bottom: .5rem; }
.pvp-code-label { font: 700 12px sans-serif; color: var(--text-muted); text-transform: uppercase; }
.pvp-code-value { font: 800 22px monospace; letter-spacing: 3px; color: var(--px-gold3); }
.pvp-code-input { width: 100%; padding: 10px 12px; border-radius: 0; border: 1px solid var(--border-strong);
  background: var(--p1-bg); color: var(--text); font: 800 18px monospace; letter-spacing: 3px; text-align: center; }
.credits-foot { font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted); margin-top: 1.5rem; }

/* "Word lands → flies into the Reforge pool" flourish */
.b-reforge-fly {
  position: fixed; z-index: 600; pointer-events: none;
  margin-left: -20px; margin-top: -12px;       /* center on the start point */
  font: 700 13px 'Silkscreen', monospace;
  color: var(--px-ash4); background: var(--area-color, var(--px-blu3));
  padding: 4px 9px; border-radius: 0; white-space: nowrap;
  box-shadow: 0px 2px 0 rgba(0,0,0,0.4);
  transform: translate(0, 0) scale(1); opacity: 1;
  transition: transform 0.5s cubic-bezier(.32,.72,.3,1), opacity 0.5s ease-in;
  will-change: transform, opacity;
}
/* "Transform → word flies to your launch lane" flourish */
.b-transform-fly {
  position: fixed; z-index: 600; pointer-events: none;
  margin-left: -20px; margin-top: -12px;
  font: 700 13px 'Silkscreen', monospace;
  color: var(--px-org0); background: var(--px-gold2);        /* transform / orange */
  padding: 4px 9px; border-radius: 0; white-space: nowrap;
  box-shadow: 0px 2px 0 rgba(0,0,0,0.4);
  transform: translate(0, 0) scale(1); opacity: 1;
  transition: transform 0.46s cubic-bezier(.32,.72,.3,1), opacity 0.46s ease-in;
  will-change: transform, opacity;
}
.b-reforge-flash { animation: reforgeFlash 0.42s ease; border-radius: 0; }
@keyframes reforgeFlash {
  0%   { box-shadow: 0px 0px 0 2px rgba(240,200,64,0); }
  35%  { box-shadow: 0px 0px 0 2px rgba(240,200,64,0.65); background: rgba(240,200,64,0.12); }
  100% { box-shadow: 0px 0px 0 2px rgba(240,200,64,0); background: transparent; }
}
.menu-cards { display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:14px; text-align:left; }
.menu-card { background:var(--surface); border:1px solid var(--border); border-radius: 0; padding:1.5rem; cursor:pointer; transition:box-shadow 0.15s, transform 0.15s; }
.menu-card:hover { box-shadow: 0px 4px 0 rgba(0,0,0,0.1); transform:translateY(-2px); }
.menu-card-icon { font-size:32px; margin-bottom:0.5rem; }
.menu-card-title { font-size:24px; font-weight:700; margin-bottom:0.4rem; }
.menu-card-desc { font-size:16px; font-family: 'Silkscreen', monospace; color:var(--text-muted); line-height:1.5; }

/* ── PvE HUD ── */
.pve-hud { display:flex; justify-content:space-between; align-items:center; margin-bottom:0.75rem; }
.pve-hud-label { font-size:8px; font-family: 'Silkscreen', monospace; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.5px; }
.pve-hud-value { font-size:24px; font-weight:700; }
.pve-hud-center { text-align:center; }
.pve-hud-right { text-align:right; }
.pve-timer { font-size:24px; font-weight:700; font-family:monospace; letter-spacing:-1px; }
.pve-lives { font-size:24px; }
.life-icon { margin-left:2px; }

/* ── PvE Progress dots ── */
.pve-progress { display:flex; gap:5px; margin-bottom:1.25rem; justify-content:center; }
.prog-dot { width:22px; height:6px; border-radius: 0; background:var(--border); transition:background 0.2s; }
.prog-done { background:var(--p1); }
.prog-current { background:var(--p2); }

/* ── PvE Challenge ── */
.pve-challenge { background:var(--surface); border:1px solid var(--border); border-radius: 0; padding:1rem 1.25rem; }
.pve-challenge-label { font-size:8px; font-family: 'Silkscreen', monospace; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.5px; margin-bottom:8px; }
.pve-target-tiles { display:flex; gap:5px; flex-wrap:wrap; }

/* ── PvE Results ── */
.pve-over-stats { display:grid; grid-template-columns:repeat(4,1fr); gap:8px; margin:1.25rem 0; }
.pve-stat { background:var(--surface2); border-radius: 0; padding:10px 8px; text-align:center; }
.pve-stat-label { font-size:8px; font-family: 'Silkscreen', monospace; color:var(--text-muted); text-transform:uppercase; letter-spacing:0.4px; margin-bottom:4px; }
.pve-stat-value { font-size:16px; font-weight:700; }
.pve-results { text-align:left; border:1px solid var(--border); border-radius: 0; overflow:hidden; }
.pve-result-row { display:flex; align-items:center; gap:8px; padding:8px 12px; font-family: 'Silkscreen', monospace; font-size:16px; border-bottom:1px solid var(--border); }
.pve-result-row:last-child { border-bottom:none; }
.pve-result-done { background:var(--success-bg); }
.pve-result-skipped { background:var(--error-bg); }
.pve-result-arrow { font-size:16px; width:14px; flex-shrink:0; }
.pve-result-original { font-weight:700; min-width:60px; }
.pve-result-sep { color:var(--text-muted); }
.pve-result-answer { font-weight:600; flex:1; }
.pve-result-pts { font-size:16px; color:var(--success); font-weight:600; }

@media (max-width: 500px) {
  .menu-cards { grid-template-columns:1fr; }
  .pve-over-stats { grid-template-columns:repeat(2,1fr); }
}

/* ── PvE countdown timer urgent state ── */
.pve-timer-urgent {
  color: var(--px-red2);
  animation: pulse-urgent 0.6s ease-in-out infinite alternate;
}

@keyframes pulse-urgent {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0.7; transform: scale(1.08); }
}

.pve-result-example {
  font-size: 8px;
  font-style: italic;
  color: var(--text-muted);
  margin-left: auto;
  white-space: nowrap;
}

/* ── Difficulty buttons on menu ── */
.diff-btns { display:flex; gap:6px; margin-top:1rem; }
.diff-btn { flex:1; font-size:16px; padding:8px 6px; font-weight:600; }
.diff-easy  { background:var(--px-grn4); color:var(--px-grn1); border-color:var(--px-grn1); }
.diff-easy:hover  { background:var(--px-grn4); }
.diff-med   { background:var(--px-gold4); color:var(--px-gold0); border-color:var(--px-gold0); }
.diff-med:hover   { background:var(--px-gold4); }
.diff-hard  { background:var(--px-red4); color:var(--px-red2); border-color:var(--px-red2); }
.diff-hard:hover  { background:var(--px-red4); }

/* ── Difficulty badge in HUD / game over ── */
.pve-diff-badge { display:inline-block; font-size:8px; font-family: 'Silkscreen', monospace; font-weight:600; padding:2px 8px; border-radius: 0; margin-top:3px; }
.pve-diff-easy   { background:var(--px-grn4); color:var(--px-grn1); }
.pve-diff-medium { background:var(--px-gold4); color:var(--px-gold0); }
.pve-diff-hard   { background:var(--px-red4); color:var(--px-red2); }

/* ── Word of the Day ── */
.wotd-badge {
  display:inline-block;
  font-size:8px; font-family:monospace; font-weight:700;
  letter-spacing:0.5px;
  padding:2px 8px; border-radius: 0; margin-top:3px;
  background:var(--px-gold0); color:var(--px-gold3); border:1px solid var(--px-gold2);
}
.wotd-menu {
  font-family: 'Silkscreen', monospace; font-size:16px;
  color:var(--px-gold3);
  background:rgba(240,200,60,0.10);
  border:1px solid rgba(240,200,60,0.35);
  border-radius: 0;
  padding:6px 12px; margin:0 auto 0.5rem;
  display:inline-block;
}
.wotd-menu strong { font-family:monospace; letter-spacing:1.5px; color:white; }

/* ── Bank tile: consumed by current input ── */
@keyframes tile-used-pulse {
  0%, 100% { opacity: 0.20; }
  50%      { opacity: 0.42; }
}
.tile.tile-used {
  opacity: 0.25;
  pointer-events: none;
  transform: scale(0.9);
  box-shadow: none;
  animation: tile-used-pulse 1.1s ease-in-out infinite;
}

/* ── Bank tile: freshly drawn (drops/pops in after a word is played) ── */
@keyframes tile-enter {
  0%   { transform: translateY(-16px) scale(0.4); opacity: 0; }
  55%  { transform: translateY(0) scale(1.12);    opacity: 1; }
  100% { transform: translateY(0) scale(1);       opacity: 1; }
}
.tile.tile-enter { animation: tile-enter 0.34s cubic-bezier(0.2, 0.85, 0.3, 1.2); }

/* ── Input tile: letter not available in pool ── */
.word-tile.from-invalid {
  background: var(--px-ink0);
  border-color: var(--px-ink0);
  color: var(--px-ash4);
}
.word-tile.from-invalid .word-tile-score { color: var(--px-ash4); }



/* ── Star / wildcard bank tile ── */
.tile.tile-star {
  background: var(--px-gold4);
  border-color: var(--px-gold2);
  color: var(--px-gold1);
  font-size: 32px;
}
.tile.tile-star .tile-score { color: var(--px-gold1); }
.tile.tile-star:hover { background: var(--px-gold4); transform: translateY(-3px); }

/* ── Augmented letters (power-up bank tiles) ─────────────────────────────────
   Each effect tints the tile in its theme while keeping the LETTER high-contrast
   (kept dark over a light wash + a white text-shadow). A small effect glyph sits
   in the top-left corner; a soft pulse draws the eye. */
.tile.tile-aug {
  /* 400, not a heavier weight: Press Start 2P has no bold face and a synthesised one
     smears the pixel grid. The tint + white shadow already carry the emphasis. */
  font-weight: 400;
  color: var(--px-ink2);
  text-shadow: 0 1px 0 rgba(255,255,255,0.8);
  border-width: 2px;
  animation: aug-pulse 1.8s ease-in-out infinite;
}
.tile.tile-aug .tile-aug-glyph {
  position: absolute;
  top: 1px;
  left: 4px;
  font-size: 16px;
  line-height: 1;
  filter: drop-shadow(0 1px 1px rgba(0,0,0,0.35));
  pointer-events: none;
}
@keyframes aug-pulse {
  0%, 100% { box-shadow: 0px 2px 0 rgba(0,0,0,0.08), 0px 0px 0 2px rgba(0,0,0,0); }
  50%      { box-shadow: 0px 2px 0 rgba(0,0,0,0.08), 0px 0px 0 2px var(--aug-glow, rgba(255,255,255,0.6)); }
}
.tile.tile-aug-freeze { background: var(--px-blu4); border-color: var(--px-blu3); --aug-glow: rgba(95,176,255,0.85); }
.tile.tile-aug-slow   { background: var(--px-gold4); border-color: var(--px-gold2); --aug-glow: rgba(230,162,60,0.8); }
.tile.tile-aug-flame  { background: var(--px-gold4); border-color: var(--px-org3); --aug-glow: rgba(255,106,51,0.85); }
.tile.tile-aug-bomb   { background: var(--px-ash4); border-color: var(--px-red3); --aug-glow: rgba(226,75,74,0.8); }
.tile.tile-aug-heal   { background: var(--px-grn4); border-color: var(--px-grn3); --aug-glow: rgba(63,191,94,0.8); }
.tile.tile-aug-shield { background: var(--px-blu4); border-color: var(--px-blu2); --aug-glow: rgba(55,211,211,0.8); }
.tile.tile-aug-vacuum { background: var(--px-blu4); border-color: var(--px-blu2); --aug-glow: rgba(37,184,154,0.8); }
.tile.tile-aug-pierce { background: var(--px-pur4); border-color: var(--px-pur3); --aug-glow: rgba(154,106,255,0.85); }
.tile.tile-aug-double { background: var(--px-gold4); border-color: var(--px-gold3); --aug-glow: rgba(240,192,64,0.9); }

/* Live "this word will trigger" chip in the word builder */
.b-aug-pending {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: 0;
  font-size: 16px;
  background: rgba(0,0,0,0.06);
  border: 1px solid var(--border);
  vertical-align: middle;
}

/* ── Augmented-letters legend popup ── */
.battle-footer-btn { margin-top: 6px; font-size: 16px; padding: 4px 12px; }
.b-aug-legend-overlay {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(0,0,0,0.55);
  align-items: center; justify-content: center; /* display toggled inline */
}
.b-aug-legend-card {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 0;
  padding: 20px 22px;
  max-width: 480px; width: 92%;
  max-height: 82vh; overflow-y: auto;
  box-shadow: 0px 18px 0 rgba(0,0,0,0.4);
}
.b-aug-legend-title { margin: 0 0 6px; font-size: 16px; }
.b-aug-legend-sub { margin: 0 0 14px; font-size: 16px; color: var(--text-muted); line-height: 1.45; }
.b-aug-legend-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.b-aug-legend-item { display: flex; align-items: center; gap: 12px; }
.b-aug-legend-glyph {
  flex: 0 0 auto;
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  font-size: 16px;
  border-radius: 0;
  border: 2px solid var(--border-strong);
  background: var(--surface2);
}
.b-aug-legend-text { display: flex; flex-direction: column; }
.b-aug-legend-text strong { font-size: 16px; color: var(--text); }
.b-aug-legend-text span { font-size: 16px; color: var(--text-muted); }
/* swatch colors mirror the in-game tiles */
.b-aug-sw-freeze { background: var(--px-blu4); border-color:var(--px-blu3); }
.b-aug-sw-slow   { background: var(--px-gold4); border-color:var(--px-gold2); }
.b-aug-sw-flame  { background: var(--px-gold4); border-color:var(--px-org3); }
.b-aug-sw-bomb   { background: var(--px-ash4); border-color:var(--px-red3); }
.b-aug-sw-heal   { background: var(--px-grn4); border-color:var(--px-grn3); }
.b-aug-sw-shield { background: var(--px-blu4); border-color:var(--px-blu2); }
.b-aug-sw-vacuum { background: var(--px-blu4); border-color:var(--px-blu2); }
.b-aug-sw-pierce { background: var(--px-pur4); border-color:var(--px-pur3); }
.b-aug-sw-double { background: var(--px-gold4); border-color:var(--px-gold3); }
.b-aug-sw-rally { background: var(--px-gold4); border-color:var(--px-gold3); }

/* ── Buyable power-up bar (below the Letter Bank, Adventure) ── */
.b-pu-points { font-weight: 800; color: var(--px-gold3); }
.b-mouse-ico { vertical-align: -3px; margin: 0 1px; opacity: .85; }

/* Custom PvP song box (lobby) */
.pvp-song-box { margin: 14px auto 0; max-width: 640px; width: 100%; border: 1px solid var(--border); border-radius: 0; padding: 10px 14px; background: var(--surface); font-family: 'Silkscreen', monospace; }
.pvp-song-row { display: flex; gap: 8px; align-items: center; margin: 8px 0 4px; }
.pvp-song-pickhint { font-size: 8px; color: var(--text-muted); margin: 0 0 8px; }
.pvp-song-name { width: 100%; box-sizing: border-box; padding: 7px 10px; margin-bottom: 6px; border-radius: 0; border: 1px solid var(--border-strong); background: var(--bg); color: var(--text); font: 600 13px system-ui, sans-serif; }
/* Home/card lobby modes use the full menu width so cards lay out in 3 columns.
   Two classes for specificity so it beats the base .pvp-lobby-wrap max-width. */
.pvp-lobby-wrap.pvp-lobby-home { max-width: 940px; }
.pvp-song-box > summary { cursor: pointer; font-weight: 700; font-size: 16px; }
.pvp-song-box > summary span { color: var(--text-muted); font-weight: 400; font-size: 16px; }
.pvp-song-hint { font-size: 16px; color: var(--text-muted); line-height: 1.5; margin: 8px 0; }
.pvp-song-hint code { background: rgba(0,0,0,0.25); padding: 1px 4px; border-radius: 0; font-size: 8px; }
/* Beginner's format guide inside the custom-music panel (pvpSongGuideHTML). */
.pvp-song-guide { border: 1px solid var(--border); border-radius: 0; padding: 6px 10px; margin: 8px 0; background: rgba(0,0,0,0.12); }
.pvp-song-guide > summary { cursor: pointer; font-size: 16px; font-weight: 700; }
.pvp-song-guide p, .pvp-song-guide ol { font-size: 16px; color: var(--text-muted); line-height: 1.5; margin: 8px 0; }
.pvp-song-guide ol { padding-left: 18px; }
.pvp-song-guide li { margin-bottom: 4px; }
.pvp-song-guide code { background: rgba(0,0,0,0.25); padding: 1px 4px; border-radius: 0; font-size: 8px; font-family: 'Silkscreen', monospace; }
.pvp-song-legend { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; align-items: baseline; font-size: 16px; color: var(--text-muted); line-height: 1.45; margin: 8px 0; }
.pvp-song-legend .pvp-song-leg-s { font-family: 'Silkscreen', monospace; color: var(--text); font-weight: 700; white-space: nowrap; }
.pvp-song-eg { font-family: 'Silkscreen', monospace; font-size: 8px; line-height: 1.4; color: var(--text); background: rgba(0,0,0,0.25); border-radius: 0; padding: 8px 10px; margin: 8px 0; overflow-x: auto; }
.pvp-song-input { width: 100%; resize: vertical; font-family: 'Silkscreen', monospace; font-size: 16px; line-height: 1.35; padding: 8px; border-radius: 0; border: 1px solid var(--border-strong); background: var(--bg); color: var(--text); }
.pvp-song-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.pvp-song-msg { font-size: 16px; margin-top: 8px; min-height: 14px; color: var(--text-muted); }
.pvp-song-msg.ok { color: var(--px-grn3); }
.pvp-song-msg.err { color: var(--px-red3); }
.b-free-pu-btn { flex: 1; font-size: 16px; padding: 4px 10px; font-weight: 700; }
.b-free-pu-btn.on { background: var(--px-grn3); border-color: var(--px-grn2); color: var(--px-grn0); }
.b-pu-bar { display: flex; flex-wrap: wrap; gap: 6px; min-height: 44px; margin-top: 4px; }
.b-pu-tile {
  position: relative; width: 46px; min-height: 42px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  border-radius: 0; border: 2px solid var(--border-strong);
  cursor: pointer; user-select: none; color: var(--px-ink1);
  transition: transform .08s, box-shadow .08s, opacity .12s;
}
.b-pu-tile:hover { transform: translateY(-2px); }
.b-pu-glyph { font-size: 16px; line-height: 1; }
.b-pu-price { font-size: 8px; font-weight: 800; margin-top: 2px; }
.b-pu-armed { box-shadow: 0px 0px 0 2px var(--px-gold3), 0px 0px 0 2px var(--px-gold3); }
.b-pu-disabled { opacity: .42; filter: grayscale(.55); cursor: not-allowed; }
/* currently-selected (keyboard cursor) — bright yellow glow, always clear */
.b-pu-sel { outline: 2px solid var(--px-gold3); outline-offset: 2px; opacity: 1; z-index: 1; }
.b-pu-sel.b-pu-armed { box-shadow: 0px 0px 0 2px var(--px-gold3); }
/* Can't afford it (or on cooldown) → no yellow highlight; stays dimmed like the rest. */
.b-pu-sel.b-pu-disabled { outline: none; opacity: .42; }
/* typing field glows the equipped power-up's color while one is armed */
.word-input.b-armed-glow {
  border-color: var(--armed-c, var(--px-gold3)) !important;
  outline: none;
  animation: armedGlowPulse 1.1s ease-in-out infinite;
}
@keyframes armedGlowPulse {
  0%, 100% { box-shadow: 0px 0px 0 2px var(--armed-c, var(--px-gold3)), 0px 0px 0 2px var(--armed-c, var(--px-gold3)); }
  50%      { box-shadow: 0px 0px 0 2px var(--armed-c, var(--px-gold3)), 0px 0px 0 2px var(--armed-c, var(--px-gold3)); }
}
.b-pu-cd {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: rgba(10,12,18,.62); color: var(--px-ash4); font-weight: 800; font-size: 16px; border-radius: 0;
}
/* admin price/cooldown rows in the legend popup */
.b-pu-admin-row { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; flex-wrap: wrap; }
.b-pu-admin-name { min-width: 104px; font-size: 16px; font-weight: 700; }
.b-pu-admin-row label { font-size: 8px; color: var(--text-muted); display: flex; align-items: center; gap: 4px; }
.b-pu-admin-row input { width: 64px; }

/* ── Pre-game briefing popup ── */
.b-start-overlay {
  position: fixed; inset: 0; z-index: 1100;
  background: rgba(0,0,0,0.62);
  display: flex; align-items: center; justify-content: center;
}
.b-start-card {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 0;
  padding: 26px 28px;
  max-width: 460px; width: 92%;
  text-align: center;
  box-shadow: 0px 20px 0 rgba(0,0,0,0.45);
}
.b-start-level { font-size: 16px; font-weight: 700; color: var(--text-muted); margin-bottom: 4px; }
.b-start-name { margin: 0 0 12px; font-size: 24px; }
.b-start-tip { font-size: 16px; line-height: 1.5; color: var(--px-ash4); margin: 0 0 20px; }
.b-start-btn {
  font-size: 16px;
  font-weight: 800;
  padding: 10px 30px;
  color: var(--px-gold0);
  background: var(--px-gold3);
  border-color: var(--px-org2);
  box-shadow: 0px 4px 0 var(--px-gold1), 0px 6px 0 rgba(180,125,30,.35), inset 0px 2px 0 rgba(255,255,255,.65);
}
.b-start-btn:hover { background: var(--px-gold4); transform: translateY(-2px); }

/* ── Alt-tab auto-pause popup (single-player Adventure) ── */
.b-pause-overlay {
  position: fixed; inset: 0; z-index: 1150; /* above the start popup, below the tutorial overlay */
  /* Fully opaque: the board must be invisible while paused, so alt-tabbing can't be
     used to freeze the game and study the lanes/bank at leisure. */
  background: var(--px-ink0);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.b-pause-card {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 0;
  padding: 30px 40px 26px;
  max-width: 420px; width: 92%;
  text-align: center;
  box-shadow: 0px 20px 0 rgba(0,0,0,0.55);
}
.b-pause-title {
  margin: 0 0 14px; font-size: 24px; font-weight: 800;
  letter-spacing: 3px; text-transform: uppercase;
  color: var(--px-gold3);
}
.b-pause-face {
  display: inline-block;
  width: 112px; height: 112px;
  object-fit: contain;
  margin: 0 auto 8px;
  filter: drop-shadow(0 6px 14px rgba(0,0,0,0.5));
}
span.b-pause-face .svg-icon svg { width: 112px; height: 112px; }
.b-pause-name { font-size: 16px; font-weight: 400; font-family: 'PressStart', monospace; margin-bottom: 14px; color: var(--px-ash4); }
.b-pause-hint {
  font-size: 16px; letter-spacing: 1.5px; text-transform: uppercase;
  color: var(--px-ash4); margin: 0 0 16px;
}

/* ── Mobile keypad (touch only, replaces the OS keyboard) ─────────────────────
   Built body-level by bApplyKeypad in battle.js, deliberately OUTSIDE .battle-stage: inside
   it, the stage `zoom` (about 0.24 on a phone) would render these keys around 7px wide. So
   these are the one part of the battle screen sized in TRUE device pixels, and their height
   is subtracted from the stage's fit budget (vpDocked in ui.js) so the board sits above them.
   z-index below the start popup (1100) and the pause popup (1150), so a briefing or a pause
   still covers the pad. */
.b-keypad {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 800;
  background: var(--surface);
  border-top: 1px solid var(--border-strong);
  padding: 6px 4px;
  display: flex; flex-direction: column; gap: 4px;
  user-select: none;
  /* The pad is furniture, not the page: a swipe on it must never pan or bounce the board. */
  touch-action: none;
}
.b-kp-row { display: flex; gap: 4px; justify-content: center; }
.b-kp-key {
  flex: 1 1 0;
  height: 44px;                  /* thumb target; trimmed in the short-viewport rule below */
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border);
  border-radius: 0;
  background: var(--surface2);
  color: var(--text);
  /* Press Start for the letters, same face as the letter tiles they stand in for. */
  font-family: 'PressStart', monospace;
  font-size: 16px;
  font-weight: 400;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0px 2px 0 var(--px-ink0);
}
.b-kp-key:active { background: var(--p1-bg); box-shadow: none; transform: translateY(2px); }
/* FILL / DEL / GO are words, not glyphs, so they take the UI face at the smallest rung and a
   little extra width. Gold marks the two that DO something (fill the source word, fire it)
   against DEL, which only edits. */
.b-kp-wide {
  flex: 1.5 1 0;
  font-family: 'Silkscreen', monospace;
  font-size: 8px;
  letter-spacing: 1px;
  color: var(--px-gold3);
}
.b-kp-del { color: var(--px-ash4); }

/* The toggle is body-level for the same reason the pad is: at stage scale a button in the
   input row would be about 10px tall, and the player could never tap it to put the pad away
   again. bSyncKeypadToggle parks it just above the pad when open. */
.b-kp-toggle {
  position: fixed; right: 8px; bottom: 8px; z-index: 801;
  min-width: 56px; height: 44px;
  padding: 0 10px;
  border: 1px solid var(--border-strong);
  border-radius: 0;
  background: var(--px-gold2);
  color: var(--px-ink0);
  font-family: 'Silkscreen', monospace;
  font-size: 16px;
  letter-spacing: 1px;
  cursor: pointer;
  box-shadow: 0px 2px 0 var(--px-ink0);
}
.b-kp-toggle:active { box-shadow: none; transform: translateY(2px); }

/* Landscape phone: the whole viewport is about 390px tall, so give the board back 24px by
   shortening the keys. Still a 36px target, which is tappable. */
@media (max-height: 500px) {
  .b-kp-key { height: 36px; }
  .b-kp-toggle { height: 36px; }
}

/* ── Admin: live enemy send-speed panel ── */
.b-speed-panel {
  position: fixed;
  top: 64px; right: 16px;
  z-index: 900;
  width: 300px;
  max-height: calc(100vh - 90px);
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 0;
  padding: 14px 16px;
  box-shadow: 0px 12px 0 rgba(0,0,0,0.4);
  font-family: 'Silkscreen', monospace;
}
.b-dlg-section { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
.b-dlg-title { font-size: 16px; font-weight: 700; margin-bottom: 6px; }
.b-dlg-title span { font-weight: 400; color: var(--text-muted); }
.b-dlg-section label { display: block; font-size: 8px; color: var(--text-muted); margin: 6px 0 2px; }
.b-dlg-area {
  width: 100%;
  box-sizing: border-box;
  font: 12px/1.35 sans-serif;
  padding: 5px 7px;
  border-radius: 0;
  border: 1px solid var(--border);
  background: var(--surface2);
  color: var(--text);
  resize: vertical;
}
.b-speed-head { display: flex; align-items: center; justify-content: space-between; font-weight: 700; font-size: 16px; margin-bottom: 10px; }
.b-speed-x { background: none; border: none; cursor: pointer; font-size: 16px; color: var(--text-muted); }
.b-speed-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.b-speed-row > label { min-width: 92px; font-size: 16px; }
.b-speed-row input[type=range] { flex: 1; }
.b-speed-subhead { font-size: 16px; font-weight: 700; color: var(--text-muted); margin: 12px 0 6px; padding-top: 8px; border-top: 1px solid var(--border); }
.b-speed-row > span { font-weight: 700; font-size: 16px; min-width: 46px; text-align: right; }
.b-speed-note { font-size: 8px; color: var(--text-muted); line-height: 1.4; margin-bottom: 10px; }
.b-speed-actions { display: flex; gap: 8px; }
.b-speed-actions .btn { font-size: 16px; padding: 5px 10px; flex: 1; }
.b-speed-msg { font-size: 8px; margin-top: 8px; min-height: 14px; color: var(--text-muted); }
.b-speed-msg.ok  { color: var(--px-grn3); }
.b-speed-msg.err { color: var(--px-red3); }
.b-speed-row label { font-size: 16px; min-width: 56px; color: var(--text-muted); }

/* Admin duration sliders inside the Augmented Letters popup */
.b-aug-admin { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); text-align: left; }
.b-aug-admin-title { font-size: 16px; font-weight: 700; margin-bottom: 10px; }
.b-aug-admin-title span { font-weight: 400; color: var(--text-muted); }
.b-aug-admin .b-speed-row #b-augdur-freeze-val,
.b-aug-admin .b-speed-row #b-augdur-slow-val { min-width: 52px; text-align: right; font-weight: 700; font-size: 16px; }

/* ── Input tile filled by a wildcard ── */
.word-tile.from-wild {
  background: var(--px-gold4);
  border-color: var(--px-gold2);
  color: var(--px-gold0);
}
.word-tile.from-wild .word-tile-score { color: var(--px-gold1); }

/* ══════════════════════════════════════════════════════
   ETYMA STORY MODE
   ══════════════════════════════════════════════════════ */

/* ── Title Screen ── */
.etyma-title-screen {
  min-height: 90vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--px-ink1);
  border-radius: 0;
  padding: 3rem 2rem;
  text-align: center;
  gap: 2rem;
}

.etyma-logo-wrap { display: flex; flex-direction: column; align-items: center; gap: 1rem; }

.etyma-logo {
  font-family: 'Silkscreen', monospace;
  font-size: 48px;
  font-weight: 700;
  letter-spacing: 16px;
  color: var(--px-ash4);
  text-shadow: 0 0 40px rgba(255,255,255,0.3), 0 0 80px rgba(100,150,255,0.2);
  animation: etyma-glow 3s ease-in-out infinite alternate;
}

@keyframes etyma-glow {
  from { text-shadow: 0 0 20px rgba(255,255,255,0.2), 0 0 40px rgba(100,150,255,0.1); letter-spacing: 16px; }
  to   { text-shadow: 0 0 40px rgba(255,255,255,0.5), 0 0 80px rgba(100,150,255,0.4); letter-spacing: 18px; }
}

.etyma-tagline {
  font-size: 16px;
  color: rgba(255,255,255,0.5);
  font-style: italic;
  font-family: 'Silkscreen', monospace;
  letter-spacing: 1px;
}

.etyma-start-btn {
  background: rgba(255,255,255,0.1);
  color: var(--px-ash4);
  border-color: rgba(255,255,255,0.3);
  font-size: 16px;
  padding: 14px 48px;
  letter-spacing: 2px;
  border-radius: 0;
  transition: background 0.2s, border-color 0.2s;
}
.etyma-start-btn:hover { background: rgba(255,255,255,0.2); border-color: rgba(255,255,255,0.6); }

.etyma-back-btn {
  background: transparent;
  color: rgba(255,255,255,0.35);
  border-color: transparent;
  font-size: 16px;
}
.etyma-back-btn:hover { color: rgba(255,255,255,0.7); background: transparent; }

/* ── Story / Narrative Screens ── */
.etyma-story-screen {
  min-height: 90vh;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0;
  padding: 2rem 1rem;
}

.etyma-story-content {
  max-width: 680px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.etyma-story-label {
  font-size: 8px;
  font-family: 'Silkscreen', monospace;
  color: rgba(255,255,255,0.4);
  text-transform: uppercase;
  letter-spacing: 3px;
}

.etyma-layer-number {
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 2px;
  opacity: 0.9;
}

.etyma-layer-title {
  font-size: 32px;
  font-weight: 700;
  color: var(--px-ash4);
  font-family: 'Silkscreen', monospace;
  line-height: 1.2;
}

.etyma-layer-subtitle {
  font-size: 16px;
  color: rgba(255,255,255,0.5);
  font-style: italic;
  font-family: 'Silkscreen', monospace;
  margin-bottom: 0.5rem;
}

.etyma-level-name {
  font-size: 24px;
  font-weight: 700;
  color: var(--px-ash4);
  font-family: 'Silkscreen', monospace;
}

.etyma-level-num {
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
  color: rgba(255,255,255,0.4);
  text-transform: uppercase;
  letter-spacing: 1px;
}

.etyma-story-text {
  font-family: 'Silkscreen', monospace;
  font-size: 16px;
  color: rgba(255,255,255,0.8);
  line-height: 1.8;
  max-width: 600px;
}
.etyma-story-text p { margin-bottom: 0.75rem; }
.etyma-story-text strong { color: var(--px-ash4); }
.etyma-story-text em { color: rgba(255,255,255,0.65); }

.etyma-next-btn {
  margin-top: 1.5rem;
  align-self: flex-start;
  background: rgba(255,255,255,0.1);
  color: var(--px-ash4);
  border-color: rgba(255,255,255,0.25);
  font-size: 16px;
  padding: 10px 28px;
  letter-spacing: 1px;
}
.etyma-next-btn:hover { background: rgba(255,255,255,0.2); }

.etyma-lives-row {
  font-size: 24px;
  letter-spacing: 4px;
  margin-top: 0.5rem;
}

/* ── Stat rows on story screens ── */
.etyma-stat-row {
  display: flex;
  gap: 12px;
  margin-top: 1rem;
  flex-wrap: wrap;
}
.etyma-stat {
  background: rgba(255,255,255,0.07);
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 0;
  padding: 10px 16px;
  min-width: 100px;
  text-align: center;
}
.etyma-stat-label {
  font-size: 8px;
  font-family: 'Silkscreen', monospace;
  color: rgba(255,255,255,0.4);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 4px;
}
.etyma-stat-val {
  font-size: 24px;
  font-weight: 700;
  color: var(--px-ash4);
}

/* ── Play screen story header ── */
.etyma-play-title { font-family: 'Silkscreen', monospace; font-size: 16px; font-weight: 700; }
.etyma-play-layer { font-size: 16px; font-weight: 400; margin-left: 6px; }
.etyma-play-level { font-size: 16px; font-family: 'Silkscreen', monospace; color: var(--text-muted); }

/* ── Menu card for ETYMA ── */
.etyma-menu-card { border-color: rgba(100,120,200,0.4); background: var(--px-ink1); }
.etyma-menu-card:hover { border-color: rgba(100,150,255,0.6); box-shadow: 0px 4px 0 rgba(100,150,255,0.15); }
.etyma-menu-title { color: var(--px-blu4); letter-spacing: 3px; }
.etyma-menu-card .menu-card-desc { color: rgba(200,210,255,0.6); }
.etyma-menu-btn { background: rgba(255,216,77,0.15); color: var(--px-gold3); border-color: rgba(255,216,77,0.3); }
.etyma-menu-btn:hover { background: rgba(255,216,77,0.3); }

/* ── Theme toggle button ── */
.theme-toggle-btn {
  font-size: 16px;
  padding: 4px 10px;
  border-radius: 0;
  opacity: 0.7;
  transition: opacity 0.15s;
}
.theme-toggle-btn:hover { opacity: 1; }
/* Muted audio toggle (music off): dimmed + struck through */
.theme-toggle-btn.audio-off { opacity: 0.4; text-decoration: line-through; }
.theme-toggle-btn.audio-off:hover { opacity: 0.65; }

/* Battle game-speed button: the audio toggles' shell, but it carries a text label
   beside its icon, and lights up whenever the speed is above 1x so a raised speed
   reads at a glance. It sets no font-size of its own, inheriting the 16px above, which
   is what keeps it on the type ladder the pixel lint enforces. */
.b-speed-btn { display: inline-flex; align-items: center; gap: 4px; }
.b-speed-btn.speed-up { opacity: 1; color: var(--px-gold3); }

/* ── PvP room settings form ── */
.pvp-settings { max-width: 460px; margin: 0 auto; }
.pvp-set-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 10px 14px;
  align-items: center;
  margin-top: 10px;
  font-family: 'Silkscreen', monospace;
}
.pvp-set-grid > label { font-size: 16px; color: var(--text-muted); text-align: right; }
.pvp-set-grid select,
.pvp-set-grid input[type=number] {
  width: 100%;
  box-sizing: border-box;
  padding: 6px 8px;
  border-radius: 0;
  border: 1px solid var(--border);
  background: var(--surface2);
  color: var(--text);
  font: 13px sans-serif;
}
.pvp-set-grid .pvp-check { text-align: left; font-size: 16px; color: var(--text); display: flex; align-items: center; gap: 6px; }
.pvp-set-grid .pvp-check input { width: auto; }
.pvp-rules-chip {
  font: 12px sans-serif;
  color: var(--text-muted);
  background: rgba(0,0,0,0.06);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 5px 12px;
  display: inline-block;
  margin: 0 auto 1.25rem;
}

/* ── Ranked PvP ── */
.pvp-ranked-card { border-color: var(--px-gold2); box-shadow: 0px 0px 0 2px rgba(212,175,55,0.25); }
.pvp-ranked-locked { opacity: 0.82; }
.ranked-chip { background: var(--px-gold2); color:var(--px-ink1); font-weight:700; }
.ranked-panel { text-align:center; }
.rank-badge {
  display:inline-block; min-width:200px;
  background: var(--surface2); border:1px solid var(--px-gold2); border-radius: 0;
  padding:14px 18px; margin:0 auto;
}
.rank-badge-sm { min-width:140px; padding:10px 12px; }
.rank-badge-name { font:700 20px sans-serif; color:var(--text); }
.rank-badge-sm .rank-badge-name { font-size:16px; }
.rank-tier { color:var(--px-gold2); font-weight:800; }
.rank-bar { height:8px; border-radius: 0; background:rgba(0,0,0,0.18); overflow:hidden; margin:8px 0 4px; }
.rank-bar-fill { height:100%; background:var(--px-gold2); border-radius: 0; transition:width .4s ease; }
.rank-bar-label { font:12px sans-serif; color:var(--text-muted); }
.ranked-idle, .ranked-searching, .ranked-accept { display:flex; flex-direction:column; align-items:center; gap:6px; }
.ranked-waited { font:700 28px sans-serif; color:var(--text); }
.ranked-countdown { font:800 56px sans-serif; color:var(--px-gold2); line-height:1; animation:rankedPulse 1s ease-in-out infinite; }
@keyframes rankedPulse { 0%,100%{transform:scale(1);} 50%{transform:scale(1.12);} }
.ranked-vs { display:flex; align-items:center; justify-content:center; gap:18px; margin:10px 0; }
.ranked-vs-x { font:800 22px sans-serif; color:var(--text-muted); }
.ranked-vs-name { font:13px sans-serif; color:var(--text-muted); margin-top:4px; }
.loading-spinner-ring {
  width:38px; height:38px; border-radius: 0;
  border:4px solid rgba(212,175,55,0.25); border-top-color:var(--px-gold2);
  animation:rankedSpin .8s linear infinite; margin-bottom:4px;
}
@keyframes rankedSpin { to { transform:rotate(360deg); } }
.ranked-result { display:flex; flex-direction:column; align-items:center; gap:6px; margin-bottom:1.25rem; }
.ranked-delta { font:700 18px sans-serif; color:var(--text-muted); }
.ranked-delta.pos { color:var(--px-grn2); }
.ranked-delta.neg { color:var(--px-red2); }
.rank-promo { font:800 16px sans-serif; color:var(--px-gold2); }
.rank-demo  { font:700 15px sans-serif; color:var(--px-red2); }

/* ── PixiJS Canvas Lane ── */
.pve-lane {
  width: 100%;
  height: 50vh;
  min-height: 220px;
  max-height: 360px;
  border-radius: 0;
  overflow: hidden;
  margin-bottom: 1rem;
  position: relative;
  background: var(--px-ink1);
}

.pve-lane canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* ── Tile press animation ── */
.tile {
  transition: transform 0.08s cubic-bezier(0.34, 1.56, 0.64, 1), background 0.1s, opacity 0.15s;
}
.tile:active {
  transform: scale(0.88) translateY(1px) !important;
}

/* ── Word tile entrance ── */
@keyframes tile-pop {
  0%   { transform: scale(0.7); opacity: 0; }
  70%  { transform: scale(1.08); }
  100% { transform: scale(1); opacity: 1; }
}

.word-tile {
  animation: tile-pop 0.18s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

/* ── Success flash on word display ── */
@keyframes success-flash {
  0%   { background: var(--success-bg); }
  50%  { background: rgba(126,200,80,0.25); }
  100% { background: var(--surface2); }
}

#pve-word-display.flash-success {
  animation: success-flash 0.5s ease-out;
}

/* ── Battle Mode ── */
/* The battle screen lives in a FIXED-width stage so the whole UI — battlefield,
   letter bank, power-ups, reforge — is identical regardless of window width (word
   travel timing no longer drifts with screen size, since the canvas backing width
   is constant — see BATTLE_DESIGN_W). The `zoom` here is just a PRE-JS fallback; at
   runtime applyBattleScale (js/ui.js) overrides it with a value derived from the window
   size, so 720p/1080p/etc. all fill the screen proportionally. `zoom` (not transform)
   reflows the layout, so the scaled footprint removes the page scroll rather than
   leaving empty space. Overlays (start popup, power-up legend, speed panel) render
   OUTSIDE this div so they still cover the true viewport.
   NOTE: width must stay equal to BATTLE_DESIGN_W in js/battle.js. */
.battle-stage {
  width: 1660px; /* MUST match BATTLE_DESIGN_W in js/battle.js */
  margin: 0 auto;
  /* Small breathing room above the title. Included in scrollHeight, so applyBattleScale
     measures it and the stage still fits the window without overflow. */
  padding-top: 16px;
  zoom: 0.8;
}

/* On the battle screen, surrender the body's side padding + the #app 90vw cap so the
   fixed-width stage can scale into the full viewport (applyBattleScale fills the limiting
   axis). Other screens keep the padded, max-90vw layout. */
body.in-battle { padding: 0; }
body.in-battle #app { width: 100vw; max-width: 100vw; }

/* Touch devices: pin the battle screen to the viewport so the document cannot scroll.
   applyBattleScale already fits the whole stage into the visible area (and re-fits when the
   on-screen keyboard takes the bottom half of the screen), so there is nothing left to scroll
   TO, but the browser doesn't know that: when the keyboard opens it scrolls the focused
   typing field into view, which drags the battlefield off the top of the screen and leaves the
   player looking at the letter bank. A fixed body removes the scroll the browser would use.
   Every overlay (start popup, pause popup, aug legend, speed panel, tutorial spotlight, invite
   banner) is position:fixed, and a fixed ancestor is NOT a containing block for fixed
   descendants, so they still cover the true viewport and are not clipped by the overflow. */
@media (pointer: coarse) {
  /* min-height:0 undoes the base body's 100vh: on mobile `vh` is the LARGE viewport (URL bar
     hidden), so leaving it would make the fixed body taller than the box it is pinned to. */
  body.in-battle { position: fixed; inset: 0; min-height: 0; overflow: hidden; }
}

/* ── Custom game cursor (whole app) ───────────────────────────────────────────
   A themed pointer everywhere in the game. The default is a gold arrow; anything
   clickable swaps to a matching gold pointing-finger hand so interactivity still
   reads. The arrow's hotspot is its tip (4,3); the hand's is the fingertip (10,2).
   The SVGs are BASE64 data URIs (raw '<svg>' markup in a url() is rejected by some
   Chromium builds, which silently fall back to the native cursor) and carry
   explicit width/height (also required by Chromium). The hand rules use !important
   to beat the many scattered `cursor: pointer` rules above without touching each
   one. cursor is inherited, so plain body coverage cascades into every element
   that doesn't declare its own. */
:root {
  --cursor-game: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScyOCcgaGVpZ2h0PScyOCcgdmlld0JveD0nMCAwIDI4IDI4Jz48cGF0aCBkPSdNNCAzIEw0IDI0IEwxMCAxOCBMMTQgMjcgTDE4IDI1IEwxNCAxNiBMMjIgMTYgWicgZmlsbD0nI2ZmY2UzZicgc3Ryb2tlPScjN2E0ZDAwJyBzdHJva2Utd2lkdGg9JzEuNicgc3Ryb2tlLWxpbmVqb2luPSdyb3VuZCcvPjwvc3ZnPg==") 4 3, auto;
  --cursor-game-hot: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScyOCcgaGVpZ2h0PScyOCcgdmlld0JveD0nMCAwIDI4IDI4Jz48cGF0aCBkPSdNOSAzLjVDOSAyLjcgOS43IDIgMTAuNSAyUzEyIDIuNyAxMiAzLjVWMTFoMVY2LjVjMC0uOC43LTEuNSAxLjUtMS41UzE2IDUuNyAxNiA2LjVWMTFoMVY3LjVjMC0uOC43LTEuNSAxLjUtMS41UzIwIDYuNyAyMCA3LjVWMTJoMVY5LjVjMC0uOC43LTEuNSAxLjUtMS41UzI0IDguNyAyNCA5LjVWMTdjMCA0LjQtMy42IDgtOCA4aC0xLjJjLTIuMyAwLTQuNC0xLTUuOS0yLjdsLTQuMy01Yy0uNi0uNy0uNS0xLjcuMi0yLjMuNy0uNiAxLjctLjUgMi4zLjFMOSAxN1YzLjV6JyBmaWxsPScjZmZlMjdhJyBzdHJva2U9JyM3YTRkMDAnIHN0cm9rZS13aWR0aD0nMS42JyBzdHJva2UtbGluZWpvaW49J3JvdW5kJy8+PC9zdmc+") 10 2, pointer;
}
body { cursor: var(--cursor-game); }
body button,
body a,
body summary,
body label,
body [onclick],
body .btn,
body .bover-word,
body .b-pu,
body .b-mode-tab,
body .letter-bank > *,
body .b-reforge-row > * {
  cursor: var(--cursor-game-hot) !important;
}
/* Disabled / non-interactive states keep the plain arrow, not the hand. */
body button:disabled,
body .btn:disabled,
body .b-pu-disabled,
body .coop-count-chip.taken {
  cursor: var(--cursor-game) !important;
}
/* Text entry keeps a normal I-beam so typing targets read correctly. */
body input,
body textarea {
  cursor: text !important;
}

/* Electron shell only: reserve room at the end of the battle header's right-side
   button row for the injected fullscreen Quit (✕) button (see etyma-app/preload.js),
   so it sits beside the buttons instead of overlapping them. The .electron-fullscreen
   class is set by the preload, so this rule never affects the plain-browser build. */
body.electron-fullscreen #b-header-right { padding-right: 58px; }

/* FIXED design height (was 60.5vh, clamped 308-550px). The stage width is a fixed
   BATTLE_DESIGN_W, so a viewport-relative lane height made the battlefield's aspect ratio —
   and therefore the enemy figure's size relative to the field — change with the window
   (taller lane at 1080p than at 720p). With both axes fixed, the whole stage is one constant
   design box and applyBattleScale's zoom does all the fitting, so the battlefield is
   pixel-identical (just uniformly scaled) on every monitor.
   NOTE: keep in sync with BATTLE_DESIGN_H in js/battle.js. */
.battle-lane-wrap {
  position: relative;
  width: 100%;
  height: 550px; /* MUST match BATTLE_DESIGN_H in js/battle.js */
  border-radius: 0;
  overflow: hidden;
  margin-bottom: 1rem;
  background: var(--px-ink1);
}
.battle-lane-wrap canvas { display:block; width:100%; height:100%; }
/* `image-rendering` is an INHERITED property, so the battle canvas picks up the global
   `pixelated` on body. The canvas backing is a fixed 1660x550 but applyBattleScale never
   sets a zoom of exactly 1 (it is `* 0.99`, and `* 0.98` again on windows >= 900px tall,
   so ~0.9702 at best and far less in a small window), which means the browser always
   rescales the whole bitmap on its way to the screen. Nearest-neighbour does that by
   dropping rows and columns outright rather than blending them: pixel sprites shrug it
   off, but a hand-painted scene comes back chewed.
   So a PAINTED scene opts the canvas out, the same exception `img.painted-face` and the
   menu backdrop already take. It is per-scene, not global, because the opt-out covers the
   whole surface: in a painted area the sprites drawn on top soften slightly too (which
   suits Area 1, whose figures are painted as well), while every placeholder-art area keeps
   its hard nearest-neighbour edges. bDrawLaneBackground in js/battle.js owns the class. */
.battle-lane-wrap canvas.painted-scene { image-rendering: auto; }

/* Enemy speech bubble (auto-appearing comedic side comments). `top` and `right` are set by
   bSay() in js/battle.js, which parks it just before the enemy's hit line and centres it on
   the top lane: those are canvas coordinates, and this element shares the canvas's box, so
   the geometry lives with the geometry rather than as percentages guessed against it. The
   translateY(-50%) below is what centres it on that `top`, so it has to survive the pop. */
.b-enemy-speech {
  position: absolute;
  max-width: min(260px, 55%);
  padding: 8px 12px;
  border-radius: 0;
  background: rgba(255,255,255,0.95);
  color: var(--px-pur0);
  font: 600 13px/1.35 sans-serif;
  box-shadow: 0px 6px 0 rgba(0,0,0,0.45);
  pointer-events: none;
  opacity: 0;
  transform: translateY(calc(-50% + 6px)) scale(0.96);
  transition: opacity 0.18s ease, transform 0.18s ease;
  z-index: 5;
  text-align: right;
}
.b-enemy-speech::after { /* tail pointing RIGHT, across the hit line at the enemy figure */
  content: '';
  position: absolute;
  right: -7px; top: 50%; margin-top: -7px;
  border: 7px solid transparent;
  border-left-color: rgba(255,255,255,0.95);
  border-right: 0;
}
.b-enemy-speech.show { opacity: 1; transform: translateY(-50%) scale(1); }

.battle-bottom {
  display: grid;
  grid-template-columns: 520px 1fr; /* 520px fits all 10 power-up tiles (10×46 + 9×6) on one row */
  gap: 12px;
  align-items: start;
}

.battle-main-col { min-width: 0; }
.battle-side-col { min-width: 0; }

/* Incoming words (left) + Reforge (right) side by side, 50/50. When reforge is
   disabled the lone Incoming-words column simply fills the width. */
.b-lists-row { display:flex; gap:10px; align-items:flex-start; }
.b-lists-row .b-list-col { flex:1 1 0; min-width:0; }

/* Incoming words: a FIXED-height scroll box, never a growing one. The list used to size
   itself to the fight, so a 4th incoming word made this column taller than the bank/input
   column, and that is the axis applyBattleScale measures the stage against: the whole board
   re-zoomed mid-battle, which is disruptive exactly when the player is busiest. The box is
   now a constant 130px whatever is on the field, the same 130px as the Reforge box beside it.
   Row pitch is 29px (a 24px chip plus the 5px gap) and the border plus padding eat 12px, so 4
   words sit in it whole and the 5th peeks, which is also the "there is more below" cue.
   The frame is the Reforge box's frame in the incoming words' own colour: rgb(217,123,90) is
   var(--px-org3), i.e. var(--p2), the red the chips already border with on hover. */
.b-word-list {
  display:flex; flex-direction:column; gap:5px;
  height:130px; overflow-y:auto; overflow-x:hidden;
  border:1px solid rgba(217,123,90,0.55); border-radius: 0; padding:5px;
  /* No scrollbar-gutter: reserving the bar's 11px permanently left a dead strip down the
     right of every chip for the 1 to 4 words that are on the board most of the time. The
     scrollbar now takes its width only while the list actually overflows, so the chips give
     back 11px then. That is a horizontal nudge of the hp readout, not the vertical growth
     this box exists to stop, and the box HEIGHT still never moves. */
}
.b-no-words { font-size:16px; font-family: 'Silkscreen', monospace; color:var(--text-hint); font-style:italic; }

.b-word-chip {
  display: flex; justify-content:space-between; align-items:center;
  /* A flat 24px, 30% shorter than the ~34.5px this used to measure (a 16px Silkscreen line
     box is 20.5px at the font's normal 1.28 line-height, plus 6px of vertical padding and a
     1px border on each side). Stating the height outright also flattens the 1.6px difference
     between a chip carrying a type icon (.svg-icon is 1.1em tall) and one without, so every
     row is the same pitch the list height above is counted in. flex:0 0 auto stops the scroll
     box from shrinking the rows to fit instead of scrolling them. */
  flex: 0 0 auto; height: 24px; line-height: 1;
  padding: 0 10px; border-radius: 0; cursor:pointer;
  background: var(--p2-bg); color: var(--p2-dark);
  border: 1px solid transparent; font-size:16px; font-weight:600;
  transition: border-color 0.1s;
}
/* The row is one line now, so a long word must never wrap into a clipped second one. */
.b-word-text { white-space: nowrap; }
.b-word-chip:hover { border-color: var(--p2); }
.b-word-chip.b-word-selected { border-color: var(--px-gold3) !important; outline:1px solid var(--px-gold3); }
/* Word points in the Incoming list. Press Start for the taller digits, and the opacity is
   gone: 0.7 on top of an already-small readout was what made these hard to read. */
.b-word-hp { font-size:8px; font-family: 'PressStart', monospace; line-height:1; font-weight:400; }

/* Word reforge (landed words you can re-transform) */
.b-reforge-label, .b-reforge-label span { color: var(--px-gold3); }
/* Fixed height and scroll, for the same reason as the incoming list above: a full pool wraps
   to four rows of chips, which grew this column past the bank/input one and re-zoomed the
   whole stage mid-fight. 130px matches the incoming box exactly so the pair reads as one row
   of two equal frames. align-content is FLEX-START because a wrap container with a definite
   height spreads its rows to fill by default, which would have pushed the chips apart as the
   pool filled. */
.b-reforge-row {
  display:flex; flex-wrap:wrap; align-content:flex-start; gap:5px;
  height:130px; overflow-y:auto; overflow-x:hidden;
  border:1px solid rgba(232,198,77,0.55); border-radius: 0; padding:5px;
  /* No scrollbar-gutter here, unlike the incoming list: at the 7-chip REFORGE_MAX the pool
     wraps to at most 4 rows (111px), so this box does not actually scroll, and those 11px are
     what buy the empty hint below its single line. overflow-y stays as the safety net if the
     cap is ever raised. */
}
/* The empty-pool hint stays at 16px (8px, the next rung down the ladder, was unreadably
   small, and Silkscreen is a bitmap face whose art pixel is 0.125em: the sizes between the
   rungs render smeared rather than smaller, which is what the lint's multiple-of-8 rule is
   protecting). "Land words on the enemy" measures 262px at 16px against the 243px this box
   has inside its border and padding, so the line is tightened by one pixel per character
   (23 of them) to 239px. That is the whole budget: a longer string cannot hold one line at
   this size in this column. */
.b-reforge-empty {
  font-size:16px; font-family: 'Silkscreen', monospace; color:var(--px-gold3); font-style:italic;
  letter-spacing:-1px;
}
.b-reforge-chip {
  display:inline-flex; align-items:center; gap:5px;
  /* Same 24px flat row as .b-word-chip, down from ~32.5px (a 20.5px Silkscreen line box plus
     5px of padding each side and the border), so both boxes are built on one grid. */
  flex: 0 0 auto; height: 24px; line-height: 1;
  padding: 0 9px; border-radius: 0; cursor:pointer;
  background: var(--px-gold0); color: var(--px-gold3);
  border: 1px solid transparent; font-size:16px; font-weight:700;
  transition: border-color 0.1s, transform 0.1s;
}
.b-reforge-chip:hover { border-color: var(--px-gold2); transform: translateY(-1px); }
.b-reforge-chip.b-reforge-selected { border-color:var(--px-gold3) !important; outline:1px solid var(--px-gold3); }
/* Same treatment as .b-word-hp — taller digits, no opacity dimming. */
.b-reforge-pts { font-size:8px; font-family: 'PressStart', monospace; line-height:1; font-weight:400; }

/* NOTE: .battle-bottom deliberately has NO narrow-viewport single-column form. It lives
   inside .battle-stage, which is a FIXED 1660px wide at every window size (only the stage's
   `zoom` changes), so the two columns always fit it. The device width says nothing about the
   room available. A `@media (max-width: 600px)` stack used to fire here on phones and roughly
   DOUBLED the stage's natural height, which is the axis applyBattleScale fits against, so the
   whole board was scaled to half the size it needed to be. Removed 2026-08-12. */

/* On 1080p-class displays only (taller than 720p), the everything-below-the-lanes block
   overflows just enough to trip a scrollbar. Trim those sections 5% (the stage's overall
   2% width trim lives in applyBattleScale, gated on the same min-height). 720p is untouched. */
@media (min-height: 900px) {
  .battle-bottom { zoom: 0.95; }
}

/* ── Battle mode tabs ── */
.b-mode-tabs {
  display: flex;
  gap: 7px;
  margin-bottom: 8px;
}
.b-mode-tab {
  flex: 1;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 8px;
  border-radius: 0;
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
  font-weight: 500;
  letter-spacing: 0.2px;
  cursor: pointer;
  border: 1px solid var(--border-strong);
  color: var(--text-muted);
  background: var(--surface2);
  transition: transform 0.1s, border-color 0.12s, background 0.12s, color 0.12s, box-shadow 0.12s;
  user-select: none;
  overflow: hidden;
}
.b-mode-tab:hover { color: var(--text); transform: translateY(-1px); }
.b-mode-tab:active { transform: translateY(0); }
.b-mode-tab .b-tab-name { line-height: 1; }

/* Keycap chip showing the tab's hotkey */
.b-tab-key {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 0;
  font-size: 8px;
  font-weight: 600;
  font-family: 'Silkscreen', monospace;
  line-height: 1;
  color: inherit;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.12);
}

/* Create tab — blue */
.b-tab-create { color: var(--p1); border-color: rgba(118,194,229,0.4); }
.b-tab-create .b-tab-key { background: rgba(118,194,229,0.15); border-color: rgba(118,194,229,0.4); }
.b-tab-create:hover { border-color: var(--p1); background: var(--px-blu1); }
.b-mode-active-create {
  background: var(--px-blu1);
  color: var(--p1-dark);
  border-color: var(--p1);
  box-shadow: 0px 0px 0 2px rgba(118,194,229,0.35), 0px 2px 0 rgba(118,194,229,0.18);
}

/* Transform tab — red */
.b-tab-transform { color: var(--px-red3); border-color: rgba(226,75,74,0.4); }
.b-tab-transform .b-tab-key { background: rgba(226,75,74,0.15); border-color: rgba(226,75,74,0.4); }
.b-tab-transform:hover { color: var(--px-red4); border-color: var(--px-red3); background: var(--px-red0); }
.b-mode-active-transform {
  background: var(--px-red1);
  color: var(--px-red4);
  border-color: var(--px-red3);
  box-shadow: 0px 0px 0 2px rgba(226,75,74,0.35), 0px 2px 0 rgba(226,75,74,0.2);
}

/* Shuffle tab — green (an action, no active state) */
.b-tab-shuffle { color: var(--px-grn3); border-color: rgba(126,200,80,0.4); }
.b-tab-shuffle .b-tab-key { background: rgba(126,200,80,0.15); border-color: rgba(126,200,80,0.4); }
.b-tab-shuffle:hover { color: var(--px-grn4); border-color: var(--px-grn3); background: var(--px-grn0); }

/* Reforge tab — yellow */
.b-tab-reforge { color: var(--px-gold3); border-color: rgba(217,168,42,0.4); }
.b-tab-reforge .b-tab-key { background: rgba(217,168,42,0.15); border-color: rgba(217,168,42,0.4); }
.b-tab-reforge:hover { color: var(--px-gold3); border-color: var(--px-gold2); background: var(--px-gold0); }
.b-mode-active-reforge {
  background: var(--px-gold0);
  color: var(--px-gold3);
  border-color: var(--px-gold2);
  box-shadow: 0px 0px 0 2px rgba(217,168,42,0.35), 0px 2px 0 rgba(217,168,42,0.2);
}

/* ── Reroll tab — charges up as you play, then turns gold when ready ── */
.b-reroll-tab {
  cursor: not-allowed;
  pointer-events: none;
  color: var(--text-muted);
}
/* Count badge (×N rerolls banked) — hidden until at least one is ready */
.b-reroll-count {
  display: none;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 0;
  font-size: 8px;
  font-weight: 700;
  line-height: 1;
  background: var(--px-gold3);
  color: var(--px-gold0);
}
.b-reroll-tab.b-reroll-ready .b-reroll-count { display: inline-flex; }
/* Progress bar toward the next reroll */
.b-reroll-bar {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 3px;
  background: rgba(255,255,255,0.08);
}
.b-reroll-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--px-grn2);
  transition: width 0.3s ease;
}
.b-reroll-tab.b-reroll-ready {
  cursor: pointer;
  pointer-events: auto;
  color: var(--px-gold4);
  border-color: rgba(255,226,122,0.5);
  background: var(--px-gold0);
}
.b-reroll-tab.b-reroll-ready .b-tab-key {
  background: rgba(255,226,122,0.18);
  border-color: rgba(255,226,122,0.45);
  color: var(--px-gold3);
}
.b-reroll-tab.b-reroll-ready .b-reroll-fill {
  background: var(--px-gold2);
}
.b-reroll-tab.b-reroll-ready:hover { border-color: var(--px-gold3); transform: translateY(-1px); }

/* Small keycap hint inside buttons (e.g. Submit ⏎ / Clear ␣) */
.key-hint {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.3em;
  padding: 0 4px;
  margin-left: 5px;
  font-size: 0.85em;
  line-height: 1.4;
  border: 1px solid currentColor;
  border-radius: 0;
  opacity: 0.75;
}

/* ── Admin Panel ── */
.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-family: 'Silkscreen', monospace;
  font-size: 16px;
}
.admin-table th {
  text-align: left;
  padding: 8px 12px;
  font-size: 8px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
}
.admin-table td {
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.admin-table tr:hover td { background: var(--surface2); }

.admin-form {
  max-width: 560px;
  margin: 1.5rem 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.admin-field { display: flex; flex-direction: column; gap: 4px; }
.admin-label {
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

/* ── Battle End Screen ── */
.battle-over-wrap {
  max-width: 800px;
  margin: 0 auto;
  text-align: center;
  padding: 1rem 0;
}

/* Game-style result panel — a framed card instead of a bare webpage block. */
.bover-panel {
  /* Flat fill: the sheen layer this used to carry was a gradient the pixel sweep
     stripped, leaving an invalid layer list that reset the whole background to
     transparent (same defect as the deleted decorative `body` rule). */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 1.75rem 1.5rem 1.5rem;
  box-shadow: 0px 18px 0 rgba(0,0,0,0.45), inset 0px 2px 0 rgba(255,255,255,0.05);
  position: relative;
  overflow: hidden;
}
.bover-panel::before {
  content: '';
  position: absolute; inset: 0 0 auto 0; height: 4px;
  background: var(--area-color, var(--p1));
  opacity: 0.9;
}
.bover-panel.is-win::before  { background: var(--px-gold3); }
.bover-panel.is-loss::before { background: var(--p2); }

.bover-hero { display: flex; flex-direction: column; align-items: center; gap: 6px; margin-bottom: 1.25rem; }
.bover-hero-icon {
  display: grid; place-items: center;
  width: 84px; height: 84px; border-radius: 0;
  margin-bottom: 4px;
  background: rgba(255,255,255,0.10);
  border: 1px solid var(--border);
}
.is-win  .bover-hero-icon { color: var(--px-gold3); box-shadow: 0px 0px 0 2px rgba(255,216,77,0.30); }
.is-loss .bover-hero-icon { color: var(--p2);  box-shadow: 0px 0px 0 2px rgba(217,123,90,0.28); }
.bover-level {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: 'Silkscreen', monospace; font-size: 16px; font-weight: 700; color: var(--px-gold3);
}
.bover-level svg { flex: none; }
/* Difficulty badge in the end-game header — tinted to match the map's star colors. */
.bover-diff {
  font-family: 'Silkscreen', monospace; font-size: 8px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .5px; line-height: 1.6;
  padding: 1px 8px; border-radius: 0; margin-left: 2px;
  color: var(--px-gold3); background: rgba(255,216,77,.14); border: 1px solid rgba(255,216,77,.4);
}
.bover-diff.easy { color: var(--success); background: rgba(120,200,120,.14); border-color: rgba(120,200,120,.4); }
.bover-diff.hard { color: var(--p2); background: rgba(217,123,90,.16); border-color: rgba(217,123,90,.45); }
/* Power-up-unlocked banner (e.g. Shield after the Area 1 boss). */
.bover-unlock {
  margin: 0.6rem auto 0; max-width: 460px;
  font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--px-gold3);
  background: rgba(255,216,77,.12); border: 1px solid rgba(255,216,77,.4);
  border-radius: 0; padding: 8px 12px;
}
.bover-unlock strong { color: var(--px-ash4); }
.bover-sub {
  font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted);
  margin: 0.35rem 0 0; max-width: 460px;
}
.bover-locked {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 16px; font-family: 'Silkscreen', monospace; color: var(--text-muted); align-self: center;
}
.score-label svg { vertical-align: -2px; margin-right: 3px; opacity: 0.85; }
.bover-stat3 { grid-template-columns: repeat(3, 1fr); }

.b-results-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  text-align: left;
  margin-top: 0.5rem;
}

.b-results-col { }

.b-results-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Show ~5 rows then scroll, so a long match's word list can't overflow the
     end-game panel off the bottom of the screen. */
  max-height: 160px;
  overflow-y: auto;
  padding-right: 4px;
}

.b-result-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 10px;
  border-radius: 0;
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
}
.b-result-player { background: var(--p1-bg); }
.b-result-enemy  { background: var(--p2-bg); }
.b-result-enemy.is-blocked { background: rgba(126,200,80,0.10); opacity: 0.92; }
.b-result-enemy.is-hit     { background: var(--p2-bg); }
.b-result-mark { display: inline-flex; flex: none; }
.b-result-word   { font-weight: 700; flex: 1; }
.b-result-enemy.is-blocked .b-result-word { color: var(--text-muted); font-weight: 600; }
.b-result-into {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 16px; font-family: 'Silkscreen', monospace; white-space: nowrap;
}
.b-result-into svg { flex: none; opacity: 0.7; }
.b-result-into .bover-word { color: var(--success); font-weight: 700; }
.bover-word { cursor: pointer; border-bottom: 1px dashed transparent; }
.bover-word:hover { border-bottom-color: currentColor; }
.bover-def {
  margin: 1rem auto 0; max-width: 580px;
  font: 13px/1.5 sans-serif; color: var(--text-muted);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 0; padding: 10px 14px; text-align: center;
}
.bover-def.show { border-color: var(--area-color, var(--p1)); }
.bover-def strong { color: var(--text); }
.b-result-score  { font-size: 16px; font-family: 'Silkscreen', monospace; opacity: 0.75; white-space: nowrap; }
.b-result-example {
  font-size: 8px;
  font-family: 'Silkscreen', monospace;
  color: var(--success);
  font-style: italic;
  white-space: nowrap;
}
.b-result-empty {
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
  color: var(--text-hint);
  font-style: italic;
  padding: 4px 10px;
}

@media (max-width: 500px) {
  .b-results-grid { grid-template-columns: 1fr; }
}

/* ── Cutscene Overlay ── */
.cutscene-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(0,0,0,0.92);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  animation: cutscene-fade-in 0.3s ease-out;
}

@keyframes cutscene-fade-in {
  from { opacity: 0; transform: scale(0.97); }
  to   { opacity: 1; transform: scale(1); }
}

.cutscene-label {
  font-family: 'Silkscreen', monospace;
  font-size: 16px;
  letter-spacing: 4px;
  text-transform: uppercase;
  color: rgba(255,255,255,0.5);
  margin-bottom: 0.5rem;
}

.cutscene-word {
  font-family: 'PressStart', monospace;
  font-size: 48px;
  font-weight: 700;
  color: var(--px-ash4);
  letter-spacing: 6px;
  text-shadow: 0 0 40px rgba(255,255,255,0.4);
  animation: cutscene-word-pop 0.4s cubic-bezier(0.34,1.56,0.64,1) both;
}

@keyframes cutscene-word-pop {
  from { transform: scale(0.5); opacity: 0; }
  to   { transform: scale(1);   opacity: 1; }
}

.cutscene-skip {
  position: absolute;
  bottom: 2rem;
  font-size: 16px;
  font-family: 'Silkscreen', monospace;
  color: rgba(255,255,255,0.3);
  letter-spacing: 1px;
}

/* ── "Special word" announcement ──
   Phase one of a cutscene: this card holds the moment for a second while the still and
   the clip are pulled into memory, so the scene proper can start both at once instead of
   playing audio over a picture that is still downloading. It is not click-to-skip. */
.sw-announce-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(0,0,0,0.92);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: cutscene-fade-in 0.3s ease-out;
}

.sw-announce-card {
  padding: 32px 40px;
  text-align: center;
  background: var(--px-ink2);
  border: 2px solid var(--px-gold3);
  box-shadow: 8px 8px 0 var(--px-shadow);
  animation: cutscene-word-pop 0.3s cubic-bezier(0.34,1.56,0.64,1) both;
}

.sw-announce-title {
  font-family: 'PressStart', monospace;
  font-size: 16px;
  color: var(--px-gold3);
  letter-spacing: 6px;
  margin-bottom: 24px;
}

/* One rung under .cutscene-word's 48px, so the scene proper still lands as an escalation. */
.sw-announce-word {
  font-family: 'PressStart', monospace;
  font-size: 32px;
  color: var(--px-ash4);
  letter-spacing: 4px;
}

/* ── Pre/Post-battle story scenes (PvZ-style click-through dialogue) ── */
.levelscene-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(0,0,0,0.93);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 1.5rem;
  animation: cutscene-fade-in 0.3s ease-out;
}

.levelscene-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-width: 560px;
  width: 100%;
  padding: 2rem 2rem 1.25rem;
  border-radius: 0;
  background: rgba(30,30,40,0.96);
  border: 1px solid rgba(255,255,255,0.12);
  box-shadow: 0px 20px 0 rgba(0,0,0,0.6);
}

.levelscene-portrait {
  width: 132px;
  height: 132px;
  margin-bottom: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0;
  background: rgba(255,224,192,0.14);
  border: 2px solid rgba(255,224,192,0.35);
  color: var(--px-gold4);
  font-size: 48px;
  animation: cutscene-word-pop 0.35s cubic-bezier(0.34,1.56,0.64,1) both;
}
.levelscene-portrait-svg { width: 96px; height: 96px; }
.levelscene-portrait-svg svg { width: 100%; height: 100%; }

.levelscene-name {
  font-family: 'PressStart', monospace;
  font-size: 24px;
  font-weight: 700;
  color: var(--px-ash4);
  letter-spacing: 2px;
  text-transform: uppercase;
  margin-bottom: 0.6rem;
}

.levelscene-text {
  font-family: 'Silkscreen', monospace;
  font-size: 16px;
  line-height: 1.55;
  color: rgba(255,255,255,0.9);
  min-height: 3.2em;
  margin-bottom: 1.25rem;
}

.levelscene-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  padding-top: 0.75rem;
  border-top: 1px solid rgba(255,255,255,0.08);
}

.levelscene-count {
  font-family: 'Silkscreen', monospace;
  font-size: 16px;
  color: rgba(255,255,255,0.4);
  letter-spacing: 1px;
}

.levelscene-hint {
  font-family: 'Silkscreen', monospace;
  font-size: 16px;
  color: rgba(255,255,255,0.3);
  letter-spacing: 1px;
}

.levelscene-skip-btn {
  font-size: 16px;
  padding: 6px 14px;
}

/* ── Online PvP lobby ── */
.pvp-lobby-wrap {
  max-width: 640px;
  margin: 2rem auto 0;
  text-align: center;
}
.pvp-lobby-cards {
  display: flex;
  flex-direction: column;
  gap: 14px;
  text-align: left;
}
/* Ranked is the full-width hero card; Create + Join sit side by side beneath it. */
.pvp-lobby-cards > .pvp-ranked-card { width: 100%; }
.pvp-room-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.pvp-lobby-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0;
  padding: 2rem 1.5rem;
}
.pvp-code-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
}
.pvp-room-code {
  font-family: monospace;
  font-size: 48px;
  font-weight: 700;
  letter-spacing: 8px;
  padding: 0.5rem 0;
  color: var(--p2-dark, var(--px-gold0));
  user-select: all;
}
@media (max-width: 600px) {
  .pvp-room-row { grid-template-columns: 1fr; }
  .pvp-room-code { font-size: 32px; letter-spacing: 5px; }
}

/* ── Co-op lobby ─────────────────────────────────────────────────────────── */
.pvp-home-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; text-align: left; }
.pvp-panel { background: var(--surface); border: 1px solid var(--border); border-radius: 0; padding: 1.5rem; text-align: left; }
.pvp-panel-title { font-size: 24px; font-weight: 800; margin: 0 0 1rem; text-align: center; }
.pvp-panel-actions { display: flex; justify-content: space-between; gap: 10px; margin-top: 1.25rem; }
.pvp-code-input { width: 100%; font-family: monospace; font-size: 24px; letter-spacing: 6px; text-align: center; padding: 10px; border-radius: 0; border: 1px solid var(--border); background: var(--bg); color: var(--text); }
.pvp-code-box { display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap; background: var(--bg); border: 1px solid var(--border); border-radius: 0; padding: 10px 14px; margin-bottom: 1rem; }
.pvp-code-label { font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted); }
.pvp-code-value { font-family: monospace; font-size: 24px; font-weight: 700; letter-spacing: 6px; color: var(--px-gold3); user-select: all; }
.coop-field { margin-bottom: 1rem; }
.coop-field > label { display: block; font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted); margin-bottom: 6px; }
.coop-count-row { display: flex; gap: 8px; }
.coop-count-chip { flex: 1; padding: 10px 0; border-radius: 0; border: 1px solid var(--border); background: var(--bg); color: var(--text); font-weight: 800; font-size: 16px; cursor: pointer; }
.coop-count-chip.on { border-color: var(--px-gold3); box-shadow: inset 0px 0px 0 2px rgba(255,216,77,.35); color: var(--px-gold3); }
.coop-role-row { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }
.coop-role-chip { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 6px; border-radius: 0; border: 1px solid var(--border); background: var(--bg); color: var(--text); cursor: pointer; text-align: center; }
.coop-role-chip.on { border-color: var(--px-grn3); box-shadow: inset 0px 0px 0 2px rgba(126,200,80,.3); }
.coop-role-ico { font-size: 24px; }
.coop-role-name { font-weight: 700; font-size: 16px; }
.coop-role-desc { font-family: 'Silkscreen', monospace; font-size: 8px; color: var(--text-muted); line-height: 1.25; }
.coop-seat-list { display: flex; flex-direction: column; gap: 8px; margin: 0.5rem 0 1.25rem; }
.coop-seat { display: grid; grid-template-columns: 1fr auto auto; gap: 10px; align-items: center; padding: 10px 12px; border-radius: 0; border: 1px solid var(--border); background: var(--bg); }
.coop-seat.ready { border-color: var(--px-grn3); }
.coop-seat-empty { opacity: 0.5; font-style: italic; }
.coop-seat-name { font-weight: 400; font-family: 'PressStart', monospace; }
.coop-seat-role { font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted); }
.coop-seat-ready { font-family: 'Silkscreen', monospace; font-size: 16px; font-weight: 700; }
.coop-seat.ready .coop-seat-ready { color: var(--px-grn3); }
@media (max-width: 600px) {
  .pvp-home-cards { grid-template-columns: 1fr; }
  .coop-role-row { grid-template-columns: 1fr; }
}

/* ── Master volume control ── */
.vol-control {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 16px;
}
.vol-slider {
  width: 90px;
  cursor: pointer;
  accent-color: var(--p2-dark, var(--px-gold0));
  vertical-align: middle;
}
.vol-pct {
  font-size: 8px;
  font-family: 'Silkscreen', monospace;
  color: var(--text-muted);
  min-width: 32px;
  text-align: right;
}

/* Per-channel (SFX / Music) volume popover that opens from its header icon. */
.audio-ctl { position: relative; display: inline-flex; }
.audio-pop {
  position: absolute;
  top: calc(100% + 6px);
  /* Opens rightward from its icon. The audio buttons live at the LEFT of every header
     (matching the battle screen), so a right-aligned popover would hang off the left edge
     — especially in-battle where body.in-battle drops the side padding. */
  left: 0;
  right: auto;
  display: none;
  z-index: 200;
  background: var(--surface, var(--px-ash4));
  border: 1px solid var(--border, var(--px-ash3));
  border-radius: 0;
  padding: 8px 10px;
  box-shadow: 0px 6px 0 rgba(0,0,0,0.22);
  min-width: 170px;
}
.audio-pop.open { display: block; }
.audio-pop-row { display: flex; align-items: center; gap: 8px; }
.audio-pop .vol-slider { width: 100px; }
.audio-mute-btn {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 2px 4px;
}
.audio-next-btn {
  margin-top: 8px;
  width: 100%;
  font-size: 16px;
  padding: 4px 8px;
}

/* ═══ Game-style main menu ═══════════════════════════════════════════════════ */
/* The pre-pixel design layered two soft radial tints over --bg here as a decorative
   "stage" backdrop. DON'T bring it back: the pixel design wants a flat --bg, which
   the `body` rule at the top of this file already sets.
   Why it was DELETED rather than left in place (the white-page bug, 2026-08-10): the
   pixel sweep stripped the banned radial-gradient wrappers but left the bare
   `rgba()` stops behind, giving `background: <color>, <color>, var(--bg)` — a layer
   list with a color in a non-final layer, which is invalid. And because the value
   contained `var()`, that is decided at COMPUTED-VALUE time, so the property reset to
   its initial `transparent` instead of falling back to the earlier `body` rule the way
   a parse-time error would. Result: the page canvas showed through WHITE on every
   screen except the menu, whose full-viewport backdrop (now `body.on-menu::before`)
   happened to cover it. See also `.bover-panel`, which had the identical defect. */

/* The zoom target for applyMenuScale() (js/ui.js). Every length in here is fixed px (the
   940px column, the card grid's 240px minimum, and the logo's clamp(), which is pinned at
   its 78px max on any window wider than ~975px), so the menu lays out IDENTICALLY at every
   window width and only ever ran out of HEIGHT. That is what lets a single `zoom` shrink it
   to fit a 720p window as a faithful scaled-down copy of the 1080p layout (3 cards stay 3
   cards), instead of the layout reflowing into a different one. Don't swap these fixed
   lengths for vw/% without revisiting that. */
.menu-screen {
  max-width: 940px;
  margin: 0 auto;
  padding: 2.5rem 1.25rem 3rem;
}

/* Big chunky game logo */
.menu-logo {
  font-family: 'PressStart', monospace;
  font-size: clamp(42px, 8vw, 78px);
  font-weight: 400;   /* Press Start ships no bold face; 700 only ever asked for a smear */
  letter-spacing: 1px;
  line-height: 1.05;
  color: var(--px-gold3);
  text-shadow: 0 2px 0 var(--px-gold1), 0 4px 0 var(--px-gold0), 0 7px 16px rgba(0,0,0,0.55);
}
.menu-tagline {
  font-family: 'Silkscreen', monospace;
  font-size: clamp(13px, 2.4vw, 17px);
  margin-bottom: 0.5rem;
}

/* Playtest marker under the tagline — gold "beta"-style pill with a green live dot.
   Pull for the full build. The whole pill is zoomed to 0.8 (20% smaller) rather than
   stepped down a font rung, because the ladder's next rung below 16px is 8px — half
   the size, not a fifth. Trade-off: 16px x 0.8 = 12.8px, which is 1.6 CSS px per art
   pixel, so this one badge antialiases where the rest of the UI sits on the grid. */
.menu-playtest {
  display: flex;
  width: fit-content;
  align-items: center;
  gap: 8px;
  margin: 0 auto 1.9rem;
  padding: 5px 15px;
  zoom: 0.8;
  font-family: 'Silkscreen', monospace;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--px-gold4);
  background: rgba(255, 216, 77, 0.10);
  border: 1px solid rgba(255, 216, 77, 0.45);
  border-radius: 0;
}
.menu-playtest-dot {
  width: 8px;
  height: 8px;
  border-radius: 0;
  background: var(--px-grn3);
  box-shadow: 0px 0px 0 2px rgba(126, 200, 80, 0.85);
  animation: menu-playtest-pulse 2s ease-in-out infinite;
}
@keyframes menu-playtest-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.4; }
}

/* Cards become chunky, beveled game buttons that lift on hover. Background is
   left to each card's own theme (e.g. the red Adventure card) so we only add
   depth/shape here. */
.menu-screen .menu-cards,
.pvp-lobby-wrap .menu-cards,
.admin-screen .menu-cards {
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px;
}
.menu-screen .menu-card,
.pvp-lobby-wrap .menu-card,
.admin-screen .menu-card {
  border: 2px solid var(--border-strong);
  border-radius: 0;
  padding: 1.75rem 1.5rem;
  box-shadow: 0px 6px 0 rgba(0,0,0,0.35), 0px 12px 0 rgba(0,0,0,0.35);
  transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
}
.menu-screen .menu-card:hover,
.pvp-lobby-wrap .menu-card:hover,
.admin-screen .menu-card:hover {
  transform: translateY(-4px);
  box-shadow: 0px 10px 0 rgba(0,0,0,0.35), 0px 18px 0 rgba(0,0,0,0.45);
  border-color: var(--px-gold3);
}
.menu-screen .menu-card-icon,
.pvp-lobby-wrap .menu-card-icon,
.admin-screen .menu-card-icon  { font-size: 48px; }
.menu-screen .menu-card-title,
.pvp-lobby-wrap .menu-card-title,
.admin-screen .menu-card-title { font-family: 'PressStart', monospace; font-size: 24px; }
/* Center the icon + title; the description now sits below the button. */
.menu-screen .menu-card,
.pvp-lobby-wrap .menu-card { text-align: center; }
.menu-screen .menu-card .btn.primary + .menu-card-desc,
.pvp-lobby-wrap .menu-card .btn.primary + .menu-card-desc { margin-top: 0.9rem; }
/* Primary buttons use the ETYMA brand yellow with dark text for contrast —
   now the global .btn.primary default, so no per-screen override is needed. */

@media (max-width: 600px) {
  .menu-screen .menu-cards,
  .pvp-lobby-wrap .menu-cards,
  .admin-screen .menu-cards { grid-template-columns: 1fr; }
}

/* ── PvP / Co-op / Team lobby redesign ─────────────────────────────────────────
   Every "action" button in a lobby (Find Match, Create, Join, Team Up, I'm Ready,
   Accept…) is the ETYMA gold: a beveled, brushed-gold key that lifts on hover. The
   later position + selector list wins over the flat-gold rule above, so both the
   panel buttons and the menu-card buttons share one look. */
.pvp-lobby-wrap .btn.primary,
.pvp-lobby-wrap .menu-card .btn.primary {
  background: var(--px-gold3);
  border: 1px solid var(--px-org2);
  color: var(--px-gold0);
  font-weight: 800;
  letter-spacing: .2px;
  box-shadow: 0px 4px 0 var(--px-gold1), 0px 6px 0 rgba(180,125,30,.35), inset 0px 2px 0 rgba(255,255,255,.65);
  transition: transform .1s ease, box-shadow .1s ease, background .1s ease;
}
.pvp-lobby-wrap .btn.primary:hover,
.pvp-lobby-wrap .menu-card .btn.primary:hover {
  background: var(--px-gold4);
  box-shadow: 0px 4px 0 var(--px-gold1), 0px 10px 0 rgba(180,125,30,.45), inset 0px 2px 0 rgba(255,255,255,.8);
  transform: translateY(-1px);
}
.pvp-lobby-wrap .btn.primary:active,
.pvp-lobby-wrap .menu-card .btn.primary:active {
  transform: translateY(2px);
  box-shadow: 0px 2px 0 var(--px-gold1), 0px 4px 0 rgba(180,125,30,.4), inset 0px 2px 0 rgba(255,255,255,.5);
}
.pvp-lobby-wrap .btn.primary:disabled {
  background: var(--surface2); color: var(--text-muted);
  border-color: var(--border-strong); box-shadow: none; transform: none;
}

/* Lobby cards: gold-tinted icon in a soft round badge, gold hover ring. */
.pvp-lobby-card .menu-card-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 62px; height: 62px; border-radius: 0;
  background: rgba(255,216,77,.18);
  border: 1px solid rgba(255,216,77,.28);
}
.pvp-lobby-card .menu-card-icon .svg-icon { width: 34px; height: 34px; color: var(--px-gold3); }
.pvp-card-lock .svg-icon { width: .8em; height: .8em; color: var(--text-muted); vertical-align: -.06em; }

/* Panel titles carry a leading icon. */
.pvp-panel-title .svg-icon { width: .95em; height: .95em; vertical-align: -.12em; color: var(--px-gold3); margin-right: 4px; }

/* Icon+label utility buttons (Copy / Invite) keep the icon and text aligned. */
.btn-icon { display: inline-flex; align-items: center; gap: 6px; }
.btn-icon .svg-icon { width: 15px; height: 15px; }

/* Seat / lane inline icons. */
.coop-seat-name .svg-icon.seat-host { color: var(--px-gold3); width: 1em; height: 1em; vertical-align: -.14em; }
.coop-seat-role .svg-icon { width: 13px; height: 13px; vertical-align: -.16em; color: var(--text-muted); }
.coop-seat-ready .svg-icon { width: 13px; height: 13px; vertical-align: -.16em; }
.coop-seat.ready .coop-seat-ready .svg-icon { color: var(--px-grn3); }

/* Taken lane / team chips (was inline opacity). */
.coop-count-chip.taken { opacity: .4; cursor: not-allowed; }
.coop-count-chip.taken .svg-icon { width: .9em; height: .9em; vertical-align: -.12em; }
.coop-count-chip .svg-icon { width: .95em; height: .95em; vertical-align: -.1em; }

/* Team-colour markers replace the 🟦/🟥 emoji. */
.team-dot { display: inline-block; width: .78em; height: .78em; border-radius: 0; vertical-align: -.04em; }
.team-dot.team-a { background: var(--px-blu3); box-shadow: 0px 0px 0 2px rgba(74,134,255,.5), 0px 0px 0 2px rgba(74,134,255,.4); }
.team-dot.team-b { background: var(--px-red3); box-shadow: 0px 0px 0 2px rgba(255,90,90,.5), 0px 0px 0 2px rgba(255,90,90,.4); }
.team-chip { display: inline-flex; align-items: center; justify-content: center; gap: 7px; }

/* Role chips: gold-tinted role icon. */
.coop-role-ico .svg-icon { width: 22px; height: 22px; color: var(--px-gold3); }
.coop-role-chip.on .coop-role-ico .svg-icon { color: var(--px-gold4); }

/* ── Selected count / lane / team chips ────────────────────────────────────────
   Picked count + lane chips get an on-brand gold fill (not a thin ring). Team-side
   chips instead take their own colour so A reads blue and B reads red. These come
   after the base .coop-count-chip.on rule so they win. */
.coop-count-chip {
  transition: background .12s ease, border-color .12s ease, box-shadow .12s ease, color .12s ease;
}
.coop-count-chip.on {
  background: var(--px-gold3);
  border-color: var(--px-org2);
  color: var(--px-gold0);
  box-shadow: 0px 2px 0 var(--px-gold1), inset 0px 2px 0 rgba(255,255,255,.55);
}
.team-chip.on { color: var(--px-ash4); }
.team-chip-a.on {
  background: rgba(74,134,255,.34);
  border-color: var(--px-blu3);
  box-shadow: inset 0px 0px 0 2px rgba(74,134,255,.55), 0px 0px 0 2px rgba(74,134,255,.28);
}
.team-chip-b.on {
  background: rgba(255,90,90,.34);
  border-color: var(--px-red3);
  box-shadow: inset 0px 0px 0 2px rgba(255,90,90,.55), 0px 0px 0 2px rgba(255,90,90,.28);
}
.team-chip { padding: 11px 0; }

/* ── Wider lobby (team / co-op wait rooms with two columns of seats) ──────────── */
.pvp-lobby-wrap.pvp-lobby-wide { max-width: 860px; }
.pvp-lobby-wide .pvp-panel { max-width: 100%; }

/* Seat names never wrap; long usernames ellipsize instead of stacking. */
.coop-seat { align-items: center; }
/* This one truncates with a BROWSER-inserted ellipsis, so it can't be fixed by editing a
   string: the engine draws the font's own U+2026, and .coop-seat-name is font-weight 700,
   where Silkscreen's ellipsis is a single solid 6x1 bar (an underscore) rather than three
   dots. The string form of text-overflow renders literal periods instead; engines that
   don't support it ignore the second declaration and keep the first, so this can only
   help. Where it isn't supported the bar returns — dropping the weight to 400 is the
   other lever, at the cost of the bold seat name. */
.coop-seat-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; text-overflow: "..."; white-space: nowrap; }

/* ── Ranked rules (icon pills) ─────────────────────────────────────────────────── */
.pvp-rules-box {
  background: rgba(255,216,77,.05);
  border: 1px solid rgba(255,216,77,.18);
  border-radius: 0;
  padding: 16px 16px 18px;
  margin-bottom: 1.5rem;
}
.pvp-rules-title {
  font: 800 11px system-ui, sans-serif;
  text-transform: uppercase;
  letter-spacing: 1.6px;
  color: var(--px-gold3);
  text-align: center;
  margin-bottom: 13px;
}
.pvp-rules-pills {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 8px;
}
.rule-pill {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px; border-radius: 0;
  background: var(--surface2);
  border: 1px solid var(--border-strong);
  font: 600 13px system-ui, sans-serif; color: var(--text);
  white-space: nowrap;
}
.rule-pill .svg-icon { flex: 0 0 auto; width: 15px; height: 15px; color: var(--px-gold3); }

/* ── Normal page: join-by-code above, create-a-room below, with an "or" divider ── */
.pvp-normal { max-width: 480px; margin: 0 auto; }
.pvp-join-input { width: 150px; }
.pvp-or {
  display: flex; align-items: center; gap: 12px;
  margin: 1.25rem 0 1rem; color: var(--text-muted);
  font: 600 12px system-ui, sans-serif; text-transform: uppercase; letter-spacing: 1px;
}
.pvp-or::before, .pvp-or::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
.pvp-set-grid .pvp-check .svg-icon { width: 1em; height: 1em; vertical-align: -.14em; color: var(--px-gold3); }

/* ── Tutorial / How to Play ── */
.tutorial-wrap {
  max-width: 680px;
  margin: 1rem auto 0;
}
.tutorial-card {
  background: var(--surface2);
  border: 2px solid var(--border-strong);
  border-radius: 0;
  padding: 2rem 1.75rem;
  box-shadow: 0px 6px 0 rgba(0,0,0,0.3), 0px 12px 0 rgba(0,0,0,0.35);
  min-height: 320px;
}
.tutorial-icon { font-size: 48px; text-align: center; line-height: 1; }
.tutorial-title {
  font-family: 'PressStart', monospace;
  font-size: 24px;
  text-align: center;
  margin: 0.5rem 0 1.25rem;
  color: var(--px-gold3);
}
.tutorial-body {
  font-family: 'Silkscreen', monospace;
  font-size: 16px;
  line-height: 1.6;
  color: var(--text);
}
.tutorial-body p { margin-bottom: 0.9rem; }
.tutorial-body ul { margin: 0 0 0.9rem 1.1rem; }
.tutorial-body li { margin-bottom: 0.55rem; }
.tutorial-body strong { color: var(--p1-dark); }

.tutorial-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 1.25rem;
}
.tutorial-nav .btn:disabled { opacity: 0.35; cursor: default; pointer-events: none; }
.tut-dots { display: flex; gap: 8px; }
.tut-dot {
  width: 9px; height: 9px; border-radius: 0;
  background: var(--border-strong);
  cursor: pointer;
  transition: background 0.15s, transform 0.15s;
}
.tut-dot:hover { transform: scale(1.2); }
.tut-dot-active { background: var(--px-gold3); }
/* ─── Adventure level-select map ─────────────────────────────────────────────── */
/* Adventure level select — one area per screen (carousel) */
.adv-map-page { max-width: 1400px; margin: 0 auto; padding: 0.5rem 1rem 0; }
.adv-area-page {
  position: relative;
  border: 1px solid var(--border);
  border-radius: 0;
  overflow: hidden;
  /* Scene art is 1600×960 (5:3). Keep that exact ratio so background-size:cover
     shows the WHOLE image (no crop), and size the panel off the viewport so it
     scales with the monitor: width is the smallest of the 1400px cap, the
     available width, and the width whose 5:3 height still clears the ~180px of
     fixed chrome (body padding + header + footer). The larger cap + smaller
     chrome reserve let the scene fill more of the screen while still showing
     fully at any window size without ever overflowing the Menu/Atlas footer. */
  aspect-ratio: 5 / 3;
  width: min(1400px, 100%, calc((100vh - 200px) * 5 / 3));
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background-color: var(--surface);
}
.adv-area-page.adv-area-locked { filter: grayscale(0.5); opacity: 0.85; }
.adv-page-scrim {
  position: absolute; inset: 0; pointer-events: none;
  background: rgba(0,0,0,0.4);
}
/* Side padding clears the 64px arrows at their 8px inset. */
.adv-area-content { position: relative; z-index: 1; padding: 2.5rem 4.5rem; text-align: center; }
/* The area arrows used to be a `‹`/`›` character on a hairline border over 40% black,
   and they read as almost nothing: a <button> does NOT inherit body's Silkscreen, so
   those chevrons drew in the UA's own font, thin and anti-aliased, in muted ash over
   busy scene art. Now they're the ui_back pixel sprite in gold on a solid near-black
   plate with a 2px area-colored border and the hard drop the rest of the UI uses. */
.adv-arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  width: 64px; height: 64px; border-radius: 0;
  border: 2px solid var(--area-color, var(--p1));
  background: var(--px-ink0); color: var(--px-gold3);
  font-family: 'Silkscreen', monospace; font-size: 24px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0px 4px 0 rgba(0,0,0,0.55);
  transition: background 0.12s, color 0.12s, transform 0.12s;
}
/* 48px over a 12-cell sprite is exactly 2x, so the arrow stays on its own grid (32 would
   have been 2.67 CSS px per art pixel). The sprite's ink stops one cell short of the
   bottom, so nudge it down that cell — 4px at 2x — to sit centred in the plate. */
.adv-arrow .svg-icon { width: 48px; height: 48px; margin-top: 4px; }
/* One sprite serves both directions — ui_back is the only arrow in the set. */
.adv-arrow-next .svg-icon { transform: scaleX(-1); }
.adv-arrow:hover:not(:disabled) { background: var(--area-color, var(--p1)); color: var(--px-ink0); transform: translateY(-50%) scale(1.08); }
.adv-arrow:disabled { opacity: 0.22; cursor: default; }
.adv-arrow-prev { left: 8px; }
.adv-arrow-next { right: 8px; }
.adv-area-head { display: flex; flex-direction: column; align-items: center; gap: 4px; margin-bottom: 1.75rem; }
.adv-area-emoji { font-size: 32px; line-height: 1; }
.adv-area-icon { width: 1.25em; height: 1.25em; color: var(--area-color, var(--text)); }
.adv-area-name { font-size: 24px; font-weight: 800; color: var(--area-color, var(--text));
  /* Layered shadow so the title stays legible over light AND dark scene patches. */
  text-shadow: 0 1px 2px rgba(0,0,0,0.85), 0 2px 8px rgba(0,0,0,0.7), 0 0 18px rgba(0,0,0,0.5); }
.adv-area-sub { font-size: 16px; font-family: 'Silkscreen', monospace; color: var(--px-ash4);
  text-shadow: 0 1px 2px rgba(0,0,0,0.85), 0 1px 6px rgba(0,0,0,0.6); }
.adv-area-lock { margin-top: 6px; font-size: 16px; font-family: 'Silkscreen', monospace; color: var(--text-hint); display: inline-flex; align-items: center; gap: 5px;
  text-shadow: 0 1px 2px rgba(0,0,0,0.85), 0 1px 6px rgba(0,0,0,0.6); }
.adv-dots { position: relative; z-index: 1; display: flex; gap: 9px; justify-content: center; padding: 0 0 1.5rem; }
.adv-dot {
  width: 11px; height: 11px; border-radius: 0; padding: 0; cursor: pointer;
  border: 1px solid var(--border-strong); background: transparent;
  transition: transform 0.12s, background 0.12s;
}
.adv-dot.on { background: var(--area-color, var(--p1)); border-color: var(--area-color, var(--p1)); transform: scale(1.25); }
.adv-dot.locked { opacity: 0.4; }
.adv-node-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  /* Never wrap: the row lives inside a fixed-aspect, overflow:hidden panel, so a wrapped
     second row would be clipped rather than shown. The nodes shrink instead (see below). */
  flex-wrap: nowrap;
}
.adv-node-link {
  flex: 0 0 18px;
  height: 2px;
  background: var(--border-strong);
  margin: 0 -2px;
  align-self: center;
  margin-top: -22px;
}
.adv-node {
  /* Was a flat 110px, which capped the level name at 100px and forced it down to 8px.
     The name is now 16px, so the node tracks the panel: 5 nodes at 17% plus the four
     14px connectors is 85% + 56px, which clears the content box at every size the panel
     can take, and the 136px floor keeps the small end readable. `0 1` (not `0 0`) lets
     them squeeze on a very small window instead of overflowing the panel. */
  flex: 0 1 max(136px, 17%);
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 8px 4px;
  border-radius: 0;
  cursor: pointer;
  transition: transform 0.12s, background 0.12s;
}
.adv-node:hover:not(.adv-node-locked) { background: rgba(255,255,255,0.05); transform: translateY(-2px); }
.adv-node-face {
  width: 48px; height: 48px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 0;
  overflow: hidden;
  font-size: 24px; font-weight: 800;
  /* Opaque dark domed backing so every enemy head (light or dark) stands out instead
     of blending into a same-toned circle — mirrors the in-battle avatar treatment.
     Area 1 overrides this with its own sky (.adv-node-sky, just below). */
  background: var(--px-pur0);
  border: 2px solid var(--area-color, var(--p1));
  /* Double outline: area-colored border (above) + a dark inner ring, like drawAvatar. */
  box-shadow: inset 0px 0px 0 2px rgba(0,0,0,0.55), 0px 2px 0 rgba(0,0,0,0.45);
  color: var(--text);
}
/* Level-enemy avatar fills the node circle (clipped by overflow:hidden above). */
.adv-node-avatar { width: 100%; height: 100%; }
/* Area 1 stands its heads against Area 1's sky instead of the dark plate, since it is the
   one area whose battlefield is painted (js/ui.js adds the class for area id 'proto' only).
   Four hard bands of the painting's own blues, deepest at the top down to the horizon shade,
   with two chunky clouds. Everything sits on a 4px grid: 12 art pixels across the 48px face,
   the same 12-cell grid the icons compile on. Each cloud is one flat background layer, sized
   and placed rather than drawn, so the sky costs no image file and no extra markup. Bands
   rather than a smooth ramp because a soft gradient is not pixel art (and the lint says so).
   Locked nodes keep the dark plate: their rule comes later and they show a padlock, not a
   head. The boss face is 56px, so its clouds sit a touch up and left of the small ones. */
.adv-node-face.adv-node-sky {
  background-color: var(--sky-proto1);
  background-image:
    linear-gradient(var(--sky-proto4) 0 50%, var(--sky-proto4) 50% 100%),
    linear-gradient(var(--sky-proto4) 0 50%, var(--sky-proto4) 50% 100%),
    linear-gradient(var(--sky-proto4) 0 50%, var(--sky-proto4) 50% 100%),
    linear-gradient(var(--sky-proto4) 0 50%, var(--sky-proto4) 50% 100%),
    linear-gradient(var(--sky-proto0) 0 25%, var(--sky-proto1) 25% 50%, var(--sky-proto2) 50% 75%, var(--sky-proto3) 75% 100%);
  background-repeat: no-repeat;
  background-size: 12px 4px, 24px 4px, 12px 4px, 24px 4px, 100% 100%;
  background-position: 8px 12px, 4px 16px, 28px 28px, 24px 32px, 0 0;
}
.adv-node-avatar svg { width: 100%; height: 100%; display: block; }
.adv-node-locked .adv-node-face .svg-icon { width: 1.1em; height: 1.1em; }
.adv-node-boss .adv-node-face {
  width: 56px; height: 56px;
  font-size: 24px;
  box-shadow: inset 0px 0px 0 2px rgba(0,0,0,0.55), 0px 0px 0 2px var(--area-color, var(--p1));
}
/* Cleared keeps the dark backing (so the head stays readable) and signals progress
   with an area-colored glow instead of filling the disc with the area color. */
.adv-node-cleared .adv-node-face { color: var(--px-ash4); box-shadow: inset 0px 0px 0 2px rgba(0,0,0,0.55), 0px 0px 0 2px var(--area-color, var(--p1)); }
.adv-node-locked { cursor: default; opacity: 0.5; }
.adv-node-locked .adv-node-face { background: var(--surface); border-color: var(--border-strong); }
/* 8px was the bottom rung of the ladder and unreadable on the scene art; 16px is the next
   one up. line-height 1.5 lands each line on an exact 24px so the rows stay pixel-crisp.
   The node (not a 100px cap) sets the width now, and break-word is the escape hatch for
   the two names with a chunk longer than the node on a small window ("Consciousness",
   "McGlitchface") — at 1080p the node is wide enough that nothing breaks. */
.adv-node-name {
  font-size: 16px; font-family: 'Silkscreen', monospace; text-align: center;
  color: var(--px-ash4); line-height: 1.5; max-width: 100%;
  overflow-wrap: break-word;
  text-shadow: 0 1px 2px rgba(0,0,0,0.85), 0 1px 5px rgba(0,0,0,0.6);
}
/* Sized to sit under the 16px name rather than the 8px one it used to follow. 24px is
   exactly 1x the icons' 12-cell grid (they compile at unit 2, so a 24-unit viewBox);
   the 1.1em default would have landed on 17.6px, which is 0.73 of a cell. */
.adv-node-stars { display: flex; gap: 2px; font-size: 16px; }
.adv-node-stars .svg-icon { width: 24px; height: 24px; }
.adv-node-star { color: var(--border-strong); }
.adv-node-star.on { color: var(--px-gold3); }              /* Normal — yellow */
.adv-node-star.easy.on { color: var(--px-grn3); }         /* Easy — green (matches the Easy pill) */
.adv-node-star.hard.on { color: var(--px-red3); }         /* Hard — crimson */

/* ── Difficulty selector (Easy · Normal · Hard) ── */
.adv-diff-row { display: flex; gap: 8px; justify-content: center; margin: -0.5rem 0 1.5rem; flex-wrap: wrap; }
.adv-diff-pill { font-family: 'Silkscreen', monospace; font-weight: 800; font-size: 16px; letter-spacing: .02em;
  padding: 6px 16px; border-radius: 0; cursor: pointer; color: var(--text);
  background: rgba(0,0,0,0.35); border: 1px solid var(--border-strong); transition: all .12s ease; }
.adv-diff-pill:hover:not(.locked):not(.on) { border-color: var(--px-gold3); color: var(--px-gold3); }
.adv-diff-pill.locked { opacity: .5; cursor: not-allowed; }
.adv-diff-easy.on   { background: var(--px-grn2); border-color: var(--px-grn3); color: var(--px-ash4); box-shadow: 0px 0px 0 2px rgba(95,209,122,.3); }
.adv-diff-normal.on { background: var(--px-gold1); border-color: var(--px-gold3); color: var(--px-org0); box-shadow: 0px 0px 0 2px rgba(255,216,77,.3); }
.adv-diff-hard.on   { background: var(--px-red2); border-color: var(--px-red4); color: var(--px-ash4); box-shadow: 0px 0px 0 2px rgba(255,107,107,.35); }

/* Difficulty badge on the level start popup */
.b-diff-badge { font-family: 'Silkscreen', monospace; font-weight: 800; font-size: 8px; padding: 2px 9px; border-radius: 0; vertical-align: middle; margin-left: 4px; }
.b-diff-easy   { background: rgba(95,209,122,.2); color: var(--px-grn3); border: 1px solid var(--px-grn3); }
.b-diff-normal { background: rgba(255,216,77,.18); color: var(--px-gold3); border: 1px solid var(--px-gold3); }
.b-diff-hard   { background: rgba(255,107,107,.2); color: var(--px-red4); border: 1px solid var(--px-red4); }

/* Stars on the battle-over screen */
.adv-over-stars { font-size: 32px; letter-spacing: 6px; margin: 0.25rem 0 0.75rem; }
.adv-star { color: var(--border-strong); }
.adv-star.on { color: var(--px-gold3); text-shadow: 0 0 10px rgba(255,216,77,0.6); }                /* Normal — yellow */
.adv-star.easy.on { color: var(--px-grn3); text-shadow: 0 0 10px rgba(95,209,122,0.6); }           /* Easy — green */
.adv-star.hard.on { color: var(--px-red3); text-shadow: 0 0 10px rgba(255,90,90,0.6); }            /* Hard — crimson */

/* ─── Adventure "try me" tool glows + level footer ───────────────────────────── */
/* Slow flicker on a freshly-introduced Transform/Reforge button until first use. */
@keyframes advToolGlow {
  0%   { box-shadow: 0px 0px 0 2px rgba(240,200,64,0.25); border-color: var(--px-gold3); }
  45%  { box-shadow: 0px 0px 0 2px rgba(240,200,64,0.95); border-color: var(--px-gold3); }
  55%  { box-shadow: 0px 0px 0 2px rgba(240,200,64,0.4);  border-color: var(--px-gold3); }
  100% { box-shadow: 0px 0px 0 2px rgba(240,200,64,0.25); border-color: var(--px-gold3); }
}
.b-mode-tab.b-tool-glow {
  animation: advToolGlow 1.7s ease-in-out infinite;
  border: 1px solid var(--px-gold3);
  color: var(--px-gold3);
}

/* One-shot pulse on the reroll tab each time its counter ticks up. */
@keyframes advRerollBump {
  0%   { transform: scale(1);    box-shadow: 0px 0px 0 2px rgba(255,226,122,0); }
  40%  { transform: scale(1.13); box-shadow: 0px 0px 0 2px rgba(255,226,122,0.9); }
  100% { transform: scale(1);    box-shadow: 0px 0px 0 2px rgba(255,226,122,0); }
}
.b-mode-tab.b-reroll-bump { animation: advRerollBump 0.5s ease-out; }

/* (The battle-screen footer — level title + tip — was removed: it pushed the fixed-size
   stage past a 720p window and tripped a scrollbar. The level name + tip still show in the
   pre-level Start popup.) */

/* Armored (untransformable) words in the incoming list — metallic tint. */
.b-word-chip.b-word-armored {
  border-color: var(--px-ash3);
  background: rgba(206,212,224,0.12);
}
.b-word-chip.b-word-armored .b-word-text { color: var(--px-ash3); }

/* Jester word chip — harmless clutter; a faintly purple tint sets it apart. */
.b-word-chip.b-word-jester {
  border-color: var(--px-pur3);
  background: rgba(180,138,214,0.12);
}
.b-word-chip.b-word-jester .b-word-text { color: var(--px-pur4); }
/* Neutral (shared, contested) PvP word chip — gold, matching the lane card. */
.b-word-chip.b-word-neutral {
  border-color: var(--px-gold3);
  background: rgba(240,192,64,0.14);
}
.b-word-chip.b-word-neutral .b-word-text { color: var(--px-gold4); }
.b-word-chip.b-word-neutral .b-word-hp { color: var(--px-gold3); }
/* Illuminated word chip — no icon; text is 30% brighter than normal enemy color. */
.b-word-chip.b-word-illuminated .b-word-text { color: var(--px-red4); }

/* Ghost word chip — spectral: translucent with a pale border (matches the lane card). */
.b-word-chip.b-word-ghost {
  opacity: 0.72;
  border-color: var(--px-blu3);
  background: rgba(174,191,208,0.10);
}
.b-word-chip.b-word-ghost .b-word-text { color: var(--px-blu4); }

/* Infected (contagion) bank tile — locked until it heals. */
.tile.tile-infected {
  /* The two stripe colours were #2a2333 and #342a40, a deliberately subtle pair that both
     quantized onto pur0 and left the stripe invisible. Widened to pur0/pur1, which reads
     the way the original intended and suits the flatter palette. */
  background: repeating-linear-gradient(45deg, var(--px-pur0), var(--px-pur0) 6px, var(--px-pur1) 6px, var(--px-pur1) 12px);
  color: var(--px-pur3);
  border-color: var(--px-pur2);
  cursor: not-allowed;
  display: flex; align-items: center; justify-content: center;
  font-size: 32px;
  animation: tileInfectPulse 1s ease-in-out infinite;
}
@keyframes tileInfectPulse {
  0%,100% { box-shadow: 0px 0px 0 2px rgba(150,90,200,0.3); }
  50%     { box-shadow: 0px 0px 0 2px rgba(150,90,200,0.75); }
}

/* ─── Main-menu full-page background art ──────────────────────────────────────
   Placeholder pixel panorama from tools/gen-map-bg.js. Fixed pseudo-element so it covers
   the whole viewport but only exists while the menu screen is mounted (render() toggles
   body.on-menu); z-index -1 keeps it behind all menu content. SVG rather than a raster
   because a `cover` background lands at an arbitrary scale, and flat rects with
   crispEdges stay hard at any of them.

   It hangs off the BODY, not off .menu-screen (where it lived until 2026-08-11), because
   applyMenuScale() now puts a `zoom` on .menu-screen to fit short windows, and a
   fixed-position pseudo-element inside a zoomed element is scaled along with its host, so
   the backdrop would come out `zoom`x the viewport and stop covering it.

   image-rendering is forced back to `auto` against the global `pixelated` on `body`.
   That switch is what made the 720p background look noisy: `pixelated` makes Chromium
   rasterize an SVG at its INTRINSIC size (960x540) and then nearest-neighbour it up to the
   window, so the panorama's 1px ordered dither gets resampled at a fractional factor
   (~1.33x at 720p, ~1.68x at 1080p) and clumps into moire. With `auto` the SVG is
   rasterized once at the destination scale, where its own shape-rendering="crispEdges"
   keeps every rect hard-edged, which is exactly what the note above says the SVG is for. */
body.on-menu::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--px-ink1) url('img/scenes/scene_menu.svg') center / cover no-repeat;
  image-rendering: auto;
}

/* ─── Word-type highlights in level tips (formatTip in game.js) ──────────────── */
/* These name-chip colors mirror WORD_TYPE_COLOR (core.js) — keep the two in sync.
   The inline type ICON is now colored from WORD_TYPE_COLOR directly (not these). */
.tip-type {
  font-weight: 700;
  padding: 0 5px;
  border-radius: 0;
  background: rgba(255,255,255,0.07);
  white-space: nowrap;
}
.tip-fast        { color: var(--px-gold3); background: rgba(255,180,70,0.14); }
.tip-armored     { color: var(--px-ash3); background: rgba(206,212,224,0.14); }
.tip-shielded    { color: var(--px-blu2); background: rgba(55,211,211,0.12); }
.tip-runner      { color: var(--px-gold3); background: rgba(255,216,77,0.12); }
.tip-splitter    { color: var(--px-pur4); background: rgba(207,138,255,0.13); }
.tip-jester      { color: var(--px-pur4); background: rgba(180,138,214,0.14); }
.tip-illuminated { color: var(--px-gold4); background: rgba(255,230,168,0.13); }
.tip-mirror      { color: var(--px-blu3); background: rgba(126,192,255,0.13); }
.tip-glitch      { color: var(--px-pur4); background: rgba(181,154,230,0.14); }
.tip-viral       { color: var(--px-grn3); background: rgba(126,200,80,0.13); }
.tip-cloaked     { color: var(--px-ash2); background: rgba(170,180,192,0.13); }
.tip-ghost       { color: var(--px-blu4); background: rgba(223,231,239,0.13); }
/* Inline word-type icon swapped in for the old emoji badge by formatTip(): just the
   colored glyph (inherits its tip-<type> color), no tinted box. */
.tip-icon { background: none; width: 1.18em; height: 1.18em; vertical-align: -0.24em; margin: 0 1px; }

/* ─── In-game confirm dialog (gameConfirm in game.js) ────────────────────────── */
.gc-overlay {
  position: fixed; inset: 0; z-index: 700;
  background: rgba(0,0,0,0.6);
  display: flex; align-items: center; justify-content: center;
}
.gc-box {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 0;
  padding: 1.4rem 1.6rem;
  max-width: 400px; width: calc(100% - 48px);
  text-align: center;
  font-family: 'Silkscreen', monospace;
  box-shadow: 0px 12px 0 rgba(0,0,0,0.5);
}
.gc-msg { font-size: 16px; color: var(--text); line-height: 1.5; }
.gc-sub { font-size: 16px; color: var(--text-muted); }
.gc-buttons { display: flex; gap: 10px; justify-content: center; margin-top: 1.1rem; }

/* ── Friend room-invite banner (global, above any screen) ─────────────────── */
#friend-invite-banner {
  position: fixed;
  top: 14px; left: 50%; transform: translateX(-50%);
  z-index: 9999;
  display: flex; flex-direction: column; gap: 8px;
  width: max-content; max-width: calc(100vw - 32px);
  pointer-events: none;
}
.friend-invite-card {
  pointer-events: auto;
  display: flex; align-items: center; gap: 12px;
  background: rgba(28,30,38,0.97);
  border: 1px solid rgba(126,200,80,0.55);
  border-radius: 0;
  padding: 8px 12px;
  font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text);
  box-shadow: 0px 10px 0 rgba(0,0,0,0.5);
}
.friend-invite-card strong { color: var(--px-grn4); }

/* ── Interactive guided-battle tutorial: spotlight overlay ──
   A body-level layer (outside .battle-stage, so the stage zoom never distorts it). The
   "hole" is a positioned box whose huge spread box-shadow dims everything around it — a
   cheap spotlight with no SVG mask. The layer is click-through; only the caption catches
   clicks, so play continues on action beats. z-index sits above the battle overlays. */
.tut-overlay { position: fixed; inset: 0; z-index: 1200; pointer-events: none; }
.tut-overlay .tut-hole {
  position: fixed; left: 0; top: 0; width: 0; height: 0;
  border-radius: 0; pointer-events: none;
  box-shadow: 0px 0px 0 2px rgba(0,0,0,0.74);
  outline: 3px solid var(--px-gold3); outline-offset: 2px;
  transition: left .22s ease, top .22s ease, width .22s ease, height .22s ease;
}
/* On live / action beats the board stays fully visible — drop the dark mask, keep the ring. */
.tut-overlay.tut-no-dim .tut-hole { box-shadow: none; }
.tut-overlay .tut-caption {
  position: fixed; left: 0; top: 0; width: min(360px, 88vw); box-sizing: border-box;
  pointer-events: auto;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: 0;
  padding: 14px 16px; box-shadow: 0px 10px 0 rgba(0,0,0,0.55);
  font-family: 'Silkscreen', monospace; font-size: 16px; line-height: 1.5;
}
.tut-overlay .tut-caption b { color: var(--px-gold3); }
.tut-overlay .tut-hint { margin-top: 10px; padding: 8px 10px; border-radius: 0;
  background: rgba(255,210,74,0.12); border: 1px solid rgba(255,210,74,0.40);
  font-size: 16px; line-height: 1.45; color: var(--text); }
.tut-overlay .tut-hint b { color: var(--px-gold3); }
.tut-overlay .tut-step-no { font-size: 8px; letter-spacing: 1px; text-transform: uppercase; color: var(--text-muted); margin-bottom: 6px; }
.tut-overlay .tut-controls { display: flex; gap: 8px; justify-content: flex-end; margin-top: 12px; }
.tut-overlay .tut-controls .btn { font-size: 16px; padding: 6px 12px; }
.tut-overlay .tut-skip { background: transparent; border: 1px solid var(--border-strong); color: var(--text-muted); }
.tut-overlay .tut-next { background: var(--px-gold3); color: var(--px-gold0); font-weight: 700; border: none; }

/* ── Tutorial welcome splash (shown once, before beat 1) ────────────────────── */
.tut-splash {
  position: fixed; inset: 0; z-index: 1250;   /* above .tut-overlay, below game-confirm */
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.74); cursor: pointer;
  animation: tut-splash-fade .3s ease;
}
.tut-splash.tut-splash-out { opacity: 0; transition: opacity .35s ease; }
/* The card rides in an anchor so tutPositionSplash() can park it on the middle of the lane
   band; left unpositioned (no canvas yet) the flex centring above still applies. */
.tut-splash-anchor { max-width: 92vw; }
.tut-splash-card { text-align: center; animation: tut-splash-pop .55s cubic-bezier(.2, 1.4, .4, 1); }
.tut-splash-burst { font-size: 48px; line-height: 1; animation: ld-pop 1.6s ease-in-out infinite; }
.tut-splash-kicker {
  font-family: 'Silkscreen', monospace; font-size: 24px; letter-spacing: 5px; text-transform: uppercase;
  color: var(--text-muted); margin-top: 14px;
}
/* Same wordmark treatment as .credits-logo, plus a third hard step in black because this
   one lands on lane art rather than a flat page. The 26px glow it replaces was doing the
   separating before, at the cost of every edge in the logo. */
.tut-splash-name {
  font-family: 'PressStart', monospace;
  font-size: 48px; font-weight: 400; letter-spacing: 0.125em; line-height: 1.15;
  color: var(--px-gold3);
  text-shadow: 0 2px 0 var(--px-gold1), 0 4px 0 var(--px-gold0), 0 6px 0 rgba(0,0,0,0.35);
}
.tut-splash-sub { font-size: 32px; font-weight: 800; letter-spacing: 2px; color: var(--text); }
@keyframes tut-splash-pop { from { transform: scale(.6); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes tut-splash-fade { from { opacity: 0; } to { opacity: 1; } }

/* ── Tutorial-complete congratulations page ─────────────────────────────────── */
.tut-done-wrap { display: flex; align-items: center; justify-content: center; min-height: 70vh; padding: 2rem 1rem; }
.tut-done-card {
  max-width: 560px; width: 100%; text-align: center;
  background: var(--surface); border: 1px solid var(--border-strong); border-radius: 0;
  padding: 2.5rem 2rem 2rem; box-shadow: 0px 18px 0 rgba(0,0,0,0.35);
}
.tut-done-title { font-size: 32px; font-weight: 900; margin: 0.4rem 0 0.75rem; color: var(--text); }
.tut-done-sub { font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text-muted); margin: 0 auto 1rem; max-width: 440px; line-height: 1.5; }
.tut-done-ready { font-family: 'Silkscreen', monospace; font-size: 16px; color: var(--text); margin: 0 0 1.25rem; }
.tut-done-ready b { color: var(--px-gold3); }
.tut-done-tiles { display: flex; gap: 8px; justify-content: center; margin: 0 0 1.75rem; }
.tut-done-buttons { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.tut-done-buttons .btn { font-size: 16px; padding: 12px 22px; }

/* Achievements */
.ach-wrap { max-width: 860px; margin: 0 auto; padding-bottom: 2rem; }
.ach-cat { font-family: 'Silkscreen', monospace; margin: 1.4rem 0 .6rem; color: var(--text-muted); font-size: 16px; letter-spacing: .06em; text-transform: uppercase; }
.ach-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; }
.ach-card { display: flex; gap: 12px; padding: 12px; border-radius: 0; background: var(--card-bg, var(--px-pur0)); border: 1px solid rgba(255,255,255,.08); }
.ach-card.ach-locked { opacity: .55; }
.ach-card.ach-got { border-color: var(--px-pur3); box-shadow: inset 0px 0px 0 2px rgba(124,92,255,.4); }
/* The unlock toast. It used to be styled entirely from a cssText string in
   js/achievements.js (rounded, blurred drop, purple gradient), which put it outside every
   lint rule in this file. Only the position and the fade now live in JS. */
.ach-toast {
  position: fixed; top: 16px; left: 50%; z-index: 99999;
  display: flex; align-items: center; gap: 12px; max-width: 90vw;
  padding: 12px 18px;
  font-family: 'Silkscreen', monospace;
  color: var(--px-ash4);
  background: var(--px-pur1);
  border: 2px solid var(--px-pur3);
  box-shadow: 4px 4px 0 var(--px-shadow);
  transform: translate(-50%, -24px);
  opacity: 0;
  transition: opacity .3s, transform .3s;
}
.ach-toast.ach-toast-in { opacity: 1; transform: translate(-50%, 0); }
.ach-toast-icon { font-size: 24px; line-height: 1; }
.ach-toast-kicker { display: block; font-size: 8px; letter-spacing: .08em; text-transform: uppercase; color: var(--px-pur4); }
.ach-toast-name { font-size: 16px; }

/* Mid-match "connection lost" banner (pvpShowDcBanner in js/modes.js), same story. */
#net-dc-banner {
  position: fixed; top: 18%; left: 50%; transform: translateX(-50%); z-index: 999;
  padding: 14px 24px; text-align: center;
  font-family: 'Silkscreen', monospace; font-weight: bold;
  color: var(--px-ash4);
  background: var(--px-ink1);
  border: 2px solid var(--px-red3);
  box-shadow: 4px 4px 0 var(--px-shadow);
}
#net-dc-banner .dc-sub { font-weight: normal; color: var(--px-ash2); }

/* Cutscene still: hard frame, no rounded corner. */
.cutscene-img { max-width: 80%; max-height: 50vh; object-fit: contain; margin-bottom: 1.5rem;
  border: 2px solid var(--px-ink4); image-rendering: pixelated; }

/* ─── Profile + friends surfaces ──────────────────────────────────────────────
   These were built from inline style="" strings in js/ui.js carrying 10px radii and raw
   hex, i.e. outside every rule in this file. Same markup, now lintable. */
.profile-box { background: var(--px-ink2); border: 2px solid var(--px-ink4); padding: 12px 14px; }
.profile-box.profile-box-flush { padding: 0; overflow: hidden; }
.profile-stats { display: flex; gap: 12px; margin-top: 18px; flex-wrap: wrap; }
.profile-stat { flex: 1 1 120px; background: var(--px-ink2); border: 2px solid var(--px-ink4);
  padding: 12px 16px; text-align: center; }
.profile-stat-num { font-size: 24px; font-weight: 800; }
.profile-stat-num.stat-win { color: var(--px-grn3); }
.profile-stat-num.stat-loss { color: var(--px-red3); }
.profile-stat-lbl { color: var(--text-muted); font-size: 8px; }
.profile-h3 { margin: 24px 0 8px; font-size: 16px; }
.profile-h3 .profile-h3-sub { color: var(--text-muted); font-weight: 400; font-size: 8px; }
.profile-empty { color: var(--text-muted); font-size: 8px; }
.mh-win { font-weight: 700; color: var(--px-grn3); }
.mh-loss { font-weight: 700; color: var(--px-red3); }
.mh-delta-up { color: var(--px-grn3); font-weight: 600; }
.mh-delta-down { color: var(--px-red3); font-weight: 600; }
.mh-cell { padding: 6px 10px; }
.mh-cell.mh-mid { text-align: center; }
.mh-cell.mh-when { color: var(--text-muted); font-size: 8px; }
.profile-oppname { cursor: pointer; text-decoration: underline dotted; font-family: 'PressStart', monospace; font-weight: 400; }

.friend-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  background: var(--px-ink2); border: 2px solid var(--px-ink3); margin-bottom: 6px; }
.friend-row.friend-row-on { background: var(--px-grn0); border-color: var(--px-grn1); }
/* Presence pip: a hard square, because a 9px circle cannot be drawn on this grid. */
.friend-dot { display: inline-block; width: 8px; height: 8px; background: var(--px-ash0); }
.friend-dot.on { background: var(--px-grn3); }

.friend-grow, .friend-name { flex: 1; }
/* Usernames are player-written, so their case is theirs to keep — Press Start (see the
   mixed-case note at the top). They read a little larger than the row around them. */
.friend-name { cursor: pointer; font-family: 'PressStart', monospace; font-weight: 400; }
.friend-status { color: var(--text-muted); font-size: 8px; }
.friend-status.friend-status-live { color: var(--px-gold3); }
.btn.btn-sm { font-size: 8px; padding: 4px 12px; }

/* Staff cheat badge (js/ui.js). */
.cheat-badge { font-size: 8px; color: var(--px-gold3); font-weight: 700; padding: 3px 8px;
  background: var(--px-gold0); border: 2px solid var(--px-gold2); }

/* ─── Admin + legacy story surfaces ───────────────────────────────────────────
   Also lifted out of inline style="" strings. */
.admin-seed-picker { display: flex; flex-wrap: wrap; max-height: 160px; overflow: auto;
  padding: 6px; border: 2px solid var(--border-strong); }
.admin-io-report { margin-top: 12px; padding: 10px 12px; background: var(--info-bg);
  border: 2px solid var(--px-blu1); font-size: 8px; line-height: 1.7; }
.gc-sub.gc-err { color: var(--px-red3); margin-top: 6px; }
.admin-playing { color: var(--px-gold3); }
/* Hard two-stop bands, not smooth gradients. */
.etyma-story-screen.story-bg-red { background: linear-gradient(135deg, var(--px-ink0) 0 50%, var(--px-red0) 50% 100%); }
.etyma-story-screen.story-bg-blue { background: linear-gradient(135deg, var(--px-ink0) 0 50%, var(--px-pur0) 50% 100%); }
.etyma-layer-number.story-accent-red { color: var(--px-red3); }
.etyma-layer-number.story-accent-gold { color: var(--px-gold3); font-size: 16px; }
.story-quiet { font-style: italic; color: var(--text-muted); }

/* Battle word-display target hint + the difficulty note in the level start popup. */
.b-target-hint { color: var(--p2-dark); }
.b-target-hint.b-target-reforge { color: var(--px-gold3); }
.b-tip-hard { color: var(--px-red3); }
.b-tip-easy { color: var(--px-grn3); }
.acct-tag.acct-alert { background: var(--px-red2); }
.ach-ico { font-size: 32px; line-height: 1; width: 34px; text-align: center; }
.ach-name { font-weight: 700; font-size: 16px; }
.ach-desc { font-size: 16px; color: var(--text-muted); font-family: 'Silkscreen', monospace; margin-top: 2px; }
.ach-bar { height: 6px; border-radius: 0; background: rgba(255,255,255,.12); margin-top: 6px; overflow: hidden; }
.ach-bar-fill { height: 100%; background: var(--px-pur3); }
.ach-bar-txt { font-size: 8px; color: var(--text-muted); font-family: 'Silkscreen', monospace; margin-top: 2px; }
