* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
}

html, body { height: 100%; }

body {
  overflow: hidden;
  background: #1a1a2e;
  font-family: 'Segoe UI', Arial, sans-serif;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}

#game-canvas {
  display: block;
  width: 100vw;
  height: 100vh;
  height: 100dvh;      /* dodges the mobile URL bar resize dance */
  touch-action: none;  /* no double-tap zoom or pull-to-refresh over the game */
}

/* ── HUD ── */
#hud {
  position: fixed;
  top: 0; left: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  z-index: 10;
}

/* Top bar */
#hud-top {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 16px;
  padding: calc(12px + var(--safe-t)) calc(20px + var(--safe-r)) 12px calc(20px + var(--safe-l));
  background: rgba(0,0,0,0.35);
  backdrop-filter: blur(4px);
}
#health-text { color: #fff; font-size: 13px; }

/* Health bar */
#health-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
}
#health-icon { font-size: 20px; }
#health-bar-bg {
  width: 150px; height: 18px;
  background: rgba(255,255,255,0.2);
  border-radius: 9px;
  overflow: hidden;
}
#health-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, #7ED321, #A8E040);
  border-radius: 9px;
  transition: width 0.3s ease;
  width: 100%;
}

/* Seeds */
#seeds-display {
  display: flex; align-items: center; gap: 6px;
  color: #FFC700; font-size: 16px; font-weight: bold;
}

/* Wave info */
#wave-display {
  margin-left: auto;
  color: #fff; font-size: 15px;
  background: rgba(255,60,60,0.5);
  padding: 4px 12px;
  border-radius: 12px;
}

/* Score */
#score-display {
  color: #FFC700; font-size: 16px; font-weight: bold;
}

/* Bottom bar – tower menu + controls hint */
#hud-bottom {
  position: absolute;
  bottom: 0; left: 0; right: 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  padding: 12px calc(20px + var(--safe-r)) calc(12px + var(--safe-b)) calc(20px + var(--safe-l));
  background: rgba(0,0,0,0.35);
  backdrop-filter: blur(4px);
  pointer-events: auto;
}

#controls-hint {
  color: rgba(255,255,255,0.7);
  font-size: 12px;
  line-height: 1.6;
}

#tower-menu {
  display: flex; gap: 10px; align-items: center;
}
.tower-btn {
  background: #FFC700;
  border: 2px solid #FFD700;
  border-radius: 8px;
  padding: 8px 14px;
  font-size: 13px;
  font-weight: bold;
  cursor: pointer;
  color: #333;
  transition: background 0.15s, transform 0.1s;
}
.tower-btn:hover { background: #FFD700; transform: translateY(-2px); }
.tower-btn.active { background: #7ED321; border-color: #5CB800; color: #fff; }

/* Purification progress bar */
#purify-bar-wrap {
  position: absolute;
  top: 52px; left: 50%;
  transform: translateX(-50%);
  width: 300px;
  text-align: center;
  pointer-events: none;
}
#purify-label { color: #fff; font-size: 12px; margin-bottom: 4px; text-shadow: 0 1px 3px #000; }
#purify-bar-bg {
  height: 10px;
  background: rgba(255,255,255,0.2);
  border-radius: 5px;
  overflow: hidden;
}
#purify-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, #FFC700, #7ED321);
  border-radius: 5px;
  width: 0%;
  transition: width 0.5s ease;
}

/* Wave notification banner */
#wave-banner {
  position: absolute;
  top: 25%; left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s;
}
#wave-banner h2 {
  font-size: 42px;
  color: #FFC700;
  text-shadow: 0 2px 8px rgba(0,0,0,0.8);
  font-weight: 900;
}
#wave-banner p {
  color: #fff;
  font-size: 18px;
  text-shadow: 0 1px 4px rgba(0,0,0,0.7);
}
#wave-banner.visible { opacity: 1; }

/* Floating damage popups — the float+fade is CSS so JS doesn't run a timer per number */
.damage-popup {
  position: fixed;
  color: #fff;
  font-size: 20px;
  font-weight: 900;
  text-shadow: 0 2px 4px rgba(0,0,0,0.9);
  pointer-events: none;
  white-space: nowrap;
  z-index: 50;
  transform: translate(-50%, -50%);
  animation: popfloat 0.7s ease-out forwards;
}
@keyframes popfloat {
  0%   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, calc(-50% - 44px)) scale(1.15); }
}

/* Boss health bar */
#boss-bar-wrap {
  position: absolute;
  top: 96px; left: 50%;
  transform: translateX(-50%);
  width: min(460px, 84vw);
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s;
}
#boss-bar-wrap.visible { opacity: 1; }
#boss-name {
  color: #FF6BD6; font-size: 15px; font-weight: 800;
  text-shadow: 0 2px 6px #000; margin-bottom: 4px;
}
#boss-bar-bg {
  height: 14px;
  background: rgba(0,0,0,0.55);
  border: 2px solid rgba(255,107,214,0.7);
  border-radius: 8px;
  overflow: hidden;
}
#boss-bar-fill {
  height: 100%; width: 100%;
  background: linear-gradient(90deg, #FF3B3B, #FF6BD6);
  transition: width .18s ease;
}

/* Tower placement overlay */
#placement-hint {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  color: #00BFFF;
  font-size: 20px;
  font-weight: bold;
  text-shadow: 0 2px 6px rgba(0,0,0,0.8);
  pointer-events: none;
  display: none;
}
#placement-hint.active { display: block; }

/* Equipped gun chip */
#weapon-display {
  display: none;
  align-items: center; gap: 6px;
  color: #fff; font-size: 13px; font-weight: 700;
  background: rgba(255,255,255,.12);
  border: 1px solid rgba(255,255,255,.25);
  border-radius: 12px;
  padding: 4px 10px;
  white-space: nowrap;
}
#weapon-display span { font-size: 16px; }

/* Enemies remaining badge */
#enemies-remaining { margin-left: 8px; opacity: .9; font-size: 13px; }

/* Combo counter (floats under the top bar) */
#combo-display {
  position: absolute;
  top: 58px; right: 24px;
  color: #FF69B4;
  font-size: 26px; font-weight: 900;
  text-shadow: 0 2px 8px rgba(0,0,0,.7);
  pointer-events: none;
  opacity: 0;
  transform: scale(0.8);
  transition: opacity .2s, transform .2s;
}
#combo-display.visible { opacity: 1; transform: scale(1); }

/* Intermission countdown / shop nudge */
#intermission {
  position: absolute;
  top: 46%; left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  color: #fff;
  font-size: 22px; font-weight: 800;
  text-shadow: 0 2px 8px rgba(0,0,0,.8);
  pointer-events: none;
  display: none;
}
#intermission.visible { display: block; }
#intermission .big { display:block; font-size: 30px; color:#9BE34B; margin-bottom: 4px; }
#intermission .small { display:block; font-size: 16px; color:#FFC700; }

/* Low-health red vignette */
#lowhp-vignette {
  position: fixed; inset: 0;
  pointer-events: none; z-index: 8;
  box-shadow: inset 0 0 160px 40px rgba(220,20,20,0.0);
  transition: box-shadow .3s ease;
}
#lowhp-vignette.active {
  box-shadow: inset 0 0 160px 50px rgba(220,20,20,0.55);
  animation: hppulse 1.1s ease-in-out infinite;
}
@keyframes hppulse {
  0%,100% { box-shadow: inset 0 0 150px 40px rgba(220,20,20,0.40); }
  50%     { box-shadow: inset 0 0 180px 60px rgba(220,20,20,0.62); }
}

/* Wave banner: allow longer messages to wrap */
#wave-banner h2 { max-width: 90vw; }
#wave-banner p  { max-width: 80vw; margin: 0 auto; }

/* Intro objective ribbon */
#objective {
  position: absolute;
  top: 92px; left: 50%;
  transform: translateX(-50%);
  background: linear-gradient(90deg, rgba(255,199,0,.92), rgba(255,140,0,.92));
  color: #2a1a00;
  font-size: 16px; font-weight: 800;
  padding: 8px 22px;
  border-radius: 20px;
  box-shadow: 0 4px 14px rgba(0,0,0,.35);
  pointer-events: none;
  white-space: nowrap;
  animation: objpulse 1.6s ease-in-out infinite;
}
@keyframes objpulse { 0%,100% { transform: translateX(-50%) scale(1); } 50% { transform: translateX(-50%) scale(1.05); } }

/* "Press E" interaction prompt near the Pineapple */
#npc-prompt {
  position: fixed;
  bottom: 120px; left: 50%;
  transform: translateX(-50%);
  background: rgba(0,0,0,.78);
  color: #fff; font-size: 18px; font-weight: 700;
  padding: 10px 22px; border-radius: 12px;
  border: 2px solid #00BFFF;
  box-shadow: 0 0 18px rgba(0,191,255,.5);
  pointer-events: none; z-index: 30;
}

/* ── Pineapple shop ── */
#shop-panel {
  position: fixed; inset: 0;
  background: rgba(8,10,22,.6);
  backdrop-filter: blur(3px);
  display: none; align-items: center; justify-content: center;
  z-index: 120; pointer-events: auto;
}
#shop-card {
  /* Wider than it used to be: the gun cards now carry a render each, and the
     upgrades sit two-across, so the panel trades height for width and fits a
     1080p screen without a scrollbar. */
  width: min(580px, 94vw);
  max-height: min(92vh, 92dvh);
  overflow-y: auto;
  background: linear-gradient(160deg, #1d2b1a, #14202a);
  border: 3px solid #FFC700;
  border-radius: 18px;
  padding: 20px 22px;
  box-shadow: 0 20px 60px rgba(0,0,0,.6);
}
#shop-card h2 { color: #FFD400; font-size: 24px; text-align: center; margin-bottom: 2px; }
#shop-seeds { color: #9BE34B; text-align: center; font-size: 16px; font-weight: 700; margin-bottom: 8px; }

.shop-section {
  color: rgba(255,255,255,.55);
  font-size: 11px; font-weight: 800;
  text-transform: uppercase; letter-spacing: 1px;
  margin: 10px 2px 6px;
}

/* Gun cards — 2×2 grid of buy/equip tiles */
#shop-weapons {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.weapon-card {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  background: rgba(255,255,255,.06);
  border: 2px solid rgba(255,255,255,.14);
  border-radius: 12px;
  padding: 10px 8px 8px;
  cursor: pointer; color: #fff; text-align: center;
  transition: transform .08s, background .15s, border-color .15s;
}
.weapon-card:hover { background: rgba(255,255,255,.12); transform: translateY(-1px); }
/* The gun's own render fills the top of the card; the emoji stays as a small
   corner badge so the 1-4 swap keys and the HUD chip keep a shared symbol. */
/* 16:9, not 4:3. The renders are wide subjects on a plain backdrop, so the
   crop costs nothing — and four 4:3 tiles pushed the upgrades section off the
   bottom of a 1080p screen, which on the tutorial visit meant the player never
   saw that upgrades exist. */
.weapon-art {
  position: relative;
  display: block; width: 100%; aspect-ratio: 16 / 9;
  background-size: cover; background-position: center;
  border-radius: 8px;
  margin-bottom: 4px;
  box-shadow: inset 0 -28px 28px -20px rgba(0,0,0,.75);
}
.weapon-art .weapon-icon {
  position: absolute; right: 4px; bottom: 3px;
  font-size: 18px; line-height: 1;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.9));
}
/* Anything shorter than a full-height 1080p window: letterbox harder and drop
   the blurbs rather than make the player scroll past the guns to reach the
   upgrades. The name and the stat line still say what each gun does. */
@media (max-height: 940px) {
  .weapon-art { aspect-ratio: 21 / 9; }
  .weapon-blurb { display: none; }
}
.weapon-icon { font-size: 28px; line-height: 1; }
.weapon-name { font-size: 14px; }
.weapon-blurb { color: rgba(255,255,255,.55); font-size: 11px; }
.weapon-stats { color: #FFC700; font-size: 11px; font-weight: 700; }
.weapon-state {
  margin-top: 4px;
  font-size: 12px; font-weight: 900;
  color: #9BE34B;
  background: rgba(0,0,0,.35);
  border-radius: 8px;
  padding: 3px 10px;
}
.weapon-card.equipped { border-color: #9BE34B; background: rgba(123,211,33,.14); }
.weapon-card.equipped .weapon-state { color: #1d2b1a; background: #9BE34B; }
.weapon-card.owned .weapon-state { color: #00BFFF; }
.weapon-card.cant-afford { opacity: .55; }
.weapon-card.cant-afford .weapon-state { color: #FF6B6B; }
.weapon-card.buy-ok { animation: buyok .4s ease; }
.weapon-card.buy-no { animation: buyno .3s ease; }
/* Two across: six full-width rows were the single biggest block of vertical
   space in the panel, and halving them is what removes the scrollbar. */
/* minmax(0, …), not 1fr: a plain 1fr track refuses to shrink below its
   min-content, and these rows are wide enough to push the panel into a
   horizontal scrollbar. */
#shop-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.shop-row {
  display: flex; align-items: center; gap: 8px;
  background: rgba(255,255,255,.06);
  border: 2px solid rgba(255,255,255,.12);
  border-radius: 12px;
  padding: 8px 10px;
  cursor: pointer; color: #fff; text-align: left;
  transition: transform .08s, background .15s, border-color .15s;
}
.shop-row:hover { background: rgba(255,255,255,.12); transform: translateY(-1px); }
.shop-key {
  flex: 0 0 20px; height: 20px; line-height: 20px; text-align: center;
  background: #FFC700; color: #222; border-radius: 6px; font-weight: 900; font-size: 12px;
}
.shop-icon { font-size: 20px; }
/* min-width:0 lets a long upgrade name ellipsize instead of forcing the
   column wider than its half of the grid. */
.shop-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.shop-text b { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.shop-text small {
  color: rgba(255,255,255,.6); font-size: 11px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.shop-cost { color: #9BE34B; font-weight: 800; font-size: 13px; white-space: nowrap; }
/* A phone in portrait can't hold two of these side by side. Still minmax(0,…):
   a bare 1fr takes its minimum from the row's min-content and pushes the whole
   panel into a horizontal scrollbar. */
@media (max-width: 520px) {
  #shop-list { grid-template-columns: minmax(0, 1fr); }
}
.shop-row.cant-afford { opacity: .55; }
.shop-row.cant-afford .shop-cost { color: #FF6B6B; }
.shop-row.maxed { opacity: .6; cursor: default; }
.shop-row.maxed .shop-cost { color: #FFC700; }
.shop-row.buy-ok { animation: buyok .4s ease; }
.shop-row.buy-no { animation: buyno .3s ease; }
@keyframes buyok { 0% { background: rgba(123,211,33,.6); } 100% { background: rgba(255,255,255,.06); } }
@keyframes buyno { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-6px); } 75% { transform: translateX(6px); } }
#shop-close {
  margin-top: 16px; width: 100%;
  background: #FFC700; border: none; border-radius: 10px;
  padding: 10px; font-size: 15px; font-weight: 800; color: #222; cursor: pointer;
}
#shop-close:hover { background: #FFD700; }

/* ── Cover-art screens (title / pause / end) ────────────────────────────────
   All three share one layout: full-bleed key art, a darkening veil so text
   stays legible, and a centred content column. */
.cover-screen {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.cover-screen.visible { display: flex; }

.cover-bg {
  position: absolute; inset: 0;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

/* The key art has its hero dead centre, so the veil darkens only the top and
   bottom bands where text actually sits and leaves the middle untouched. */
.cover-veil {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom,
    rgba(6,9,20,0.88) 0%,
    rgba(6,9,20,0.62) 12%,
    rgba(6,9,20,0.10) 26%,
    rgba(6,9,20,0.00) 42%,
    rgba(6,9,20,0.00) 52%,
    rgba(6,9,20,0.22) 64%,
    rgba(6,9,20,0.72) 82%,
    rgba(6,9,20,0.93) 100%);
}
.cover-veil.strong {
  background: linear-gradient(to bottom,
    rgba(6,9,20,0.93) 0%,
    rgba(6,9,20,0.72) 14%,
    rgba(6,9,20,0.30) 30%,
    rgba(6,9,20,0.22) 50%,
    rgba(6,9,20,0.42) 66%,
    rgba(6,9,20,0.85) 84%,
    rgba(6,9,20,0.96) 100%);
}

/* Content is pinned to the two darkened bands; the centre stays clear so the
   artwork reads as artwork rather than a background behind a menu. */
.cover-content {
  position: relative;
  z-index: 1;
  text-align: center;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  width: 100%;
  height: 100%;
  padding: calc(18px + var(--safe-t)) calc(20px + var(--safe-r)) calc(20px + var(--safe-b)) calc(20px + var(--safe-l));
}
.cover-top, .cover-bottom {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  flex: 0 0 auto;
}
.cover-top { gap: 4px; }
.cover-bottom { gap: 10px; }

/* Title sits on one line where possible so it reads as a banner across the top
   rather than a block dropped over the picture. */
.game-title {
  font-size: clamp(28px, 5.6vw, 56px);
  line-height: 1.04;
  color: #FFD400;
  font-weight: 900;
  letter-spacing: 1px;
  text-shadow: 0 3px 0 #7a4a00, 0 8px 24px rgba(0,0,0,.9);
}
.game-title span { color: #9BE34B; }
.game-title.small { font-size: clamp(24px, 4.4vw, 40px); }

.game-tagline {
  color: rgba(255,255,255,.92);
  font-size: clamp(13px, 2vw, 17px);
  text-shadow: 0 2px 8px #000, 0 0 18px rgba(0,0,0,.8);
}

/* Buttons run in a row along the bottom band, keeping their footprint short. */
.menu-buttons {
  display: flex; flex-flow: row wrap; gap: 10px;
  align-items: center; justify-content: center;
}
.menu-btn {
  min-width: 190px;
  min-height: 50px;
  background: rgba(12,18,34,.72);
  border: 2px solid rgba(255,255,255,.34);
  border-radius: 12px;
  padding: 11px 24px;
  font-size: 17px;
  font-weight: 800;
  color: #fff;
  cursor: pointer;
  backdrop-filter: blur(4px);
  box-shadow: 0 6px 20px rgba(0,0,0,.45);
  transition: background .15s, transform .1s, border-color .15s;
}
.menu-btn:hover { background: rgba(30,44,74,.85); transform: translateY(-2px); }
.menu-btn:active { transform: translateY(0); }
.menu-btn.primary {
  background: linear-gradient(180deg, #FFD400, #FF9E00);
  border-color: #FFE774;
  color: #3a2400;
}
.menu-btn.primary:hover { background: linear-gradient(180deg, #FFE04D, #FFAE20); }

.menu-stats {
  color: #fff; font-size: clamp(14px, 2vw, 17px); line-height: 1.6;
  text-shadow: 0 2px 6px #000, 0 0 16px rgba(0,0,0,.85);
}
.menu-stats b { color: #FFD400; }
.menu-best { color: #9BE34B; font-size: 14px; font-weight: 700; text-shadow: 0 2px 6px #000; }
.menu-hint {
  color: rgba(255,255,255,.72);
  font-size: 12px; line-height: 1.6;
  text-shadow: 0 2px 6px #000;
}
.menu-row {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  color: #fff; font-size: 14px;
  text-shadow: 0 2px 6px #000;
}
.menu-row input[type=range] { width: 150px; accent-color: #FFC700; }

/* Audio settings — one grid so the mute buttons, labels, sliders and readouts
   line up across both rows regardless of how long the translated labels are. */
.menu-settings {
  display: inline-grid;
  /* minmax(0, 1fr) on the slider column, not plain 1fr: a range input has an
     intrinsic min-content width (~129px in Chrome) that a bare 1fr refuses to
     go under, which pushed the readout outside the panel on phone widths. */
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px 12px;
  margin-top: 6px;
  padding: 12px 16px;
  border-radius: 14px;
  background: rgba(0,0,0,0.38);
  border: 1px solid rgba(255,255,255,0.12);
  max-width: min(420px, calc(100vw - 48px));
}
.menu-settings .menu-row { display: contents; }
.menu-settings .opt-label { justify-self: start; white-space: nowrap; }
.menu-settings input[type=range] { width: 100%; min-width: 0; }
.menu-settings input[type=range]:disabled { opacity: .35; cursor: not-allowed; }

/* Phone widths: mute + label + slider + readout don't fit on one line, so the
   label moves above its own row.
   Each row becomes its OWN grid here rather than staying `display: contents` —
   with a full-width label in a shared grid, auto-placement packs the next row's
   mute button into the leftover cell beside the previous row's readout. Every
   cell is therefore placed explicitly, since DOM order is mute-label-slider-val
   but the label has to render first. */
@media (max-width: 430px) {
  .menu-settings {
    display: flex; flex-direction: column;
    gap: 14px;
    padding: 12px;
  }
  .menu-settings .menu-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 4px 10px;
    align-items: center;
  }
  .menu-settings .opt-label {
    grid-row: 1; grid-column: 1 / -1;
    font-size: 13px; opacity: .85;
  }
  .menu-settings .opt-mute { grid-row: 2; grid-column: 1; }
  .menu-settings input[type=range] { grid-row: 2; grid-column: 2; }
  .menu-settings .opt-val { grid-row: 2; grid-column: 3; }
}

.opt-mute {
  width: 34px; height: 34px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 16px; line-height: 1; cursor: pointer;
  border-radius: 10px;
  border: 1px solid rgba(255,255,255,0.22);
  background: rgba(255,255,255,0.10);
  color: #fff;
  transition: background .15s ease, border-color .15s ease, transform .08s ease;
}
.opt-mute:hover { background: rgba(255,255,255,0.20); }
.opt-mute:active { transform: scale(0.94); }
.opt-mute.muted {
  background: rgba(255,74,74,0.22);
  border-color: rgba(255,120,120,0.55);
}
.opt-val {
  justify-self: end;
  min-width: 4.2em; text-align: right;
  font-variant-numeric: tabular-nums;
  color: #FFC700; font-size: 13px; font-weight: 700;
}
.opt-mute.muted + .opt-label { opacity: .55; }

/* ── Graphics quality picker (touch devices only) ──────────────────────────────
   Mounted on both the title screen and the pause menu. Deliberately quiet: a
   small segmented control plus one line of explanation, sitting under the
   primary buttons so it never competes with "Play". */
.gfx-picker {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  margin-top: 10px;
}
.gfx-line { display: flex; align-items: center; gap: 10px; }
.gfx-label {
  color: #fff; font-size: 14px; font-weight: 700;
  text-shadow: 0 2px 6px #000;
}
.gfx-seg {
  display: inline-flex; gap: 2px; padding: 2px;
  border-radius: 12px;
  background: rgba(0,0,0,0.45);
  border: 1px solid rgba(255,255,255,0.18);
}
.gfx-btn {
  min-width: 58px; padding: 7px 12px;
  border: 0; border-radius: 10px;
  background: transparent; color: rgba(255,255,255,0.72);
  font: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
  transition: background .15s ease, color .15s ease, transform .08s ease;
}
.gfx-btn:active { transform: scale(0.95); }
.gfx-btn.active {
  background: #FFC700; color: #23180a;
  box-shadow: 0 2px 8px rgba(0,0,0,.45);
}
.gfx-hint {
  max-width: 32em; text-align: center;
  color: rgba(255,255,255,0.78); font-size: 12px; line-height: 1.35;
  text-shadow: 0 2px 6px #000;
}

/* The end-of-run overlay reuses the cover art via a background image */
#overlay-title {
  font-size: clamp(30px, 5.6vw, 52px); color: #FFC700;
  text-shadow: 0 3px 0 #7a4a00, 0 8px 24px rgba(0,0,0,.9); font-weight: 900;
}
#overlay-sub {
  color: #fff; font-size: clamp(14px, 2.2vw, 19px);
  text-shadow: 0 2px 8px #000, 0 0 18px rgba(0,0,0,.8);
}

/* Loading screen */
#loading-screen {
  position: fixed;
  inset: 0;
  background: #0d1b2a;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
  overflow: hidden;
}
#loading-art {
  position: absolute; inset: 0;
  background-position: center;
  background-size: cover;
}
/* Same top/bottom banding as the other cover screens, so the art reads here too */
#loading-screen .cover-veil {
  background: linear-gradient(to bottom,
    rgba(6,9,20,0.86) 0%, rgba(6,9,20,0.40) 20%, rgba(6,9,20,0.00) 40%,
    rgba(6,9,20,0.00) 58%, rgba(6,9,20,0.55) 80%, rgba(6,9,20,0.92) 100%);
}
#loading-content {
  position: relative; z-index: 1; text-align: center;
  align-self: flex-start;
  padding: calc(24px + var(--safe-t)) 20px 0;
  width: 100%;
}
#loading-screen h1 {
  color: #FFC700; font-size: clamp(26px, 6vw, 42px); margin-bottom: 12px;
  text-shadow: 0 3px 12px rgba(0,0,0,.9);
}
#loading-screen p { color: rgba(255,255,255,0.85); font-size: 16px; text-shadow: 0 2px 6px #000; }

/* Boot progress — the bar lives in the lower band of the cover art, where the
   veil is already dark enough to read against. */
#loading-bottom {
  position: absolute; z-index: 2;
  left: 50%; transform: translateX(-50%);
  bottom: calc(28px + var(--safe-b));
  width: min(520px, calc(100vw - 48px));
  text-align: center;
}
#loading-tip {
  min-height: 2.6em; margin-bottom: 14px;
  color: rgba(255,255,255,0.9); font-size: 14px; line-height: 1.3;
  text-shadow: 0 2px 6px #000;
  opacity: 0; transition: opacity .32s ease;
}
#loading-tip.visible { opacity: 1; }
#loading-bar {
  position: relative;
  height: 12px; border-radius: 999px; overflow: visible;
  background: rgba(0,0,0,0.45);
  border: 1px solid rgba(255,255,255,0.18);
  box-shadow: inset 0 2px 5px rgba(0,0,0,.5);
}
#loading-fill {
  position: absolute; inset: 1px auto 1px 1px;
  width: 0%;
  border-radius: 999px;
  background: linear-gradient(90deg, #3FA34D 0%, #7BC950 55%, #FFC700 100%);
  box-shadow: 0 0 12px rgba(123,201,80,.55);
  /* No CSS transition: loading.js eases the value itself, and a transition on
     top of that would lag behind and overshoot. */
}
#loading-seed {
  position: absolute; right: -9px; top: 50%;
  transform: translateY(-50%);
  font-size: 17px;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,.7));
}
#loading-status {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-top: 9px;
  color: rgba(255,255,255,0.8); font-size: 13px; text-shadow: 0 2px 5px #000;
}
#loading-pct { font-variant-numeric: tabular-nums; color: #FFC700; font-weight: 700; }

#loading-screen {
  opacity: 1;
  transition: opacity .45s ease;
}
#loading-screen.done { opacity: 0; pointer-events: none; }

/* ── Touch controls ─────────────────────────────────────────────────────────
   Hidden entirely on pointer-fine devices; touch.js adds .touch-mode to body. */
#touch-controls { display: none; }

body.touch-mode #touch-controls {
  display: block;
  position: fixed; inset: 0;
  z-index: 40;
  pointer-events: none;
}
body.touch-mode #touch-controls.hidden { display: none; }

#stick-base {
  position: absolute;
  width: 132px; height: 132px;
  margin: -66px 0 0 -66px;
  border-radius: 50%;
  border: 3px solid rgba(255,255,255,.35);
  background: rgba(0,0,0,.25);
  opacity: 0;
  transition: opacity .15s;
  pointer-events: none;
}
#stick-base.active { opacity: 1; }
#stick-knob {
  position: absolute; top: 50%; left: 50%;
  width: 58px; height: 58px;
  margin: 0; border-radius: 50%;
  background: rgba(255,255,255,.6);
  box-shadow: 0 2px 12px rgba(0,0,0,.5);
  transform: translate(-50%, -50%);
}

#touch-right {
  position: absolute;
  right: calc(20px + var(--safe-r));
  bottom: calc(28px + var(--safe-b));
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 14px;
}
/* Top-right corner, clear of the thumb zone. #touch-controls is fixed/inset:0,
   so this anchors to the viewport rather than to the action pad. */
#touch-toggles {
  position: absolute;
  top: calc(10px + var(--safe-t));
  right: calc(12px + var(--safe-r));
  display: flex;
  gap: 8px;
  pointer-events: auto;
  z-index: 2;
}
#touch-buttons {
  display: grid;
  grid-template-columns: auto auto;
  gap: 12px;
  align-items: end;
  pointer-events: auto;
}
.touch-btn {
  width: 78px; height: 78px;
  border-radius: 50%;
  border: 3px solid rgba(255,255,255,.4);
  background: rgba(20,30,50,.55);
  color: #fff;
  font-size: 14px; font-weight: 900;
  cursor: pointer;
  touch-action: none;
  backdrop-filter: blur(3px);
}
.touch-btn.big { width: 94px; height: 94px; font-size: 16px; }
.touch-btn.pressed { background: rgba(255,199,0,.55); transform: scale(0.94); }
.touch-btn.action {
  font-size: 32px;
  opacity: .35;
  border-color: rgba(255,255,255,.25);
}
.touch-btn.action.available {
  opacity: 1;
  border-color: #00BFFF;
  box-shadow: 0 0 18px rgba(0,191,255,.6);
  animation: objpulse 1.4s ease-in-out infinite;
}

/* Round, high-contrast and a full 48px touch target — the old 54×42 grey pill
   in the action cluster read as a disabled chip, not as "pause". */
.touch-toggle {
  display: flex; align-items: center; justify-content: center;
  width: 48px; height: 48px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,.55);
  background: rgba(12,18,32,.72);
  color: #fff;
  font-size: 14px; font-weight: 900;
  letter-spacing: 1px;
  cursor: pointer;
  touch-action: none;
  backdrop-filter: blur(3px);
  box-shadow: 0 2px 10px rgba(0,0,0,.45);
}
.touch-toggle.on { color: #9BE34B; border-color: #9BE34B; }
.touch-toggle.pressed { background: rgba(255,199,0,.55); transform: scale(0.94); }
#btn-pause { font-size: 16px; }

/* Make room for it: the wave chip owns the top-right corner otherwise. */
body.touch-mode #hud-top { padding-right: calc(66px + var(--safe-r)); }

/* ── Responsive / mobile layout ─────────────────────────────────────────── */
@media (max-width: 820px) {
  #hud-top { gap: 6px 10px; padding-top: calc(8px + var(--safe-t)); padding-bottom: 8px; }
  #health-bar-bg { width: 92px; height: 14px; }
  #health-icon { font-size: 16px; }
  #health-text { font-size: 11px; }
  #seeds-display, #score-display { font-size: 14px; }
  #wave-display { font-size: 13px; padding: 3px 9px; }
  #weapon-display { font-size: 0; gap: 0; padding: 4px 8px; }   /* icon only on phones */
  #weapon-display span { font-size: 16px; }
  /* The top bar wraps to two rows on a phone, so everything below it shifts
     down into its own band instead of colliding with the wave counter. */
  #purify-bar-wrap { top: 72px; width: min(260px, 62vw); }
  #purify-label { font-size: 11px; }
  #boss-bar-wrap { top: 108px; }
  #objective { font-size: 13px; padding: 6px 14px; white-space: normal; max-width: 88vw; top: 108px; }
  #combo-display { font-size: 20px; top: 178px; right: 14px; }
  #intermission { font-size: 17px; width: 88vw; }
  #intermission .big { font-size: 22px; }
  #intermission .small { font-size: 13px; }
  #wave-banner h2 { font-size: 30px; }
  #wave-banner p { font-size: 15px; }
  #npc-prompt { bottom: 150px; font-size: 15px; padding: 8px 16px; }
  .menu-btn { min-width: 220px; }
  /* Keyboard legend is dead weight on a phone, and the space it frees keeps
     the bottom band off the hero in the artwork. */
  .menu-hint { display: none; }
}

/* Keyboard hints are noise on touch; the tower button needs a fat tap target */
body.touch-mode #controls-hint { display: none; }
body.touch-mode #hud-bottom {
  background: none;
  backdrop-filter: none;
  justify-content: flex-start;
  padding-bottom: calc(8px + var(--safe-b));
  /* The bar is invisible here but spans the full width — leaving it clickable
     would eat joystick touches in the bottom-left corner of the play area. */
  pointer-events: none;
}
body.touch-mode #tower-menu { pointer-events: auto; }
body.touch-mode .tower-btn { padding: 12px 18px; font-size: 15px; min-height: 48px; }
body.touch-mode #tower-menu-label { display: none; }

/* Landscape phones: the HUD has to give the play area room */
@media (max-height: 480px) and (orientation: landscape) {
  #hud-top { padding-top: calc(4px + var(--safe-t)); padding-bottom: 4px; }
  #purify-bar-wrap { top: 38px; }
  #boss-bar-wrap { top: 70px; }
  #objective { top: 68px; }
  .touch-btn { width: 64px; height: 64px; }
  .touch-btn.big { width: 76px; height: 76px; }
  /* Short viewports: squeeze the bands so the art still shows between them. */
  .cover-content { padding-top: calc(8px + var(--safe-t)); padding-bottom: calc(8px + var(--safe-b)); }
  .menu-btn { min-height: 42px; min-width: 160px; padding: 8px 18px; font-size: 15px; }
  .menu-buttons { gap: 8px; }
  .cover-top, .cover-bottom { gap: 4px; }
  .menu-hint { display: none; }
  .game-title { font-size: clamp(22px, 4vw, 34px); }
}

/* ─── Opening cinematic ─────────────────────────────────────────────────────
   Letterboxed captions over the intro sequence. The whole HUD is suppressed
   with body.cinematic so the establishing shots read as film, not gameplay. */
body.cinematic #hud,
body.cinematic #touch-controls,
body.cinematic #npc-prompt { display: none !important; }

body.cinematic::before,
body.cinematic::after {
  content: '';
  position: fixed;
  left: 0; right: 0;
  height: 8vh;
  background: #000;
  z-index: 30;
  pointer-events: none;
}
body.cinematic::before { top: 0; }
body.cinematic::after  { bottom: 0; }

#cine-caption {
  position: fixed;
  bottom: 14vh; left: 50%;
  transform: translateX(-50%) translateY(10px);
  width: min(90vw, 780px);
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s, transform 0.5s;
  z-index: 31;
}
#cine-caption.visible { opacity: 1; transform: translateX(-50%) translateY(0); }
#cine-caption h2 {
  font-size: clamp(24px, 4.4vw, 44px);
  color: #FFE9A6;
  font-weight: 900;
  text-shadow: 0 3px 12px rgba(0,0,0,0.9);
  margin-bottom: 6px;
}
#cine-caption p {
  font-size: clamp(14px, 2vw, 20px);
  color: #fff;
  text-shadow: 0 2px 8px rgba(0,0,0,0.9);
}

#cine-skip {
  position: fixed;
  right: 3vw; bottom: 10vh;
  color: rgba(255,255,255,0.65);
  font-size: 14px;
  text-shadow: 0 1px 4px rgba(0,0,0,0.9);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s;
  z-index: 31;
}
#cine-skip.visible { opacity: 1; }

/* Impact frame: full-screen violet flash. Slow fade-out, near-instant pop-in. */
#cine-flash {
  position: fixed; inset: 0;
  background: radial-gradient(circle at 50% 45%,
    rgba(236,180,255,0.95), rgba(150,60,200,0.55) 45%,
    rgba(40,10,60,0.25) 75%, transparent);
  opacity: 0;
  pointer-events: none;
  z-index: 29;
  transition: opacity 0.7s ease-out;
}
#cine-flash.visible { opacity: 1; transition: opacity 0.04s; }

/* Shop progression: entries the Pineapple hasn't unlocked yet. */
.weapon-card.locked, .shop-row.locked {
  opacity: 0.45;
  filter: grayscale(0.75);
}
.shop-note {
  font-size: 12px;
  color: #a8a29b;
  margin: 2px 0 6px;
  text-align: center;
}

/* Language toggle on the title screen. */
.lang-toggle {
  position: absolute;
  top: max(14px, env(safe-area-inset-top));
  right: max(16px, env(safe-area-inset-right));
  display: flex;
  gap: 6px;
  z-index: 5;
}
.lang-btn {
  padding: 6px 12px;
  border: 2px solid rgba(255,255,255,0.35);
  border-radius: 8px;
  background: rgba(0,0,0,0.35);
  color: rgba(255,255,255,0.75);
  font-weight: 800;
  font-size: 14px;
  cursor: pointer;
}
.lang-btn.active {
  background: #FFC700;
  border-color: #FFC700;
  color: #3b2b00;
}

/* Tower upgrade prompt — same family as the Pineapple's interaction prompt,
   green accent so "build/upgrade" and "shop" read as different verbs. */
#tower-prompt {
  position: fixed;
  bottom: 120px; left: 50%;
  transform: translateX(-50%);
  background: rgba(0,0,0,.78);
  color: #fff; font-size: 18px; font-weight: 700;
  padding: 10px 22px; border-radius: 12px;
  border: 2px solid #7ED321;
  box-shadow: 0 0 18px rgba(126,211,33,.5);
  pointer-events: none; z-index: 30;
}
@media (max-width: 700px) {
  #tower-prompt { bottom: 150px; font-size: 15px; padding: 8px 16px; }
}

/* ── iOS GPU-memory guard ─────────────────────────────────────────────────────
   A backdrop-filter over a live WebGL canvas makes WebKit snapshot what's
   behind the element into extra GPU surfaces (full device resolution, several
   buffers per filter region) and re-composite the canvas through the blur
   chain every frame. On iPhone that allocation — the HUD bars and touch
   buttons permanently, plus the shop's full-screen layer the moment it opens —
   is what pushed the GPU process into a reset (screen goes dark mid-game,
   worst right after arming at the Pineapple, when the shop first appears).
   Phones therefore run with NO backdrop blur anywhere; slightly more opaque
   backgrounds keep the legibility the blur was providing. Desktop keeps the
   pretty version. */
@media (pointer: coarse) {
  #hud-top, #hud-bottom, #shop-panel, .menu-btn, .touch-btn, .touch-toggle {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  #hud-top, #hud-bottom { background: rgba(0,0,0,0.52); }
  /* Opaque, not just dark: main.js throttles the 3D render to ~8 fps while the
     shop is open (nothing in the world is ticking behind it), and at .82 the
     field showed through and stuttered. The extra 15% costs nothing visually
     and turns the throttle into something the player cannot see. */
  #shop-panel  { background: rgb(8,10,22); }
  .menu-btn    { background: rgba(12,18,34,.92); }
  .touch-btn   { background: rgba(20,30,50,.72); }
  .touch-toggle { background: rgba(12,18,32,.88); }
}
