/* War Nerve Tower Defense - all styling for the game.
   Split out of index.html so the markup and the script are readable on their
   own; still no build step, this is linked straight from the page. */

:root {
  --bg: #12151b;
  --path: #232a35;
  --grid-line: rgba(255, 255, 255, 0.05);
  --ink: #eef1f6;
  --muted: #838da0;
  --panel: rgba(20, 24, 31, 0.85);
  --panel-border: rgba(255, 255, 255, 0.1);
  --accent: #4c9eff;
  /* The Improviser's orange, reused as the menu's primary-action colour. */
  --accent-orange: #ff9f4c;
  --danger: #ff5d5d;
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  /* Player-set UI scale (start menu). Applied via zoom to the chrome panels
     below, never the game canvas, so only the UI grows/shrinks. */
  --ui-scale: 1.3;
}

/* Scale the persistent on-screen game chrome by the player's chosen UI size.
   These are the content-sized, edge-anchored panels - not their full-width
   flex wrappers (#hud / #buildBar), which would overflow if zoomed, and NOT
   the menu overlays (.overlay-card), which are full modals that would balloon
   out of the viewport at large sizes. */
#hud > *,
#buildBar > *,
#editorBar,
#bossHealthBar,
.announcement-banner,
#targetPickBar,
#keybindLegend {
  zoom: var(--ui-scale, 1);
}

* {
  box-sizing: border-box;
}

/* Keep the hidden attribute authoritative even for elements whose class
   styles set an explicit display (e.g. .build-option's display: flex). */
[hidden] {
  display: none !important;
}

html, body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  height: 100%;
  background: var(--bg);
  font-family: var(--font-body);
  font-size: 125%;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
}

input, select {
  -webkit-user-select: text;
  user-select: text;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* While editing, the board yields the left column to the editor controls so
   they sit beside it rather than over it. The tile size is derived from the
   same reserved width (see EDITOR_GUTTER), so the board still fits. */
body.editing {
  padding-left: 200px;
}

canvas {
  display: block;
  background: var(--bg);
  transition: box-shadow 0.2s ease;
}


#hud {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  /* Chips keep their natural height - without this the Lives chip
     stretches to match the funds column as queued-expense lines pile up. */
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  padding: max(10px, env(safe-area-inset-top)) 10px 0;
  z-index: 10;
  pointer-events: none;
}

/* Lives read-out and the time (pause/resume) control share one frame,
   the same treatment the funds column gets on the opposite side. */
.lives-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 0 10px 8px;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  pointer-events: auto;
}

.lives-col .chip {
  background: none;
  border: none;
  padding-bottom: 2px;
}

.lives-btn-row {
  display: flex;
  gap: 6px;
  width: 100%;
}

#pauseBtn, #speedBtn {
  -webkit-appearance: none;
  appearance: none;
  flex: 1;
  height: 34px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--panel-border);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.04);
  color: var(--ink);
  font-size: 1rem;
}

#pauseBtn:active, #speedBtn:active {
  transform: translateY(1px) scale(0.96);
}

/* Paused reads white, not the accent blue - the blue glow is the
   fast-forward button's signature and the two shouldn't look alike. */
#pauseBtn.paused {
  border-color: var(--ink);
  color: var(--ink);
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.35);
  animation: pauseFlutter 0.9s ease-in-out infinite;
}

#speedBtn.fast {
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 0 10px rgba(76, 158, 255, 0.35);
}

/* Top speed (4x) glows yellow instead of the accent blue, so it reads as
   distinct from 2x at a glance. */
#speedBtn.fast-max {
  border-color: #ffd54a;
  color: #ffd54a;
  box-shadow: 0 0 10px rgba(255, 213, 74, 0.45);
}

/* Down to the last few lives: the Lives chip takes the same glowing frame
   the fast-forward button wears, in red, so the danger reads at a glance. */
#livesChip.critical {
  border-color: var(--danger);
  box-shadow: 0 0 10px rgba(255, 93, 93, 0.45);
}

@keyframes pauseFlutter {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

.chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 1px;
  padding: 6px 12px;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 12px;
}

.chip-label {
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.chip-value {
  font-size: 1.35rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.chip-value.low {
  color: var(--danger);
}

/* The ticks span the whole chip rather than sitting as a short run in the
   middle of it: the row stretches, and each tick takes an equal share of it,
   so the cycle reads as a progress track across the wave display. */
.wave-bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 20px;
  padding-top: 4px;
  /* The chip is a column, so width comes from stretching across its cross
     axis rather than from flex-grow. */
  align-self: stretch;
  width: 100%;
  min-width: 0;
}

/* Which loop of the boss roster you're on, sat beside the wave ticks. Every
   loop makes enemies tougher, so it's a difficulty readout, not decoration. */
.boss-cycle {
  margin-left: 6px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #ffd54a;
  font-variant-numeric: tabular-nums;
}

.wave-bar {
  flex: 1;
  min-width: 0;
  height: 100%;
  border-radius: 1px;
  background: rgba(255, 255, 255, 0.14);
}

.wave-bar.filled {
  background: var(--accent);
}

/* Left alone for a while, the menu fades off the demo playing behind it. It
   still swallows input while invisible (the board isn't the player's), and
   any move/keypress brings it straight back - see the idle timer in JS. */
#startOverlay > .overlay-card {
  transition: opacity 1.4s ease;
}

/* Only the card fades - the overlay's own dark scrim stays, so the demo
   behind it keeps reading as a backdrop rather than the live game. */
#startOverlay.idle > .overlay-card {
  opacity: 0;
}

/* Game chrome, all of it, is hidden on the menus: behind the start screen the
   board plays itself as a backdrop, and funds/wave/lives readouts belong to a
   run the player is actually in. Shown once a run begins. */
body:not(.in-game) #buildBar,
body:not(.in-game) #keybindLegend,
body:not(.in-game) #hud,
body:not(.in-game) #announcementBanner,
body:not(.in-game) #hintAnnounce,
body:not(.in-game) #bossHealthBar {
  display: none;
}

#buildBar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: max(14px, env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  z-index: 10;
  pointer-events: none;
}

/* Keyboard shortcut legend, lower-left. Dim by default so it reads as
   background furniture next to the board, brightening on hover for a
   player who actually goes looking for it. Hidden on touch-only devices
   (the phone-mode block below), where there are no keys to press. */
#keybindLegend {
  position: fixed;
  left: max(14px, env(safe-area-inset-left));
  bottom: max(14px, env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 5px;
  z-index: 10;
  pointer-events: none;
  opacity: 0.4;
  transition: opacity 0.15s ease;
}

#keybindLegend:hover {
  opacity: 0.85;
}

/* A thin window has no room beside the board for the shortcut list - it
   ends up crowding the build bar, so it drops out entirely. */
@media (max-width: 900px) {
  #keybindLegend {
    display: none;
  }
}

.keybind-row {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 0.68rem;
  color: var(--muted);
}

.keybind-row span {
  margin-left: 3px;
}

.keybind-row kbd {
  font-family: inherit;
  font-size: 0.64rem;
  line-height: 1;
  padding: 3px 5px;
  min-width: 9px;
  text-align: center;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 3px;
}

#formationBar, #targetPickBar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: max(14px, env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  z-index: 12;
}

.formation-bar-row {
  display: flex;
  gap: 10px;
  pointer-events: auto;
}

/* Build stays centred on screen no matter what sits beside it: the row
   centres Build alone, and Blueprint hangs off its right edge rather than
   sharing the row's width (which would push Build off-centre). */
.build-bar-row {
  position: relative;
  display: flex;
  justify-content: center;
  pointer-events: none;
}

/* The primary action on the board, so it outsizes the other .build-btn
   users (the formation and target-pick bars keep the base size). */
#buildMenuBtn {
  font-size: 1rem;
  padding: 15px 72px;
  border-radius: 26px;
}

/* A blueprint row: the place-it button takes the width, its delete sits
   alongside at a fixed size. */
.blueprint-row {
  display: flex;
  align-items: stretch;
  gap: 6px;
  width: 100%;
}

.blueprint-row .build-option {
  flex: 1;
}

.blueprint-delete {
  -webkit-appearance: none;
  appearance: none;
  flex: 0 0 auto;
  width: 44px;
  border: none;
  border-radius: 14px;
  background: rgba(255, 93, 93, 0.14);
  color: var(--danger);
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 700;
}

.blueprint-delete:active {
  transform: translateY(1px) scale(0.98);
}

/* An icon rather than a labelled button: it hangs off Build's edge, so
   keeping it round and narrow leaves Build unambiguously the centre of the
   bar instead of one of two competing buttons. */
.blueprint-btn {
  position: absolute;
  /* Blueprints belongs to Build - it places things - so it hangs off Build's
     right edge. Repair and Demolish are a separate pair (see below). */
  left: 100%;
  margin-left: 10px;
  /* Stretch to the row's height so the icon matches the Build button; keep it
     a circle by tying width to that height. */
  top: 0;
  bottom: 0;
  aspect-ratio: 1;
  pointer-events: auto;
  -webkit-appearance: none;
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid var(--panel-border);
  border-radius: 50%;
  background: var(--panel);
  color: var(--muted);
  font-size: 1.1rem;
  line-height: 1;
}

.blueprint-btn:active {
  transform: translateY(1px) scale(0.97);
}

/* Repair and Demolish are the "maintenance" pair: they act on towers that
   already exist, so they sit together past a deliberate gap, separate from
   Build/Blueprints which place new ones. All the chips hang off Build's
   right edge (left: 100%), spaced by margin - the row shrink-wraps Build,
   so that edge is the button itself. */
.repair-btn {
  margin-left: 96px;
}

/* One chip width (the chip is as wide as the bar is tall, ~54px) plus a
   10px gap past Repair, so the two never overlap. */
.demolish-btn {
  margin-left: 160px;
}

/* Armed states: green for Repair, red for Demolish. */
.repair-btn.armed {
  border-color: #5be08a;
  color: #5be08a;
  background: rgba(91, 224, 138, 0.16);
}

.demolish-btn.armed {
  border-color: var(--danger);
  color: var(--danger);
  background: rgba(255, 93, 93, 0.16);
}

#formationError {
  color: var(--danger);
}

.wave-notice {
  min-height: 1em;
  font-size: 0.72rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.build-hint {
  min-height: 1.1em;
  font-size: 0.86rem;
  font-weight: 600;
  text-align: center;
  color: var(--ink);
}

/* Can't-afford variant reads in the warning colour so it stands out. */
.build-hint.warn {
  color: var(--danger);
}

/* The title used to be pushed clear of Start by the size picker between them.
   With the picker gone to its own screen, the gap is the title's own. */
.start-title {
  margin-bottom: 44px;
}

.designer-credit {
  margin: 14px 0 0;
  font-size: 0.72rem;
  color: var(--muted);
  text-align: center;
}
.designer-credit a {
  color: var(--accent);
  text-decoration: none;
}
.designer-credit a:hover {
  text-decoration: underline;
}
.game-version {
  display: block;
  margin-top: 4px;
  opacity: 0.7;
}
.copyright-line {
  display: block;
  margin-top: 4px;
  opacity: 0.55;
}

.wn-logo {
  width: 150px;
  height: auto;
  display: block;
  margin: 0 auto 14px;
}

/* Fog toggle on the board-select screen fills the card width so its checkbox
   sits at the right edge like the option rows it came from. */
/* The board pick is a continuation of Start, so it carries Start's orange
   rather than the blue accent the rest of the UI uses. Redefining the
   variable inside the overlay recolours everything that derives from it
   (buttons, focus rings, the section headings) in one place. */
#sizeOverlay {
  --accent: var(--accent-orange);
}

.size-fog {
  width: 100%;
}

/* Photosensitivity warning shown on load, then fades to reveal the game. */
#seizureWarning {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: #0a0f1e;
  color: #eef1f6;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 24px;
  font-family: var(--font-body);
  opacity: 1;
  transition: opacity 0.6s ease;
}
#seizureWarning.hidden {
  opacity: 0;
  pointer-events: none;
}
#seizureWarning .seizure-inner {
  max-width: 460px;
  opacity: 0;
  animation: seizureTextFade 3.5s ease forwards;
}
@keyframes seizureTextFade {
  0%   { opacity: 0; }
  20%  { opacity: 1; }
  80%  { opacity: 1; }
  100% { opacity: 0; }
}
#seizureWarning .seizure-icons {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 22px;
  margin-bottom: 22px;
}
#seizureWarning .seizure-icon {
  width: 52px;
  height: 52px;
  color: var(--danger);
}
#seizureWarning .seizure-heading {
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: 16px;
  color: var(--danger);
}
#seizureWarning p {
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--muted);
}

/* The board-pick screen is start-menu chrome, not the debug panel, so its
   rows and number fields are sized to match the pills and dropdown around
   them rather than the tighter Options layout. */
.size-extras {
  width: 100%;
  text-align: left;
}

.size-extras .opt-row {
  width: 100%;
  padding: 4px 0;
  font-size: 0.82rem;
}

.size-extras .opt-row input[type="number"] {
  width: 96px;
  padding: 8px 12px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.06);
  font-size: 0.9rem;
  font-weight: 700;
  text-align: right;
}

/* The stepper arrows sit oddly against the rounded pill and are redundant
   next to typing or the scroll wheel. */
.size-extras .opt-row input[type="number"]::-webkit-inner-spin-button,
.size-extras .opt-row input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.size-extras .opt-row input[type="number"] {
  -moz-appearance: textfield;
}

/* Booleans across the menus read as a pill-shaped switch rather than the
   OS checkbox, so they sit with the rounded fields and buttons around them.
   Applies wherever a checkbox appears in an opt-row or a ui-size-row. */
.opt-row input[type="checkbox"],
.ui-size-row input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  flex: none;
  position: relative;
  width: 38px;
  height: 22px;
  margin: 0;
  border: 1px solid var(--panel-border);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease;
}

/* The knob. Slides across when the switch is on. */
.opt-row input[type="checkbox"]::after,
.ui-size-row input[type="checkbox"]::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 3px;
  width: 16px;
  height: 16px;
  transform: translateY(-50%);
  border-radius: 50%;
  background: var(--muted);
  transition: left 140ms ease, background 140ms ease;
}

.opt-row input[type="checkbox"]:checked,
.ui-size-row input[type="checkbox"]:checked {
  background: rgba(91, 224, 138, 0.28);
  border-color: rgba(91, 224, 138, 0.55);
}

.opt-row input[type="checkbox"]:checked::after,
.ui-size-row input[type="checkbox"]:checked::after {
  left: 18px;
  background: #5be08a;
}

.opt-row input[type="checkbox"]:disabled,
.ui-size-row input[type="checkbox"]:disabled {
  opacity: 0.55;
  cursor: default;
}

.size-select {
  /* Sized to the card like every other overlay child - without this the
     grid below is free to grow to its widest hint and spill into the
     card's side padding. */
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* Top margin makes the title -> picker gap (this + the card's 12px gap =
     32px) match the padding above the title (the card's 32px top padding). */
  margin: 20px 0 14px;
}

.size-label {
  font-size: 0.72rem;
  opacity: 0.65;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.size-row {
  display: flex;
  justify-content: center;
  gap: 12px;
  /* Bottom-align so the square frames read as increasing sizes left to right. */
  align-items: end;
}

/* Each button is a square sized to the board it represents. */
.size-btn[data-board="small"] { width: 74px; height: 74px; }
.size-btn[data-board="normal"] { width: 98px; height: 98px; }
.size-btn[data-board="large"] { width: 124px; height: 124px; }

.size-btn {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  flex: 0 0 auto; /* keep the per-button square size, don't stretch */
  box-sizing: border-box;
  padding: 8px;
  border: 2px solid transparent;
  border-radius: 12px;
  font-family: var(--font-body);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* Every size button is the same blue, and the picked one is marked by a white
   outline rather than a different fill. Scoped to the overlay so it outranks
   the global `.overlay button` rule, which would otherwise paint them with
   this screen's orange --accent. */
#sizeOverlay .size-btn {
  background: #4c9eff;
  color: #0a1420;
}

#sizeOverlay .size-btn.picked {
  border-color: var(--ink);
}

.size-btn:active {
  transform: translateY(1px) scale(0.97);
}

.size-btn-name {
  font-weight: 700;
  font-size: 0.95rem;
}

/* No accent focus ring on the size buttons - the global overlay rule would
   otherwise leave a blue outline on the last-clicked one. */
.size-btn:focus,
.size-btn:focus-visible {
  outline: none;
}

.build-btn {
  position: relative;
  pointer-events: auto;
  -webkit-appearance: none;
  appearance: none;
  border: 1px solid var(--panel-border);
  background: var(--panel);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 700;
  padding: 10px 24px;
  border-radius: 20px;
}

.build-btn.exit-mode {
  background: rgba(255, 93, 93, 0.18);
  border-color: rgba(255, 93, 93, 0.7);
  color: #ff7b7b;
}

.build-suggest-icon {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #5be08a;
  color: #0d3a1f;
  border: 1px solid #1f6b3d;
  font-size: 0.7rem;
  font-weight: 800;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  display: none;
}

.build-btn:active {
  transform: translateY(1px) scale(0.98);
}

.build-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.build-options {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
}

.overlay-card .build-option {
  position: relative;
  -webkit-appearance: none;
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  border: none;
  background: rgba(255, 255, 255, 0.04);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 700;
  padding: 12px 16px;
  border-radius: 14px;
}

.build-option-badge {
  position: absolute;
  top: -6px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  font-size: 0.65rem;
  font-weight: 800;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  display: none;
}

.pending-badge {
  right: -6px;
  width: auto;
  min-width: 18px;
  padding: 0 4px;
  border-radius: 9px;
  white-space: nowrap;
  background: #ffd54a;
  color: #4a3600;
  border: 1px solid #7a5c00;
}

.spec-pending-badge {
  position: absolute;
  top: -6px;
  left: -6px;
  width: auto;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 9px;
  white-space: nowrap;
  font-size: 0.65rem;
  font-weight: 800;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  display: none;
  background: #ffd54a;
  color: #4a3600;
  border: 1px solid #7a5c00;
}

.build-option-progress {
  position: absolute;
  left: 6px;
  right: 6px;
  bottom: 4px;
  height: 8px;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.4);
  overflow: hidden;
  display: none;
  pointer-events: none;
}

.build-option-progress-fill {
  display: block;
  height: 100%;
  background: #ffd54a;
  width: 0%;
  transition: width 0.15s ease;
}

.overlay-card .build-option .build-option-cost {
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--muted);
}

.overlay-card .build-option.armed {
  background: var(--accent);
  color: #0a1420;
}

.overlay-card .build-option.armed .build-option-cost {
  color: rgba(10, 20, 32, 0.7);
}

.overlay-card .build-option[data-type="sniper"].armed {
  background: #b06bff;
}

.overlay-card .build-option[data-type="trap"].armed {
  background: #2dd9c4;
}

.overlay-card .build-option[data-type="remove"] {
  color: var(--danger);
}

.overlay-card .build-option.unaffordable {
  opacity: 0.45;
}

.overlay-card .build-option:active:not(.unaffordable) {
  transform: translateY(1px) scale(0.98);
}

.overlay-card .build-option:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.target-mode-section {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.overlay-copy.tower-kills-text {
  font-size: 1.05rem;
  font-weight: 600;
}

/* Medal count in the tower menu: a funds pill beside the kill count, using
   the same funds as the medal badge drawn on the tower itself so the two
   are recognisably the same award. */
.medal-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 8px;
  padding: 1px 8px;
  border-radius: 999px;
  background: rgba(255, 213, 74, 0.16);
  border: 1px solid rgba(255, 213, 74, 0.5);
  color: #ffd54a;
  font-size: 0.72rem;
  font-weight: 700;
  vertical-align: middle;
}

.medal-badge[hidden] {
  display: none;
}

.overlay-copy.tower-stats-line {
  font-size: 0.72rem;
  opacity: 0.75;
  margin-top: -8px;
  white-space: pre-line;
  line-height: 1.6;
}

.target-mode-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.overlay-card .target-mode-btn {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
  flex: 1 1 calc(50% - 6px);
  border: none;
  background: rgba(255, 255, 255, 0.04);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 700;
  padding: 8px 10px;
  border-radius: 10px;
}

.overlay-card .target-mode-btn.armed {
  background: var(--accent);
  color: #0a1420;
}

.overlay-card .target-mode-btn:active {
  transform: translateY(1px) scale(0.98);
}

.target-mode-reset-icon {
  display: none;
  position: absolute;
  top: 4px;
  right: 4px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.3);
  color: inherit;
  align-items: center;
  justify-content: center;
  font-size: 0.7rem;
  line-height: 1;
}

.overlay-card .target-mode-btn.can-revert .target-mode-reset-icon {
  display: flex;
}

.target-mode-reset-icon:active {
  transform: scale(0.9);
}

/* Relocate + bulldoze live on the revert row as icons pinned to the
   button's top-right corner, mirroring the target-mode reset icons.
   Bulldoze sits at the far right; relocate sits just to its left. */
#towerRevertBtn {
  padding-right: 90px;
}

#towerRevertBtn .target-mode-reset-icon {
  display: flex;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  width: 30px;
  height: 30px;
  font-size: 1.05rem;
}

#towerRevertBtn .target-mode-reset-icon:active {
  transform: translateY(-50%) scale(0.9);
}

#towerRevertBtn #towerRelocateBtn {
  right: 44px;
}

.overlay-card .spec-option-btn {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  text-align: left;
  padding: 8px 10px;
}

.spec-option-label {
  font-size: 0.78rem;
  font-weight: 700;
}

.spec-option-desc {
  font-size: 0.66rem;
  font-weight: 500;
  color: var(--muted);
}

.overlay-card .spec-option-btn.armed .spec-option-desc {
  color: rgba(10, 20, 32, 0.7);
}

.overlay-card .spec-option-btn.unaffordable {
  opacity: 0.45;
}

.wave-banner {
  position: fixed;
  top: max(64px, calc(env(safe-area-inset-top) + 54px));
  left: 50%;
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 20px;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 14px;
  opacity: 0;
  pointer-events: none;
  z-index: 15;
  transform: translate(-50%, -8px);
  transition: opacity 0.25s ease, transform 0.25s ease;
}

.wave-banner.visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

.announcement-banner {
  top: max(150px, calc(env(safe-area-inset-top) + 140px));
}

.announcement-text {
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.02em;
}

/* A hint is a footnote, not an interruption: a small, quiet strip that sits
   out of the way above the build bar instead of a boxed banner across the
   middle of the board, and slides up rather than appearing over the fight. */
#hintAnnounce {
  position: fixed;
  bottom: max(96px, calc(env(safe-area-inset-bottom) + 96px));
  left: 50%;
  z-index: 20;
  pointer-events: none;
  text-align: center;
  max-width: min(70vw, 460px);
  line-height: 1.3;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: rgba(255, 210, 63, 0.9);
  text-shadow: 0 1px 5px rgba(0, 0, 0, 0.8);
  padding: 7px 14px;
  background: rgba(20, 24, 31, 0.72);
  border: 1px solid rgba(255, 210, 63, 0.35);
  border-radius: 10px;
  opacity: 0;
  transform: translate(-50%, 8px);
  transition: opacity 0.3s ease, transform 0.3s ease;
}
#hintAnnounce.visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

.boss-health-bar {
  position: fixed;
  top: max(96px, calc(env(safe-area-inset-top) + 86px));
  left: 50%;
  transform: translateX(-50%);
  display: none;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 16px;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 14px;
  z-index: 14;
  pointer-events: none;
  min-width: 220px;
}

.boss-health-bar.visible {
  display: flex;
}

.boss-health-label {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--danger);
}

.boss-health-track {
  width: 220px;
  height: 10px;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.4);
  overflow: hidden;
}

.boss-health-fill {
  height: 100%;
  background: var(--danger);
  width: 100%;
  transition: width 0.15s ease;
}

@keyframes chip-pulse {
  0% { transform: scale(1); }
  35% { transform: scale(1.25); }
  100% { transform: scale(1); }
}

.chip-value.pulse, .wave-bars.pulse {
  animation: chip-pulse 0.4s ease;
}

/* The wave chip reads a quarter larger than the strength meter below it: the
   wave is the thing you check at a glance, the strength readout is detail you
   study. Sizes here are 1.25x their .hud-pill counterparts. */
#waveChip {
  transition: background 0.3s ease, border-color 0.3s ease;
  transform-origin: center;
  min-width: 250px;
  /* Even on all four sides. The shared .chip padding is 6px/12px, and the
     narrow sides were there to let the tick row run wide - the ticks stretch
     to the chip now, so they don't need the extra room. */
  padding: 8px;
  font-size: 0.85rem;
}

#waveChip .chip-label {
  font-size: 0.85rem;
}

#waveChip .chip-value {
  font-size: 1.69rem;
}

@keyframes wave-chip-shift {
  0%   { transform: scaleX(1) rotate(0deg); }
  14%  { transform: scaleX(1.05) rotate(-7deg); }
  32%  { transform: scaleX(1.32) rotate(5deg); }
  58%  { transform: scaleX(1.1) rotate(-2deg); }
  100% { transform: scaleX(1) rotate(0deg); }
}

#waveChip.chip-shift {
  animation: wave-chip-shift 420ms ease-out;
}

#waveChip.wave-flash {
  background: rgba(76, 158, 255, 0.18);
  border-color: var(--accent);
}

#waveChip.wave-flash.boss {
  background: rgba(255, 93, 93, 0.18);
  border-color: var(--danger);
}

#waveChip.wave-flash #waveChipLabel {
  color: var(--accent);
}

#waveChip.wave-flash.boss #waveChipLabel {
  color: var(--danger);
}

.wave-col, .funds-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

/* The wave chip with its next-boss note alongside. The note is absolutely
   placed so it can't stretch the row or shift the chip as its text changes
   length between boss names. */
.wave-chip-row {
  position: relative;
  display: flex;
  align-items: center;
}

.next-boss-tag {
  position: absolute;
  left: calc(100% + 10px);
  display: flex;
  align-items: center;
  white-space: nowrap;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  pointer-events: none;
}

/* The boss that's coming, shown as its own portrait. Hidden when the active
   pack has no sprite for it, where the name is shown instead. */
#nextBossIcon {
  display: none;
  width: 34px;
  height: 34px;
  object-fit: contain;
  /* Unlit: it's a warning of what's ahead, not a thing on the board yet. */
  filter: grayscale(0.35) brightness(0.85);
}

/* The whole funds display (amount, spendable balance, queued expenses)
   lives in one shared frame; the pieces inside drop their own panels. */
.funds-col {
  gap: 2px;
  padding: 0 10px 8px;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 12px;
}

.funds-col .chip,
.funds-col .icon-badge {
  background: none;
  border: none;
}

.funds-col .icon-badge {
  height: auto;
  min-width: 0;
  padding: 0;
}

.funds-col .icon-badge.danger {
  color: var(--danger);
}

/* Negative funds: the digits stay white, only the leading minus is red. */
.funds-neg {
  color: var(--danger);
}

.hud-pill {
  display: none;
  align-items: baseline;
  gap: 6px;
  padding: 4px 10px;
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  font-size: 0.68rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: var(--muted);
  pointer-events: none;
}

#strengthMeter {
  color: #5be08a;
  flex-direction: column;
  align-items: stretch;
  gap: 3px;
  min-width: 200px;
  /* Even on all four sides. The shared .hud-pill padding is 4px/10px, which
     suits a single-line chip but leaves this stacked panel looking pinched
     top and bottom against its bar and labels. */
  padding: 8px;
}

.strength-row {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
}

.strength-section-label {
  font-size: 0.52rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  opacity: 0.75;
  text-align: center;
}

.strength-divider {
  height: 1px;
  background: var(--panel-border);
  margin: 4px 0 2px;
}

#strengthLiveBarFill {
  display: block;
  height: 100%;
  background: #5be08a;
  border-radius: 2px 0 0 2px;
  transition: width 160ms ease-out, background 200ms ease-out;
}

/* The numbers say how much; the bar says who's winning. One colour states
   the verdict - green ahead, funds too-close-to-call, red behind - and the
   split reads as two brightnesses of it: the enemy's share solid, the
   player's the dim remainder. Colour carries the meaning; the split point
   carries the margin, so the bar never asks you to compare two hues.
   Default is the winning case; the states below override it in order. */
.strength-bar {
  position: relative;
  height: 10px;
  border-radius: 5px;
  background: rgba(93, 224, 138, 0.35);
  overflow: hidden;
}

#strengthBarFill {
  display: block;
  height: 100%;
  background: #5be08a;
  border-radius: 2px 0 0 2px;
  transition: width 160ms ease-out, background 200ms ease-out;
}

/* The blue slice of the enemy fill that's predicted to arrive shielded, drawn
   over the left of the fill (both are anchored at the enemy end). */
#strengthBarShield {
  position: absolute;
  left: 0;
  top: 0;
  height: 100%;
  /* Same gunmetal the shield arcs are drawn in on the board. */
  background: #5d6675;
  border-radius: 5px 0 0 5px;
  transition: width 160ms ease-out;
  pointer-events: none;
}

/* The player's answer to that shield slice: how much of your side of the bar
   can actually break shields. Grows from the right, where the player's fill
   is, in the same gunmetal so the two read as a matched pair. */
#strengthBarAntiShield {
  position: absolute;
  right: 0;
  top: 0;
  height: 100%;
  background: #5d6675;
  border-radius: 0 5px 5px 0;
  transition: width 160ms ease-out;
  pointer-events: none;
}

/* Behind: the wave outweighs the defense (text is already warning). */
#strengthMeter.warn .strength-bar,
#strengthMeter.danger .strength-bar {
  background: rgba(255, 93, 93, 0.35);
}

#strengthMeter.warn #strengthBarFill,
#strengthMeter.danger #strengthBarFill {
  background: var(--danger);
}

/* Too close to call - last, so it wins over the warn overlap just above. */
#strengthMeter.tie .strength-bar {
  background: rgba(255, 213, 74, 0.35);
}

#strengthMeter.tie #strengthBarFill {
  background: #ffd54a;
}

#strengthMeter .strength-vs {
  color: var(--muted);
  font-weight: 400;
  font-size: 0.6rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

#strengthMeter.warn {
  color: #ffd54a;
}

.hud-pill.danger {
  color: var(--danger);
}

.icon-badge {
  display: none;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 8px;
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  background: var(--panel);
  color: var(--ink);
  font-size: 0.85rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

.icon-badge.danger {
  color: var(--danger);
  border-color: var(--danger);
}

.queued-expenses {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  pointer-events: none;
}

.queued-expense-line {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 0.6rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
}

/* Queued costs are money going out, so they read red like a debit. */
.queued-expense-cost {
  color: var(--danger);
  font-variant-numeric: tabular-nums;
}

.queued-expense-icon {
  font-size: 0.5rem;
  line-height: 1;
}

.queued-expense-icon.upgrade {
  color: #5be08a;
}

.queued-expense-icon.building {
  color: #eef1f6;
}

@media (prefers-reduced-motion: reduce) {
  /* The How to play panel arrives whole instead of unfolding in stages: the
     reveal is decoration, and the content is identical either way. The
     staging itself is skipped in JS (see howToRunReveal). */
  .howto-scroll {
    transition: none;
  }
  .howto-group.howto-arriving {
    animation: none;
  }
  .chip-value.pulse {
    animation: none;
  }
  #waveChip.chip-shift {
    animation: none;
  }
  #pauseBtn.paused {
    animation: none;
  }
  .wave-banner {
    transition: opacity 0.01s linear;
  }
}

.overlay-card .text-btn {
  -webkit-appearance: none;
  appearance: none;
  border: none;
  background: rgba(255, 255, 255, 0.04);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 700;
  padding: 12px 28px;
  border-radius: 16px;
}

.overlay-card .text-btn:active {
  opacity: 0.6;
}

.options-card {
  max-width: 380px;
  max-height: 84vh;
}

/* Above everything else on screen: .overlay's own z-index (20) sits UNDER the
   editor bar (40), which would otherwise cover the window's left edge. */
#edSpriteOverlay {
  z-index: 70;
}

/* The editor's own controls are hidden while the sprite bar is up, so the
   two aren't competing for the screen - the board being tuned is what should
   be visible. */
body.sprite-editing #editorBar {
  display: none;
}

/* Not a dialog but a working bar across the foot of the screen: the board
   above it stays fully visible and clickable while values are stepped, which
   is the whole point of tuning art live. The overlay itself keeps no scrim
   and passes clicks through; only the bar takes them. */
#edSpriteOverlay {
  align-items: flex-end;
  justify-content: center;
  /* Lifted clear of the screen edge so the bar reads as a floating panel and
     the board's lower rows aren't hidden behind it. */
  padding: 0 0 9vh;
  background: transparent;
  pointer-events: none;
}

#edSpriteOverlay > .overlay-card {
  pointer-events: auto;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  width: auto;
  max-width: min(96vw, 1180px);
  gap: 10px 14px;
  padding: 10px 16px;
  border-radius: 14px;
}

/* The eyebrow is a card heading; a bar has no room for one. */
#edSpriteOverlay .overlay-eyebrow {
  display: none;
}

/* Every group is a segment of the one row, sized to its contents. */
#edSpriteOverlay .ed-group,
#edSpriteOverlay .opt-actions,
#edSpriteControls {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  width: auto;
  margin-top: 0;
  gap: 8px;
}

#edSpriteControls {
  gap: 10px 14px;
}

/* The offset axes stack instead of running along the bar, and their rows are
   right-aligned on the value so the numbers line up in a column. */
#edSpriteOverlay .ed-axis-stack {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 4px;
}

#edSpriteOverlay .ed-axis-stack .ed-group {
  justify-content: flex-end;
  flex-wrap: nowrap;
}

/* Fixed widths so X and Y read as a column of figures, not a ragged edge. */
#edSpriteOverlay .ed-axis-stack .ed-val {
  min-width: 3.4em;
  text-align: center;
}

#edSpriteOverlay .ed-axis-stack .ed-x {
  min-width: 1.2em;
  text-align: left;
}

/* The key readout is inline text in the bar, not a paragraph. */
#edSpriteOverlay .overlay-copy {
  margin: 0;
  white-space: nowrap;
}

/* The preview sits on the board's own path colour, so art is judged against
   the background it will actually stand on rather than the panel. */
#edSpritePreview {
  width: 132px;
  height: 132px;
  flex: none;
  border-radius: 10px;
  background: #39404d;
  border: 1px solid rgba(255, 255, 255, 0.12);
}

.opt-scroll {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 18px;
  overflow-y: auto;
  /* Vertical only: a row too wide for the card wraps or shrinks rather than
     putting the whole panel on a horizontal scrollbar. */
  overflow-x: hidden;
  max-height: 52vh;
  padding: 2px 4px 2px 2px;
  text-align: left;
}

/* Rows keep their contents inside the card instead of forcing it wider: the
   label takes the slack and the control keeps its size. */
.opt-row,
.ui-size-row {
  min-width: 0;
  flex-wrap: wrap;
}

.opt-row > span,
.ui-size-row > .ui-size-label {
  min-width: 0;
  overflow-wrap: anywhere;
}

.opt-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.opt-group-title {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.opt-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.82rem;
  color: var(--ink);
}

.opt-row input[type="number"],
.opt-row select {
  -webkit-appearance: none;
  appearance: none;
  width: 84px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--panel-border);
  border-radius: 8px;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.82rem;
  padding: 6px 8px;
}

.opt-row select {
  width: auto;
}

/* Buttons that live inside an options group (editor, spawn boss, jump to wave,
   download) read as compact full-width controls that match the tight opt-rows,
   rather than the roomy start-menu pills. */
.opt-group .text-btn {
  align-self: stretch;
  padding: 8px 14px;
  font-size: 0.82rem;
  font-weight: 700;
  border-radius: 10px;
  text-align: center;
}

/* A disabled/derived field reads as read-only rather than broken. */
.opt-row input:disabled {
  opacity: 0.55;
  cursor: default;
}

.fixed-target-filter {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  /* Dark color-scheme keeps the native option popup dark instead of the
     default white, matching the rest of the menu. */
  color-scheme: dark;
  background-color: rgba(255, 255, 255, 0.06);
  /* Custom chevron since appearance:none drops the native arrow. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23838da0' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  border: 1px solid var(--panel-border);
  border-radius: 10px;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 600;
  padding: 9px 32px 9px 10px;
  margin-top: 6px;
}

.fixed-target-filter:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.fixed-target-filter option {
  background: #12151b;
  color: var(--ink);
}

.opt-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  margin-top: 2px;
}

.opt-actions button:not(.text-btn) {
  flex: 1;
}

.overlay {
  position: fixed;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(6, 8, 12, 0.39);
  z-index: 20;
}

.overlay.visible {
  display: flex;
}

/* The start overlay stacks: the menu card, then anything offered beneath it
   (see .continue-demo). Every other overlay holds a single centred card. */
#startOverlay.visible {
  flex-direction: column;
}

.overlay-card {
  width: 100%;
  max-width: 340px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 32px 26px;
  text-align: center;
  background: var(--panel);
  border: 2px solid var(--panel-border);
  border-radius: 18px;
}

/* The start card carries long prose (title + changelog) rather than the
   edge-to-edge buttons the other overlays use, so it gets roomier padding all
   round - the extra top and bottom keeps the title and the last button off
   the card's edges. */
#startOverlay .overlay-card {
  padding-left: 34px;
  padding-right: 34px;
  padding-top: 44px;
  padding-bottom: 44px;
}

/* Level editor / How to play / Debug options otherwise shrink to their text
   and end up different widths - stretch them so all three match. */
#startOverlay .overlay-card .text-btn {
  align-self: stretch;
  text-align: center;
}

/* The build and blueprint menus aren't centered modals like the other
   overlays - they're popovers that rise from their buttons in the build bar,
   echoing the tower upgrade window that pops above a tapped tower. The
   backdrop is transparent (still full-screen so a tap outside closes it),
   and the card sits just above the build bar. */
#buildOverlay,
#blueprintOverlay,
#demolishOverlay {
  align-items: flex-end;
  background: transparent;
  padding-bottom: calc(env(safe-area-inset-bottom) + 84px);
}


#buildOverlay .overlay-card,
#blueprintOverlay .overlay-card,
#demolishOverlay .overlay-card {
  max-width: 264px;
  padding: 14px;
  gap: 6px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
}

/* Compact header to match the upgrade window's small floating panel. */
#buildOverlay .overlay-eyebrow,
#blueprintOverlay .overlay-eyebrow,
#demolishOverlay .overlay-eyebrow { display: none; }

#buildOverlay .overlay-title,
#blueprintOverlay .overlay-title,
#demolishOverlay .overlay-title {
  font-size: 0.95rem !important;
  margin: 0 0 2px;
}

.overlay-eyebrow {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
}

.overlay-title {
  font-size: clamp(1.7rem, 9vw, 2.2rem);
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0;
  text-wrap: balance;
}

.overlay-copy {
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--muted);
  max-width: 30ch;
  margin: 0;
}

.overlay-stat {
  font-size: 2rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  margin: 0;
}

.changelog {
  width: 100%;
  text-align: left;
}

/* Appears only once the menu's demo has run a while, so it fades in rather
   than popping into the button stack. */
/* Lives outside the menu card, so it can't inherit `.overlay-card .text-btn`
   and carries the whole look itself. Grey rather than the card's white ink:
   it's a side offer about the battle behind the menu, not a menu step. */
.continue-demo {
  -webkit-appearance: none;
  appearance: none;
  animation: continueFade 900ms ease both;
  margin-top: 14px;
  align-self: center;
  padding: 12px 28px;
  border: 1px solid #4b5364;
  border-radius: 16px;
  /* Solid grey, so it reads as a quiet secondary button against the board
     rather than a translucent panel of it. */
  background: #454b57;
  color: #cfd5e0;
  opacity: 1;
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
}

.continue-demo:hover {
  background: #454d5f;
  color: var(--ink);
}

.continue-demo:active {
  opacity: 0.6;
}

@keyframes continueFade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.last-updated {
  font-size: 0.6rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  opacity: 0.45;
  margin-top: 10px;
  text-align: center;
}

.changelog-title {
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
  cursor: pointer;
  user-select: none;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* No disclosure marker on the "What's new" toggle - just the text. */
.changelog-title::-webkit-details-marker { display: none; }

.changelog[open] .changelog-title { margin-bottom: 6px; }

/* Capped and scrollable (like .opt-scroll) so a growing changelog can't
   push the Start button off the bottom of the start card. */
.changelog-list {
  margin: 0;
  padding-left: 16px;
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--muted);
  overflow-y: auto;
  max-height: 22vh;
  overscroll-behavior: contain;
}

/* One scrollbar look for every scrolling panel: a thin, dim thumb that
   reads as part of the panel border rather than browser chrome. Firefox
   takes the standard properties; the ::-webkit rules cover the rest. */
.changelog-list, .opt-scroll {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
}

.changelog-list::-webkit-scrollbar, .opt-scroll::-webkit-scrollbar {
  width: 6px;
}

.changelog-list::-webkit-scrollbar-track, .opt-scroll::-webkit-scrollbar-track {
  background: transparent;
}

.changelog-list::-webkit-scrollbar-thumb, .opt-scroll::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.16);
  border-radius: 3px;
}

.changelog-list::-webkit-scrollbar-thumb:hover, .opt-scroll::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.28);
}

/* Pack load failures, under the pack picker. Amber rather than red: nothing
   is broken, the game just stayed on the pack it already had. */
.pack-status {
  margin-top: 6px;
  font-size: 0.7rem;
  line-height: 1.4;
  color: #e0b25a;
}

/* UI size selector on the start menu: a compact three-way segmented control. */
.ui-size-row {
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

/* The pack picker on the Choose Battleboard screen: full width like the rows
   around it, with the failure message allowed its own line beneath. */
.size-pack-row {
  width: 100%;
  flex-wrap: wrap;
}

.size-pack-row > .pack-status {
  flex-basis: 100%;
}

/* Balance section headings, one per group in balance.json. */
#balanceFields > .opt-group-title {
  display: block;
  margin-top: 12px;
  text-transform: capitalize;
}

#balanceFields > .opt-group-title:first-child {
  margin-top: 0;
}

/* Debug: paired action buttons share a row so the panel doesn't become one
   long column of full-width pills. */
.dbg-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.dbg-row > * {
  flex: 1;
  min-width: 0;
}

/* The same numbers pinned to the board while debug is unlocked. Top-left,
   under the HUD chips, and never in the way of a click. */
/* Left edge, vertically centred: the top corners belong to the funds and
   lives chips and the bottom to the build bar, so this is the one strip of
   screen the HUD never uses. Above the HUD's z-index so it can't be buried. */
#dbgHud {
  position: fixed;
  top: 50%;
  left: 8px;
  transform: translateY(-50%);
  z-index: 30;
  /* Draggable, so it can be moved off whatever it's covering. It's the only
     debug element that takes input; everything under it still gets clicks
     once the readout is dragged aside. */
  cursor: grab;
  touch-action: none;
  user-select: none;
  padding: 8px 10px;
  border-radius: 8px;
  background: rgba(6, 8, 12, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.08);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.66rem;
  line-height: 1.5;
  color: #9fb0c8;
  white-space: pre;
}

#dbgHud.dragging {
  cursor: grabbing;
  border-color: rgba(255, 255, 255, 0.24);
}

/* Live numbers: monospace so figures don't jitter as they update. */
.dbg-stats {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.72rem;
  line-height: 1.6;
  color: var(--muted);
  white-space: pre;
  overflow-x: auto;
}

/* A collapsed debug section: the summary keeps the group-title look and
   gains a marker, so it reads as openable rather than as a dead heading. */
.dbg-fold > summary {
  cursor: pointer;
  list-style: revert;
}

/* Pack picker: the dropdown and its "add a pack" + button read as one
   control, so they sit together on the row's right-hand side. */
.pack-picker {
  display: flex;
  align-items: center;
  gap: 6px;
}

.pack-add {
  -webkit-appearance: none;
  appearance: none;
  width: 34px;
  height: 34px;
  flex: none;
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.06);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}

.pack-add:hover {
  background: rgba(255, 255, 255, 0.12);
}

.pack-add:active {
  opacity: 0.6;
}

/* Sub-label under a settings row: quiet explanatory text, full width so it
   wraps under both the label and its control. */
.ui-size-hint {
  align-self: stretch;
  margin: -6px 0 2px;
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--muted);
}

.ui-size-label {
  font-weight: 700;
  font-size: 0.95rem;
  color: #ffffff;
}

.ui-size-select {
  -webkit-appearance: none;
  appearance: none;
  color: var(--ink);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.9rem;
  padding: 8px 34px 8px 14px;
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  cursor: pointer;
  /* Chevron drawn inline so the control matches the overlay's flat button
     look instead of the OS-native select chrome. */
  background:
    rgba(255, 255, 255, 0.06)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23eef1f6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E")
    no-repeat right 12px center;
}

.ui-size-select:hover {
  background-color: rgba(255, 255, 255, 0.1);
}

.ui-size-select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Dropdown list itself (where the browser honours it) reads on the dark card. */
.ui-size-select option {
  background: #141820;
  color: var(--ink);
}

/* Saved custom levels on the start menu: play, re-edit or delete each. */
.custom-level-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 8px 0 2px;
  max-height: 18vh;
  overflow-y: auto;
}

.custom-level-row {
  display: flex;
  gap: 4px;
}

.custom-level-play {
  flex: 1;
  text-align: left;
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: #2c3038;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.78rem;
  cursor: pointer;
}

/* The map picker's Play button is the action of that step, so it's sized to
   read as one - twice the height and text of the row above it. */
.custom-level-go {
  text-align: center;
  padding: 12px 20px;
  font-size: 1.56rem;
  font-weight: 700;
  /* Blue against the orange size buttons, so the choice and the action that
     acts on it don't read as the same control. Set literally rather than via
     var(--accent), which this overlay redefines as orange. */
  background: #4c9eff;
  border-color: transparent;
  color: #0a1420;
}

.custom-level-edit {
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: transparent;
  color: var(--muted);
  font-family: var(--font-body);
  font-size: 0.72rem;
  cursor: pointer;
}

/* Level editor controls - a column down the left of the board, clear of the
   board itself so nothing overlaps the tiles being edited. */
#editorBar {
  position: fixed;
  left: 12px;
  /* Anchored to the top rather than centred: the bar is sized to its contents
     (below), so a tall tool list on a small window would push its top edge off
     screen if it were centred, and there is no scrolling to get it back. */
  top: 12px;
  display: none;
  flex-direction: column;
  gap: 10px;
  /* Sized to its contents in both directions - no scrolling in either axis.
     The bar shrink-wraps whatever tools are showing rather than capping its
     height and hiding the rest behind a scrollbar. */
  width: max-content;
  min-width: 178px;
  padding: 10px;
  border-radius: 10px;
  background: rgba(20, 23, 28, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.1);
  font-family: var(--font-body);
  z-index: 40;
  overflow: visible;
}

#editorBar.visible { display: flex; }

/* The editor's own strip above the board, standing in for the HUD: starting
   wave, and the player's health beside it. */
#edTopBar {
  position: fixed;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  display: none;
  align-items: center;
  gap: 14px;
  padding: 6px 12px;
  border-radius: 10px;
  background: rgba(20, 23, 28, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.1);
  font-family: var(--font-body);
  z-index: 40;
}

#edTopBar.visible { display: flex; }

.ed-top-item {
  display: flex;
  align-items: center;
  gap: 5px;
}

/* The bar sizes to its content, so these don't stretch to the column width the
   inputs use inside #editorBar. */
#edTopBar .ed-num { width: 62px; }

.ed-section {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ed-group {
  display: flex;
  align-items: center;
  gap: 3px;
}

/* Tools stack two-up rather than in one long row - the column is narrow. */
.ed-tools {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.ed-actions { gap: 4px; }

/* Buttons paired on a row (Export / Import) split it evenly rather than each
   sizing to its own label. */
.ed-actions .ed-group .ed-action {
  flex: 1;
  text-align: center;
}

.ed-x {
  font-size: 0.62rem;
  color: var(--muted);
  padding: 0 1px;
}

.ed-label {
  font-size: 0.6rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.ed-val {
  min-width: 16px;
  text-align: center;
  font-size: 0.68rem;
  color: var(--ink);
}

.ed-step, .ed-tool, .ed-action {
  padding: 3px 6px;
  border-radius: 6px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: #2c3038;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.66rem;
  cursor: pointer;
}

.ed-tool.armed {
  border-color: var(--accent);
  color: #fff;
}

/* A stepper that carries a word rather than a single glyph, so it isn't
   squeezed into the square the +/- buttons sit in. */
.ed-step.ed-wide {
  padding: 3px 8px;
  white-space: nowrap;
}

.ed-step:disabled {
  opacity: 0.4;
  cursor: default;
}

.ed-action:disabled {
  opacity: 0.4;
  cursor: default;
}

.ed-name, .ed-num {
  padding: 3px 6px;
  border-radius: 6px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: #22262c;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.66rem;
  width: 100%;
  box-sizing: border-box;
}

.ed-num { flex: 1; min-width: 0; }

.ed-status {
  font-size: 0.62rem;
  color: var(--muted);
  min-height: 1em;
}

.ed-status.bad { color: #ff6b6b; }
/* Sprite panel: the key readout turns red when the pack has no art for it,
   so stepping values that can't show up is obvious rather than baffling. */
.ed-x.bad { color: #ff6b6b; }

/* "How to play" onboarding panel - reuses the options overlay shell
   (.options-card / .opt-scroll / .opt-actions), just its own text styles. */
.howto-scroll {
  text-align: left;
  /* Shorter than the shared .opt-scroll: How to play is a reference the
     player scrolls through, not a settings list they scan, so it reads
     better as a compact panel over the board than as a full-height wall of
     text. Every group starts collapsed, so what's on screen is a short list
     of headings rather than a wall of prose. */
  max-height: 34vh;
  gap: 10px;
}

/* The window opens in stages (see howToRunReveal): Objective alone, then its
   text, then the rest of the sections. The panel's height is transitioned
   rather than animated, so each stage grows it to whatever the content now
   needs instead of running to a fixed size. */
.howto-scroll {
  /* Matched to the stage interval, so each growth finishes as the next stage
     lands rather than still moving when it does. */
  transition: max-height 1s ease-out;
}

/* A section still waiting its turn: laid out but not painted, so revealing it
   doesn't reflow what's already on screen. */
.howto-group.howto-pending {
  display: none;
}

.howto-group.howto-arriving {
  animation: howtoGroupIn 0.45s ease-out backwards;
}

@keyframes howtoGroupIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

.howto-group {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.howto-group + .howto-group {
  margin-top: 8px;
}

/* Open content hangs off the summary-button as one connected panel: no top
   border (the summary is flush above it) and rounded only at the bottom. */
details.howto-group[open] > .howto-copy,
details.howto-group[open] > .howto-list {
  padding: 10px 14px;
  border: 1px solid var(--panel-border);
  border-top: none;
  border-bottom-left-radius: 12px;
  border-bottom-right-radius: 12px;
}

details.howto-group[open] > .howto-list {
  padding-left: 30px; /* keep room for the bullet markers */
}

.howto-title {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
}

/* Each section is a collapsed <details>; the summary is the tappable header
   with a chevron that rotates open. Content spacing lives on the summary so
   the header sits flush when collapsed. */
summary.howto-title {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 14px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--panel-border);
  border-radius: 12px;
}

summary.howto-title:hover {
  background: rgba(255, 255, 255, 0.1);
}

details.howto-group[open] summary.howto-title {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

summary.howto-title::-webkit-details-marker { display: none; }

summary.howto-title::after {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
}

details.howto-group[open] summary.howto-title::after {
  transform: rotate(45deg);
}

.howto-copy {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--muted);
}

.howto-list {
  margin: 2px 0 0;
  padding-left: 16px;
  font-size: 0.95rem;
  font-size: 0.8rem;
  line-height: 1.55;
  color: var(--muted);
}

.howto-list li + li {
  margin-top: 5px;
}

.howto-list b,
.howto-copy b {
  color: var(--ink);
  font-weight: 700;
}

.overlay button {
  -webkit-appearance: none;
  appearance: none;
  border: none;
  margin-top: 4px;
  min-height: 46px;
  padding: 0 28px;
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 700;
  color: #0a1420;
  background: var(--accent);
  border-radius: 22px;
}

.overlay button:active {
  transform: translateY(1px) scale(0.98);
}

.overlay button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* Start is the one action on the menu, so it takes the orange accent rather
   than the blue every other overlay button shares. */
#startBtn {
  min-height: 58px;
  min-width: 260px;
  padding: 0 44px;
  font-size: 1.15rem;
  background: var(--accent-orange);
}

#startBtn:focus-visible {
  outline-color: var(--accent-orange);
}

/* Threat alarm on the strength readout. It glows in whatever colour the bar
   is already showing: red for an overwhelming wave, yellow for the tie/ahead
   states, which only raise it on the last life. Kept to the readout rather
   than washing the whole window. */
#strengthMeter.threat {
  border-color: rgba(255, 213, 74, 0.75);
  animation: threatMeterPulseWarn 2.2s ease-in-out infinite;
}

#strengthMeter.threat.danger {
  border-color: rgba(255, 60, 60, 0.75);
  animation: threatMeterPulse 2.2s ease-in-out infinite;
}

@keyframes threatMeterPulseWarn {
  0%, 100% { box-shadow: 0 0 8px 1px rgba(255, 213, 74, 0.3); }
  50%      { box-shadow: 0 0 18px 4px rgba(255, 213, 74, 0.6); }
}

@keyframes threatMeterPulse {
  0%, 100% { box-shadow: 0 0 8px 1px rgba(255, 60, 60, 0.35); }
  50%      { box-shadow: 0 0 18px 4px rgba(255, 60, 60, 0.7); }
}

/* Update-available toast: sits above the HUD, self-contained, never
   touches gameplay layout. */
#updateToast {
  position: fixed;
  left: 50%;
  bottom: max(16px, env(safe-area-inset-bottom));
  transform: translateX(-50%) translateY(0);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border-radius: 12px;
  border: 1px solid var(--accent);
  background: var(--panel);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-weight: 600;
  box-shadow: 0 0 14px rgba(76, 158, 255, 0.35);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  max-width: calc(100vw - 28px);
  text-align: center;
  animation: updateToastIn 0.25s ease-out;
}

#updateToast[hidden] {
  display: none;
}

#updateToast .update-toast-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 6px var(--accent);
  flex: none;
}

@keyframes updateToastIn {
  from { opacity: 0; transform: translateX(-50%) translateY(8px); }
  to { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* Phone mode: touch input on a small screen. One responsive codebase -
   desktop keeps the fuller/denser UI (this block is simply never
   matched there); phone gets bigger tap targets/text and the secondary,
   denser readouts (strength-meter detail, expense breakdown, per-tower
   stat line) dropped so the HUD stays readable at a glance. Gated on
   pointer type AND viewport width together so a touch laptop or a
   tablet in landscape doesn't get the stripped-down phone treatment. */
@media (pointer: coarse) and (max-width: 700px) {
  #queuedExpenses,
  #strengthMeter,
  #keybindLegend,
  .tower-stats-line {
    display: none !important;
  }

  .chip-value {
    font-size: 1.7rem;
  }

  .chip-label {
    font-size: 0.68rem;
  }

  .chip {
    padding: 8px 16px;
  }

  #pauseBtn, #speedBtn {
    height: 44px;
    font-size: 1.15rem;
  }

  .build-btn {
    padding: 14px 28px;
    font-size: 1rem;
  }

  .blueprint-btn {
    font-size: 1.4rem;
  }

  .overlay-card .build-option {
    padding: 14px 16px;
  }

  .build-option-name {
    font-size: 1.05rem;
  }

  .overlay-card .target-mode-btn {
    padding: 12px 10px;
    font-size: 0.9rem;
  }
}
