* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: #0a0a12;
  color: #e8e4d8;
  font-family: 'Courier New', monospace;
  overflow: hidden;
}

#wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  position: relative;
}

/* The canvas is scaled by CSS, never by object-fit: input.js maps pointer
   positions through getBoundingClientRect, which only agrees with what's
   painted while the element's box IS the picture. Letterboxing inside the
   element (object-fit: contain) would silently offset every shot by the size
   of the bars.
   So it's sized to the larger of the two fits and left to keep its own
   aspect. --game-aspect is published by main.js from the real canvas
   dimensions; the fallback here is only for the instant before it runs. */
:root {
  --game-aspect: 1.585;
}

/* Ad rails and the mobile banner.
   CURRENTLY UNUSED — the playfield deliberately carries no ad code at all (see
   the note in home.html): it is the only way to guarantee nothing is ever
   served next to a jump button. These rules are kept because the desktop rails
   are a genuinely compliant placement if game-page revenue is wanted later
   (measured: 170px of clearance, against Google's recommended 150px from the
   edge of a game), so turning them back on is a markup-only change.

   Ad rails, desktop only.
   Fixed to the viewport edges rather than laid out in a row with the game,
   because #wrap is the positioning context for every HUD panel on screen —
   putting it inside a flex row would re-anchor the entire interface. The
   playfield is kept off them by --rail below, which the canvas subtracts from
   its own width. */
:root { --rail: 0px; }

.adRail {
  display: none;
  position: fixed;
  top: 0;
  bottom: 0;
  width: 160px;
  align-items: center;
  justify-content: center;
  padding: 8px 4px;
  background: #07070d;
  border-inline: 1px solid rgba(150, 170, 210, 0.1);
  overflow: hidden;
  z-index: 1;
}

.adRail-left { left: 0; }
.adRail-right { right: 0; }

/* Only where there is genuinely room for two 160px columns AND a playfield
   wide enough to still be worth looking at. Below this the rails would be
   taking a third of a small window away from the game, which is the wrong
   trade in both directions. Touch devices never get them: the whole layout
   there is built around thumbs at the screen edges, which is exactly where
   these would sit. */
@media (min-width: 1180px) and (pointer: fine) {
  :root { --rail: 170px; }
  .adRail { display: flex; }
}

body.touch-mode .adRail { display: none; }
body.touch-mode { --rail: 0px; }

/* Mobile bottom banner.
   It RESERVES a strip rather than floating over the game: #wrap is shortened
   by --ad-bottom, which pulls the playfield, the HUD and — critically — the
   touch controls up above it. AdSense's placement policy explicitly names game
   windows and buttons as things ads must not sit next to (accidental clicks),
   and this game's movement stick and jump button live along the bottom edge,
   so an overlay banner there would be about the worst placement available. */
:root { --ad-bottom: 0px; }

.adBanner {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--ad-bottom);
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: #07070d;
  border-top: 1px solid rgba(150, 170, 210, 0.14);
  z-index: 9;
}

/* Ads have to be distinguishable from content — labelling the strip is both
   the policy-safe thing and what stops a banner reading as part of the HUD. */
.adBannerLabel {
  font-size: 9px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: #6b7180;
}

.adBannerClose {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  font-family: 'Courier New', monospace;
  font-size: 19px;
  line-height: 1;
  background: rgba(30, 30, 42, 0.9);
  border: 1px solid #4a4a5e;
  border-radius: 5px;
  color: #e8e4d8;
  cursor: pointer;
}

.adBannerClose:hover { border-color: #e8574a; color: #e8574a; }

/* Shown wherever the side rails are not — i.e. phones and narrow windows,
   which is exactly where a horizontal strip costs least. */
@media (max-width: 1179px), (pointer: coarse) {
  :root { --ad-bottom: 66px; }
  .adBanner { display: flex; }
}

/* Dismissed: the strip goes and the playfield takes the space back. */
body.ads-dismissed { --ad-bottom: 0px; }
body.ads-dismissed .adBanner { display: none; }

/* The playfield ends where the banner starts, so nothing is ever underneath
   it. Everything in the HUD is absolutely positioned against #wrap, so this
   one rule moves the whole interface up with it. */
#wrap { height: calc(100% - var(--ad-bottom)); }

canvas#game {
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  background: #0a0a12;
  width: min(calc(100vw - var(--rail) * 2), calc((100vh - var(--ad-bottom)) * var(--game-aspect)));
  height: auto;
  cursor: crosshair;
  touch-action: none; /* stop the browser from scrolling/zooming on drag — the canvas handles touch itself */
}

/* Sits over the canvas and under every panel — it comes before them in the
   document and nothing here raises a z-index, so DOM order does the work. */
#vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 78% 70% at 50% 46%, rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, 0.42) 100%),
    linear-gradient(to bottom, rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0) 18%);
}

#hud {
  position: absolute;
  top: 12px;
  left: 12px;
  font-size: 14px;
  padding: 9px 12px;
  background: linear-gradient(180deg, rgba(18, 20, 32, 0.78), rgba(10, 10, 18, 0.72));
  border: 1px solid rgba(150, 170, 210, 0.22);
  border-radius: 7px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(3px);
  pointer-events: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Segmented rather than one continuous bar: ten notches is a quantity you can
   read at a glance without measuring a length against a track, which is all
   you get time for mid-fight. The notches are an overlay on the track, so the
   fill underneath stays a plain width the JS can set. */
#health {
  position: relative;
  width: 156px;
  height: 14px;
  background: #241521;
  border: 1px solid rgba(150, 170, 210, 0.28);
  border-radius: 3px;
  overflow: hidden;
}

#health::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.55) 0 1px, rgba(0, 0, 0, 0) 1px 15.6px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0) 45%, rgba(0, 0, 0, 0.25));
}

#healthFill {
  height: 100%;
  width: 100%;
  background: linear-gradient(180deg, #7ee08a, #3fa257);
  box-shadow: 0 0 10px rgba(110, 226, 140, 0.45);
  transition: width 0.15s ease, background 0.3s linear, box-shadow 0.3s linear;
}

/* Colour is set by class rather than by the width itself — CSS can't key a
   gradient off an element's own size, and "how much is left" is exactly the
   thing the bar has to say from across the screen. See updateHud. */
#healthFill.hurt {
  background: linear-gradient(180deg, #f4d35e, #d19b24);
  box-shadow: 0 0 10px rgba(244, 211, 94, 0.45);
}

#healthFill.critical {
  background: linear-gradient(180deg, #e8574a, #a92b20);
  box-shadow: 0 0 12px rgba(232, 87, 74, 0.6);
  animation: healthPulse 0.9s ease-in-out infinite;
}

@keyframes healthPulse {
  50% { opacity: 0.62; }
}

/* What you're holding, centred along the bottom edge near the crosshair. */
#loadout {
  position: absolute;
  bottom: 12px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 2px;
  padding: 5px;
  font-size: 13px;
  background: linear-gradient(180deg, rgba(18, 20, 32, 0.76), rgba(10, 10, 18, 0.7));
  border: 1px solid rgba(150, 170, 210, 0.22);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(3px);
  pointer-events: none;
  white-space: nowrap;
}

.loadoutItem {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 3px 14px;
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.03);
}

.loadoutItem + .loadoutItem {
  margin-left: 2px;
}

.loadoutKey {
  font-size: 10px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: #8d93a8;
}

#materialName {
  color: #f4d35e;
  font-weight: bold;
}

/* Build mode takes the material slot over (see updateHud). Green rather than
   the material amber, because the ghost on the playfield is green when it will
   go in — the HUD and the cursor should agree at a glance about which tool is
   in your hands. */
#materialName.building {
  color: #8fe06a;
}

#weaponName {
  color: #9adfff;
  font-weight: bold;
}

#ammoCount {
  color: #e8e4d8;
  font-weight: bold;
  font-variant-numeric: tabular-nums; /* the count shouldn't jitter as rounds tick down */
}

#ammoCount.reloading {
  color: #f4d35e;
}

#gold {
  color: #f4d35e;
}

#goldCount, #goldTarget { font-weight: bold; }

/* Level and XP. The bar is deliberately thin and unlabelled — it is a sense
   of "soon" rather than a number to read, and the number that matters (the
   level) is written beside it. */
#xpRow {
  color: #c9a7ff;
  display: flex;
  align-items: center;
  gap: 7px;
}

#levelNum { font-weight: bold; }

#xpTrack {
  flex: 1;
  height: 5px;
  min-width: 60px;
  background: #241b33;
  border: 1px solid rgba(150, 170, 210, 0.22);
  border-radius: 2px;
  overflow: hidden;
}

#xpFill {
  height: 100%;
  width: 0%;
  background: linear-gradient(180deg, #c9a7ff, #7b53c4);
  transition: width 0.18s ease;
}

/* The level-up hand.
   Pinned down the LEFT edge and vertically centred, which is the one part of
   the playfield the game never asks you to look at: the HUD is in the top
   corner, the loadout along the bottom, the leaderboard top right, and you
   aim into the middle and the right because the camera leans toward the
   cursor. It also deliberately does not dim or cover anything — the fight is
   still running while you read it, and that is the cost of taking your time. */
#levelUp {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: min(46vw, 300px);
  padding: 9px 11px 10px;
  font-size: 12px;
  background: linear-gradient(180deg, rgba(28, 20, 44, 0.9), rgba(12, 10, 20, 0.86));
  border: 1px solid rgba(201, 167, 255, 0.5);
  border-radius: 7px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(3px);
  pointer-events: none; /* only the rows themselves are clickable */
  animation: levelUpIn 0.28s ease both;
}

@keyframes levelUpIn {
  from { opacity: 0; transform: translate(-8px, -50%); }
}

#levelUpTitle {
  color: #c9a7ff;
  font-weight: bold;
  letter-spacing: 1px;
  text-transform: uppercase;
  margin-bottom: 7px;
}

#levelUpCards {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.levelCard {
  pointer-events: auto;
  display: block;
  width: 100%;
  text-align: left;
  font-family: 'Courier New', monospace;
  font-size: 12px;
  padding: 6px 8px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid #3a3350;
  border-radius: 5px;
  color: #e8e4d8;
  cursor: pointer;
}

.levelCard:hover {
  border-color: #c9a7ff;
  background: rgba(201, 167, 255, 0.14);
}

.levelCardKey {
  color: #c9a7ff;
  font-weight: bold;
  margin-right: 6px;
}

.levelCardName {
  color: #f4d35e;
  font-weight: bold;
}

.levelCardText {
  display: block;
  margin-top: 2px;
  opacity: 0.75;
}

/* On a phone the left edge is where the movement stick lives, so the hand
   goes to the top instead and shrinks — there is no cursor to keep clear of
   up there, and the loadout has already moved to make room. */
body.touch-mode #levelUp {
  left: 50%;
  top: auto;
  bottom: calc(96px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  width: min(90vw, 340px);
  animation: none;
}

#rewardNotice {
  position: absolute; top: 22%; left: 50%; transform: translateX(-50%);
  color: #f4d35e; font-size: 24px; font-weight: bold; pointer-events: none;
  text-shadow: 0 2px 8px #000;
}

#kills {
  color: #c0392b;
}

#lives {
  color: #8fe06a;
}

/* Pips, not a number — see updateHud. Letter-spaced so three of them read as
   three distinct chances at a glance rather than as a blob. */
#livesCount {
  font-weight: bold;
  letter-spacing: 3px;
}

/* Time of day. Warm through the day, cold once the sun is going down, so the
   row itself tells you which half of the cycle you're in without reading it. */
#timeOfDay {
  color: #f4d35e;
  transition: color 0.6s linear;
}

#timeOfDay.night {
  color: #8fb8e0;
}

#flashlightTag {
  opacity: 0.75;
  font-style: italic;
}

#fireproof {
  color: #e07a35;
  font-weight: bold;
}

/* Boss health, across the top of the screen and only while one is alive —
   the one gauge that isn't about you. Deliberately away from the HUD column
   in the corner: it belongs to the thing on screen, not to your loadout. */
#bossBar {
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  width: min(46%, 420px);
  pointer-events: none;
  text-align: center;
}

#bossName {
  color: #ff8a6a;
  font-weight: bold;
  letter-spacing: 2px;
  text-transform: uppercase;
  font-size: 14px;
  text-shadow: 0 2px 6px #000;
  margin-bottom: 4px;
}

#bossTrack {
  height: 10px;
  border: 1px solid #6b2f22;
  background: rgba(10, 10, 16, 0.72);
}

#bossFill {
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, #ff5c33, #ffb03a);
  transition: width 0.12s linear;
}

#wave {
  color: #e8574a;
  font-weight: bold;
  letter-spacing: 0.5px;
}

/* Between waves the row turns green — the clearest possible signal that the
   pressure is off and the countdown on it is time you can spend. */
#wave.calm {
  color: #8fe06a;
}

#killCount {
  font-weight: bold;
}

/* Diagnostics, not game state — dimmed and set smaller so it sits below the
   things you actually play off, while staying readable enough to catch a
   stutter the moment it happens. */
#perf {
  font-size: 11px;
  opacity: 0.55;
  letter-spacing: 0.5px;
}

#fps, #zoomLevel {
  font-weight: bold;
  font-variant-numeric: tabular-nums; /* the number mustn't jitter as it changes */
}

/* Only ever set when the frame rate is genuinely off target, so it reads as
   an alarm rather than as decoration. */
#fps.low { color: #f4d35e; }
#fps.bad { color: #e8574a; }

/* Pre-round countdown: big, centered, and unmissable, then it gets out of
   the way. Non-interactive so it can never eat an aim/shoot click. */
#countdown {
  position: absolute;
  top: 34%;
  left: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  font-size: 76px;
  font-weight: bold;
  letter-spacing: 4px;
  color: #f4d35e;
  text-shadow: 0 0 18px rgba(0, 0, 0, 0.9), 0 4px 0 rgba(0, 0, 0, 0.55);
}

#countdown.go {
  color: #8fe06a;
  font-size: 60px;
}

/* Downed (multiplayer). Deliberately NOT a full-screen cover like #gameOver:
   the fight carries on without you and watching it is the whole point of the
   20s — and of wanting it to end. Tinted red at the edges so being out of
   play is unmistakable, but the middle of the map stays readable. */
#downed {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(40, 0, 0, 0) 35%, rgba(60, 0, 0, 0.55) 100%);
}

#downedTitle {
  font-size: 40px;
  font-weight: bold;
  letter-spacing: 5px;
  color: #ff6b6b;
  text-shadow: 0 0 18px rgba(0, 0, 0, 0.9), 0 4px 0 rgba(0, 0, 0, 0.55);
}

#downedSub {
  font-size: 20px;
  letter-spacing: 2px;
  color: #f4d35e;
  text-shadow: 0 0 12px rgba(0, 0, 0, 0.9);
}

/* Game over. Covers the playfield rather than sitting beside it: the run is
   over, and leaving the frozen battlefield readable behind a small notice
   would invite you to keep looking at a world that has stopped responding.
   Deliberately BELOW the help and settings panels (z-index 5 and 6), because
   changing the life count is the one setting you're most likely to want the
   moment a run ends — it has to open on top of this, not under it. */
#gameOver {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  background: rgba(10, 10, 18, 0.86);
}

#gameOverTitle {
  font-size: 46px;
  font-weight: bold;
  letter-spacing: 10px;
  text-indent: 10px; /* balances the trailing letter-space so it reads centered */
  color: #e8574a;
  text-shadow: 0 0 24px rgba(0, 0, 0, 0.9), 0 4px 0 rgba(0, 0, 0, 0.5);
}

/* The one ending that isn't a death (see corebreach.js). Green rather than
   red, because the same panel has to be able to say two opposite things. */
#gameOverTitle.won {
  color: #8fe06a;
  letter-spacing: 12px;
  text-indent: 12px;
}

#gameOverStats {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 230px;
  font-size: 14px;
}

.gameOverStat {
  display: flex;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 4px;
  border-bottom: 1px solid #2c2c3a;
}

.gameOverValue {
  color: #f4d35e;
  font-weight: bold;
  font-variant-numeric: tabular-nums;
}

#gameOverRestart {
  font-family: 'Courier New', monospace;
  font-size: 14px;
  padding: 9px 22px;
  margin-top: 6px;
  background: rgba(30, 30, 42, 0.85);
  border: 1px solid #8fe06a;
  border-radius: 4px;
  color: #8fe06a;
  font-weight: bold;
  cursor: pointer;
}

#gameOverRestart:hover {
  background: rgba(143, 224, 106, 0.2);
}

.hidden {
  display: none !important;
}

/* A way back out of the game. Small, in the corner with the other two
   buttons, and gone on touch where the bottom edge belongs to thumbs. A page
   with no navigation off it is bad manners and is one of the things an AdSense
   review remarks on. */
#siteLink {
  position: absolute;
  bottom: 12px;
  right: 244px;
  font-family: 'Courier New', monospace;
  font-size: 12px;
  padding: 6px 14px;
  background: rgba(10, 10, 18, 0.8);
  border: 1px solid #3a3a4a;
  border-radius: 4px;
  color: #8d93a8;
  text-decoration: none;
}

#siteLink:hover { border-color: #f4d35e; color: #f4d35e; }

body.touch-mode #siteLink { display: none; }

/* Both buttons moved out of the bottom centre when the loadout strip took
   that spot — the corner is where you go looking for a panel you open
   between waves anyway. */
#helpToggle {
  position: absolute;
  bottom: 12px;
  right: 12px;
  font-family: 'Courier New', monospace;
  font-size: 12px;
  padding: 6px 14px;
  background: rgba(10, 10, 18, 0.8);
  border: 1px solid #3a3a4a;
  border-radius: 4px;
  color: #e8e4d8;
  cursor: pointer;
}

#helpToggle:hover {
  border-color: #9adfff;
  color: #9adfff;
}

/* Sits beside the controls button along the bottom edge — same visual
   treatment, since they're the same kind of thing: a panel you open, read,
   and get back out of. */
#settingsToggle {
  position: absolute;
  bottom: 12px;
  right: 128px;
  font-family: 'Courier New', monospace;
  font-size: 12px;
  padding: 6px 14px;
  background: rgba(10, 10, 18, 0.8);
  border: 1px solid #3a3a4a;
  border-radius: 4px;
  color: #e8e4d8;
  cursor: pointer;
}

#settingsToggle:hover {
  border-color: #f4d35e;
  color: #f4d35e;
}

/* Both toggles are placed with the rest of the touch layout, further down —
   the rule that used to live here put them over the HUD panel. */

/* The bottom corners belong to the stick and the jump button, so the loadout
   goes up top with the tool strip under it. */
body.touch-mode #loadout {
  bottom: auto;
  top: calc(10px + env(safe-area-inset-top, 0px));
  font-size: 11px;
}

#settings {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 6;
  width: min(94vw, 520px);
  max-height: 88vh;
  overflow-y: auto;
  font-size: 12px;
  padding: 14px 18px 12px;
  background: rgba(10, 10, 18, 0.97);
  border: 1px solid #4a4a5e;
  border-radius: 6px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6);
}

#settingsHeader {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 6px;
}

#settingsTitle {
  color: #f4d35e;
  font-weight: bold;
  font-size: 15px;
  letter-spacing: 1px;
}

#settingsClose {
  font-family: 'Courier New', monospace;
  font-size: 20px;
  line-height: 1;
  padding: 0 6px;
  background: none;
  border: none;
  color: #e8e4d8;
  cursor: pointer;
}

#settingsClose:hover {
  color: #e8574a;
}

.settingRow {
  padding: 8px 0;
  border-top: 1px solid #2c2c3a;
}

.settingHead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.settingLabel {
  color: #9adfff;
  letter-spacing: 1px;
  text-transform: uppercase;
}

.settingValue {
  color: #f4d35e;
  font-weight: bold;
  font-variant-numeric: tabular-nums;
}

.settingRange {
  width: 100%;
  margin: 6px 0 2px;
  accent-color: #9adfff;
  cursor: pointer;
}

.settingNote {
  margin: 2px 0 0;
  opacity: 0.6;
  font-style: italic;
}

/* On/off settings: the label is part of the hit target, so the whole row
   reads and behaves as one control rather than a box with text beside it. */
.settingToggle {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

.settingToggle input {
  accent-color: #9adfff;
  cursor: pointer;
}

.weaponGrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 2px 12px;
  margin: 6px 0 2px;
}

.weaponPick {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
}

.weaponPick input {
  accent-color: #9adfff;
  cursor: pointer;
}

#settingsFooter {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid #2c2c3a;
}

#settingsFooter button {
  font-family: 'Courier New', monospace;
  font-size: 12px;
  padding: 6px 16px;
  background: rgba(30, 30, 42, 0.8);
  border: 1px solid #4a4a5e;
  border-radius: 4px;
  color: #e8e4d8;
  cursor: pointer;
}

#settingsFooter button:hover {
  border-color: #9adfff;
  color: #9adfff;
}

#settingsApply {
  border-color: #8fe06a !important;
  color: #8fe06a !important;
  font-weight: bold;
}

#settingsApply:hover {
  background: rgba(143, 224, 106, 0.18) !important;
}

#help {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 5;
  max-width: min(94vw, 900px);
  max-height: 88vh;
  overflow-y: auto;
  font-size: 12px;
  padding: 14px 18px 12px;
  background: rgba(10, 10, 18, 0.96);
  border: 1px solid #4a4a5e;
  border-radius: 6px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6);
}

#helpHeader {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 10px;
}

#helpTitle {
  color: #f4d35e;
  font-weight: bold;
  font-size: 15px;
  letter-spacing: 1px;
}

#helpClose {
  font-family: 'Courier New', monospace;
  font-size: 20px;
  line-height: 1;
  padding: 0 6px;
  background: none;
  border: none;
  color: #e8e4d8;
  cursor: pointer;
}

#helpClose:hover {
  color: #e8574a;
}

.helpGrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 6px 22px;
}

.helpGrid h3 {
  margin: 6px 0 4px;
  font-size: 12px;
  color: #9adfff;
  letter-spacing: 1px;
  text-transform: uppercase;
}

/* A sub-heading inside a section, for a tool that needs its own key list —
   the grappling hook's, under Movement. Quieter than the section headings
   above it so the section still reads as one block. */
.helpGrid h4 {
  margin: 8px 0 3px;
  font-size: 11px;
  color: #cfe9ff;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  opacity: 0.85;
}

.helpGrid dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 2px 10px;
  margin: 0;
}

.helpGrid dt {
  color: #f4d35e;
  font-weight: bold;
  white-space: nowrap;
}

.helpGrid dd {
  margin: 0;
  opacity: 0.85;
}

.helpNote {
  margin: 6px 0 0;
  opacity: 0.6;
  font-style: italic;
}

/* The world's kind, under the wave counter. Quieter than the counters above
   it: it is a fact about the map that doesn't change during a run, not a
   number you watch. */
#worldRow {
  opacity: 0.62;
  letter-spacing: 0.3px;
}

#worldName {
  color: #9adfff;
}

#helpFooter {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid #2c2c3a;
  opacity: 0.7;
}

#room {
  position: absolute;
  top: 12px;
  right: 12px;
  font-size: 13px;
  padding: 6px 10px;
  background: rgba(10, 10, 18, 0.7);
  border: 1px solid #3a3a4a;
  border-radius: 4px;
  pointer-events: none;
}

#roomCode {
  color: #9adfff;
  font-weight: bold;
  letter-spacing: 1px;
}

/* First-load guide, top right. Sits where the leaderboard goes and hides it
   while it's up (see body.guide-open below) — at the start of a run the board
   is empty anyway, and two stacked panels in one corner is worse than either.
   Non-interactive apart from its close button, so it can never eat a shot. */
#guide {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 5;
  width: 268px;
  font-size: 12px;
  padding: 10px 12px 9px;
  background: linear-gradient(180deg, rgba(18, 20, 32, 0.93), rgba(10, 10, 18, 0.9));
  border: 1px solid rgba(150, 170, 210, 0.3);
  border-radius: 7px;
  box-shadow: 0 8px 26px rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(3px);
  pointer-events: none;
  animation: guideIn 0.35s ease both;
}

@keyframes guideIn {
  from { opacity: 0; transform: translateY(-6px); }
}

/* Fades rather than vanishing, so dismissing it doesn't read as a glitch. */
#guide.dismissed {
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 0.35s ease, transform 0.35s ease;
}

#guideHead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 7px;
}

#guideTitle {
  color: #f4d35e;
  font-weight: bold;
  letter-spacing: 1px;
  text-transform: uppercase;
}

#guideClose {
  pointer-events: auto; /* the one thing in here you can click */
  font-family: 'Courier New', monospace;
  font-size: 17px;
  line-height: 1;
  padding: 0 5px;
  background: none;
  border: none;
  color: #8d93a8;
  cursor: pointer;
}

#guideClose:hover { color: #e8574a; }

#guideList {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 4px 10px;
  margin: 0;
}

#guideList dt {
  color: #9adfff;
  font-weight: bold;
  white-space: nowrap;
}

#guideList dd {
  margin: 0;
  opacity: 0.88;
}

#guideList b { color: #f4d35e; }

#guideFoot {
  margin-top: 8px;
  padding-top: 7px;
  border-top: 1px solid #2c2c3a;
  opacity: 0.6;
}

body.guide-open #leaderboard { display: none; }

#leaderboard {
  position: absolute;
  top: 56px;
  right: 12px;
  width: 150px;
  font-size: 12px;
  padding: 8px 10px;
  background: rgba(10, 10, 18, 0.7);
  border: 1px solid #3a3a4a;
  border-radius: 4px;
  pointer-events: none;
}

#leaderboardTitle {
  color: #f4d35e;
  font-weight: bold;
  margin-bottom: 4px;
  letter-spacing: 0.5px;
}

#leaderboardList {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

#leaderboardList li {
  display: flex;
  gap: 6px;
  white-space: nowrap;
}

#leaderboardList li.empty {
  opacity: 0.6;
}

#leaderboardList .rank {
  color: #9adfff;
  width: 1.2em;
}

#leaderboardList .name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
}

#leaderboardList .score {
  color: #c0392b;
  font-weight: bold;
}

/* Touch controls: hidden by default, revealed by Input.setupTouchUI() (see
   input.js) adding .touch-mode to <body> — only on devices that look
   touch-only (coarse pointer, no hover), so desktop/mouse play is unaffected. */
#touchControls {
  display: none;
}

/* The panel stays available on touch — it's the only place the full weapon
   and material lists are written down, and the touch controls just cycle
   through both without saying what they are.
   Both buttons sit along the bottom EDGE CENTRE: the stick owns the bottom
   left corner and jump owns the bottom right, and the span between them is the
   one part of the screen neither thumb sweeps. They used to be moved to the
   top left, directly on top of the health bar and the wave counter — the HUD
   panel is anchored there too, so on a phone the Controls button covered the
   one readout you most need mid-fight. */
body.touch-mode #helpToggle {
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  top: auto;
  right: auto;
  left: 50%;
  transform: translateX(6px);
}

body.touch-mode #settingsToggle {
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  top: auto;
  left: auto;
  right: 50%;
  transform: translateX(-6px);
}

body.touch-mode #touchControls {
  display: block;
  position: absolute;
  inset: 0;
  pointer-events: none; /* re-enabled per-element below, so empty space never blocks aim/shoot touches */
}

/* Insets, so nothing lands under a notch, a home indicator or a rounded
   corner. env() resolves to 0 where there is no cutout, so the same numbers
   are correct on a plain rectangular screen. */
body.touch-mode #touchControls {
  --pad-x: calc(14px + env(safe-area-inset-left, 0px));
  --pad-r: calc(14px + env(safe-area-inset-right, 0px));
  --pad-b: calc(14px + env(safe-area-inset-bottom, 0px));
  --pad-t: calc(10px + env(safe-area-inset-top, 0px));
}

/* --- Left thumb: the movement stick ---------------------------------------
   A fixed base rather than one that appears wherever you happen to touch: the
   thumb finds a landmark it can feel for without looking, and the whole point
   of a stick here is that you never take your eyes off the fight to use it. */
#touchStick {
  position: absolute;
  left: var(--pad-x);
  bottom: var(--pad-b);
  width: 132px;
  height: 132px;
  border-radius: 50%;
  pointer-events: auto;
  touch-action: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  background: radial-gradient(circle at 50% 50%, rgba(30, 30, 42, 0.5), rgba(10, 10, 18, 0.34));
  border: 1px solid rgba(150, 170, 210, 0.28);
}

#touchStickRing {
  position: absolute;
  inset: 30%;
  border-radius: 50%;
  border: 1px dashed rgba(150, 170, 210, 0.22);
  pointer-events: none;
}

#touchStickKnob {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 56px;
  height: 56px;
  margin: -28px 0 0 -28px;
  border-radius: 50%;
  background: rgba(154, 223, 255, 0.26);
  border: 1px solid #9adfff;
  pointer-events: none;
  /* No transition: the knob has to sit exactly under the thumb, and easing
     toward the finger reads as lag in the controls themselves. It is snapped
     home on release instead, which is the one moment a little travel helps. */
}

#touchStick.is-idle #touchStickKnob {
  transition: transform 0.12s ease-out;
  background: rgba(154, 223, 255, 0.14);
}

/* --- Right thumb ---------------------------------------------------------- */
#touchRightPad {
  position: absolute;
  right: var(--pad-r);
  bottom: var(--pad-b);
  display: flex;
  align-items: flex-end;
  gap: 12px;
  pointer-events: none;
}

.touchBtn {
  pointer-events: auto;
  touch-action: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  width: 62px;
  height: 62px;
  border-radius: 50%;
  border: 1px solid #4a4a5e;
  background: rgba(30, 30, 42, 0.62);
  color: #e8e4d8;
  font-family: 'Courier New', monospace;
  font-size: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.touchBtn:active,
.touchBtn.is-held {
  background: rgba(154, 223, 255, 0.35);
  border-color: #9adfff;
}

/* Jump is the one you reach for most and the one you most regret missing, so
   it gets the largest target and the corner the thumb rests nearest. */
.touchJumpBtn {
  width: 84px;
  height: 84px;
  font-size: 30px;
  border-color: #8fe06a;
  background: rgba(143, 224, 106, 0.16);
}

.touchJumpBtn:active,
.touchJumpBtn.is-held {
  background: rgba(143, 224, 106, 0.4);
  border-color: #8fe06a;
}

.touchSideBtn {
  width: 58px;
  height: 58px;
  font-size: 20px;
}

/* --- Mode selector -------------------------------------------------------- */
#touchModes {
  position: absolute;
  right: var(--pad-r);
  bottom: calc(var(--pad-b) + 96px);
  display: flex;
  gap: 4px;
  padding: 4px;
  border-radius: 9px;
  background: rgba(10, 10, 18, 0.72);
  border: 1px solid rgba(150, 170, 210, 0.22);
  pointer-events: auto;
}

.touchMode {
  pointer-events: auto;
  touch-action: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  min-width: 58px;
  height: 40px;
  padding: 0 8px;
  border-radius: 6px;
  border: 1px solid transparent;
  background: transparent;
  color: #8d93a8;
  font-family: 'Courier New', monospace;
  font-size: 13px;
  font-weight: bold;
}

.touchMode.is-on {
  background: rgba(154, 223, 255, 0.2);
  border-color: #9adfff;
  color: #9adfff;
}

/* --- Tool strip ----------------------------------------------------------- */
#touchTools {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: calc(var(--pad-t) + 74px);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 5px;
  max-width: 88vw;
  pointer-events: none;
}

.touchTool {
  pointer-events: auto;
  touch-action: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  min-width: 54px;
  height: 38px;
  padding: 0 9px;
  border-radius: 7px;
  border: 1px solid #3a3a4a;
  background: rgba(10, 10, 18, 0.74);
  color: #e8e4d8;
  font-family: 'Courier New', monospace;
  font-size: 12px;
}

.touchTool:active {
  background: rgba(154, 223, 255, 0.3);
  border-color: #9adfff;
}

/* The brush row and the block row are the same strip wearing two hats — see
   the touch-building class, set by setupTouchUI when the mode changes. */
#touchControls .touchOnlyBuild { display: none; }
#touchControls.touch-building .touchNotBuild { display: none; }
#touchControls.touch-building .touchOnlyBuild { display: inline-flex; align-items: center; justify-content: center; }

/* Same trick for the detonator, which only exists while you are holding the
   charge it sets off. */
#touchControls .touchOnlySticky { display: none; }
#touchControls.touch-sticky .touchOnlySticky {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-color: rgba(224, 92, 138, 0.7);
  color: #ffb9cd;
}

#touchHint {
  position: absolute;
  top: calc(var(--pad-t) + 122px);
  left: 50%;
  transform: translateX(-50%);
  font-size: 11px;
  padding: 4px 10px;
  background: rgba(10, 10, 18, 0.6);
  border: 1px solid #3a3a4a;
  border-radius: 4px;
  opacity: 0.75;
  pointer-events: none;
  white-space: nowrap;
}

/* A short screen has no room for the tool strip AND the hint under the
   loadout; the hint is the part you only need once. */
@media (max-height: 430px) {
  #touchHint { display: none; }
  #touchTools { top: calc(var(--pad-t) + 66px); }
}

/* This game's world is wide-format; on a portrait phone the canvas shrinks
   to a thin strip with a lot of empty space above/below, leaving the touch
   buttons (anchored to the viewport, not the canvas) far from the action. */
#rotateHint {
  display: none;
}

@media (orientation: portrait) {
  body.touch-mode #rotateHint {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 10;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 0 15vw;
    background: #0a0a12;
    color: #e8e4d8;
    font-size: 16px;
  }
}
