/* =============================================================
   NOCTWARD styles
   Sections (search the marker to jump): HUD, overlays, bestiary,
   level up choices, menu, chest reveal, audio/language controls,
   joystick, evolution reference, mobile.
   Colors come from the :root tokens below - prefer a token over a
   raw hex so the palette stays consistent.
   ============================================================= */
:root {
  /* "Obsidian" palette: near-black, minimal, flat, with a periwinkle-blue accent.
     Accent is exposed as raw "r, g, b" too so the many glow/shadow rgba()s below
     can share the hue. */
  --accent: #6c7bff;
  --accent-2: #5effa6;
  --accent-rgb: 108, 123, 255;
  --accent-grad: #2b348c; /* dark stop of the xp / accent gradient */
  --btn-grad-top: #9aa6ff; /* light stop of the primary button gradient */
  --ink: #e9ecec; /* primary text */
  --ink-2: #a4aeb0; /* secondary text */
  --ink-3: #7b8587; /* dimmer labels */
  --ink-4: #545c5e; /* faint text / borders */
  --lav: #b4bdbe; /* light accents */
  --magenta: #8f9bff; /* accent variant (level-up, links) */
  --accent-active: #ffb38a; /* weapons (mirrors ACTIVE_COL in 09-combat.js) */
  --accent-passive: #8ec5ff; /* passives (mirrors PASSIVE_COL in 09-combat.js) */
  --danger: #ff4d6d;
  --gold: #ffe27a;
  /* surfaces. --bg is the whole-page backdrop, --panel the overlay panels,
     --card/--tray the raw "r,g,b" of the card + slot fills so each spot keeps
     its own alpha. */
  --bg: #040406;
  --panel: rgba(12, 13, 16, 0.96);
  --overlay: rgba(2, 2, 4, 0.84);
  --card-rgb: 20, 22, 26;
  --tray-rgb: 14, 16, 20;
  --panel-radius: 10px; /* panel corner roundness */
  --panel-border: rgba(255, 255, 255, 0.09); /* hairline panel edge */
  --mono: "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;
  --display: "Trebuchet MS", system-ui, sans-serif;
}
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html,
body {
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  font-family: var(--display);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  /* it's a game, not a document - stop the UI text from being drag-selected (and
     showing the browser's blue highlight) in menus. Real inputs opt back in below. */
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
/* text fields still need to be selectable/editable */
input,
textarea {
  -webkit-user-select: text;
  -moz-user-select: text;
  -ms-user-select: text;
  user-select: text;
}
#game {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  touch-action: none;
  cursor: crosshair;
}

/* ---------- HUD ---------- */
#hud {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 5;
  display: flex;
  flex-direction: column;
}
/* pre-run (title/menu) the HUD holds only its placeholder defaults - a 15:00
   clock and empty bars - which would show through the translucent overlay. Keep
   it hidden until a run goes live (in-run set in startGame). */
body:not(.in-run) #hud {
  display: none;
}
#topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  flex-wrap: wrap;
}
.clock {
  font-family: var(--mono);
  font-size: clamp(26px, 5vw, 38px);
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--ink);
  text-shadow: 0 0 18px rgba(var(--accent-rgb), 0.5);
  min-width: 92px;
  transition:
    opacity 0.2s ease,
    color 0.2s ease;
}
/* survival clock is frozen inside a dungeon/cave - dim it so the pause reads */
.clock.paused {
  color: var(--ink-2);
  opacity: 0.55;
  text-shadow: none;
}
.stat {
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 1px;
  color: var(--ink-2);
  display: flex;
  gap: 6px;
  align-items: baseline;
}
.stat b {
  color: var(--ink);
  font-size: 16px;
}
.stat-ico {
  font-size: 15px;
  line-height: 1;
}
.kills b {
  color: var(--danger);
}
.keys b {
  color: var(--gold);
}
/* `.stat { display:flex }` otherwise overrides the [hidden] attribute */
.stat[hidden] {
  display: none;
}
/* The XP bar sits under the topbar (HP now lives only under the player).
   With HP gone it gets the full width and a bit more presence. */
#bars {
  padding: 0 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  /* stay left of the top-right minimap on narrow screens; on wide screens the
     820px cap wins so the bar keeps its normal length */
  max-width: min(820px, calc(100vw - var(--mmw, 210px) - 8px));
}
/* transient objective banner (dungeon treasure hint). Centred near the top,
   non-interactive, fades in then out. Pointer-events off so it never blocks
   touch input on the play field. */
#toast {
  position: fixed;
  top: 86px;
  left: 50%;
  transform: translate(-50%, -8px);
  max-width: min(620px, 94vw);
  padding: 12px 18px;
  border-radius: 14px;
  background: rgba(16, 10, 28, 0.88);
  border: 1px solid rgba(255, 215, 107, 0.5);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
  color: var(--ink);
  font-size: 14px;
  line-height: 1.45;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  z-index: 6; /* under the HUD control buttons so it never covers them on phones */
  transition:
    opacity 0.35s ease,
    transform 0.35s ease;
}
#toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
}
#toast .toast-title {
  display: block;
  font-family: var(--mono);
  font-weight: 700;
  letter-spacing: 1px;
  color: var(--gold);
  margin-bottom: 3px;
}
/* achievement-unlock banners: their OWN stack (never share #toast), so a gameplay
   objective/event toast can't overwrite an "Achievement unlocked" popup. The stack
   sits below the objective toast and grows DOWNWARD, so several unlocks at once are
   all visible at a glance, gold-ribboned so each reads as a distinct feat. */
#achStack {
  position: fixed;
  top: 150px;
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: min(640px, 94vw);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  pointer-events: none;
  z-index: 8; /* above the objective toast (6) so both can coexist */
}
#achStack .achToast {
  width: 100%;
  padding: 11px 20px;
  border-radius: 14px;
  background: linear-gradient(
    180deg,
    rgba(40, 30, 8, 0.94),
    rgba(24, 16, 6, 0.94)
  );
  border: 1px solid rgba(255, 215, 107, 0.8);
  box-shadow:
    0 8px 30px rgba(0, 0, 0, 0.5),
    0 0 18px rgba(255, 206, 90, 0.25);
  color: var(--ink);
  text-align: center;
  opacity: 0;
  transform: translateY(-8px);
  transition:
    opacity 0.3s ease,
    transform 0.3s ease;
}
#achStack .achToast.show {
  opacity: 1;
  transform: translateY(0);
}
#achStack .achToast .at-badge {
  display: block;
  font-family: var(--mono);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 1.5px;
  color: var(--gold);
  text-transform: uppercase;
}
#achStack .achToast .at-name {
  display: block;
  font-weight: 700;
  font-size: 16px;
  margin: 2px 0 1px;
}
#achStack .achToast .at-desc {
  display: block;
  font-size: 12.5px;
  opacity: 0.82;
}
/* big centred reward banner (e.g. "Relic claimed"). Sits a little above the
   middle so it reads as a headline without covering the player. Pointer-events
   off so it never blocks the play field; pops in then fades out. */
#rewardBanner {
  position: fixed;
  top: 40%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.85);
  max-width: min(560px, 90vw);
  text-align: center;
  pointer-events: none;
  opacity: 0;
  z-index: 6;
  transition:
    opacity 0.35s ease,
    transform 0.35s cubic-bezier(0.2, 1.4, 0.4, 1);
}
#rewardBanner.show {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
#rewardBanner .rb-title {
  display: block;
  font-family: var(--mono);
  font-weight: 700;
  font-size: clamp(22px, 4.4vw, 38px);
  letter-spacing: 2px;
  color: var(--gold);
  text-shadow:
    0 0 18px rgba(255, 215, 107, 0.55),
    0 3px 10px rgba(0, 0, 0, 0.7);
}
#rewardBanner .rb-sub {
  display: block;
  margin-top: 6px;
  font-size: clamp(15px, 2.6vw, 22px);
  font-weight: 600;
  color: #e7d6ff;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.7);
}
.bar {
  position: relative;
  height: 14px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.1);
}
.bar > i {
  position: absolute;
  inset: 0;
  transform-origin: left;
  transform: scaleX(0);
  transition: transform 0.12s linear;
}
.xpbar {
  height: 18px;
  border-radius: 10px;
}
#xpfill {
  background: linear-gradient(90deg, var(--accent-grad), var(--accent));
  box-shadow: 0 0 14px rgba(var(--accent-rgb), 0.6);
}
.bar .lvl {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 1px;
  color: #fff;
  text-shadow: 0 1px 2px #000;
  z-index: 2;
}
/* weapon / passive inventory row */
#inv {
  margin-top: auto;
  padding: 12px 16px 16px;
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  align-items: flex-end;
}
/* active (weapons) and passive (upgrades) groups are visually split:
   each gets its own tinted tray + a labelled header row */
.invgroup {
  position: relative;
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  width: fit-content;
  padding: 16px 8px 8px;
  /* a faint tray so new players notice the hotbar instead of overlooking it */
  background: rgba(var(--tray-rgb), 0.4);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 12px;
}
/* label floats above the slots (absolute) so it never widens the tray */
.invgroup::before {
  position: absolute;
  top: 3px;
  left: 2px;
  font-family: var(--mono);
  font-size: 8.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.7;
  line-height: 1;
  white-space: nowrap; /* keep the label (incl. the drop hint) on one line */
}
#invweapons::before {
  content: "⚔ " attr(data-label);
  color: var(--accent-active);
}
#invpassives::before {
  content: "✦ " attr(data-label);
  color: var(--accent-passive);
}
.slot {
  width: 38px;
  height: 38px;
  border-radius: 9px;
  background: rgba(var(--tray-rgb), 0.85);
  border: 1px solid rgba(255, 255, 255, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  position: relative;
}
/* empty placeholder slots - show full capacity from the start */
.slot.empty {
  background: rgba(var(--tray-rgb), 0.35);
  border-style: dashed;
  border-color: rgba(255, 255, 255, 0.14);
  box-shadow: none;
}
/* filled weapon slots can be clicked/tapped to drop the weapon.
   #hud is pointer-events:none, so re-enable hits on these slots. */
#invweapons .slot.filled {
  cursor: pointer;
  pointer-events: auto;
  touch-action: manipulation;
}
/* filled slots need a clearly visible edge - the base 12% border all but
   vanished on mobile for T1 weapons (e.g. Spores), which read borderless.
   T2+ still override this with their brighter tier colours below. */
.slot.filled {
  border-color: rgba(255, 255, 255, 0.32);
}
.slot .dropx {
  position: absolute;
  top: -5px;
  right: -5px;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: #ff5a5a;
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  line-height: 15px;
  text-align: center;
  opacity: 0;
  transform: scale(0.6);
  transition:
    opacity 0.12s,
    transform 0.12s;
  pointer-events: none;
}
#invweapons .slot.filled:hover {
  border-color: #ff7a7a;
}
#invweapons .slot.filled:hover .dropx {
  opacity: 1;
  transform: scale(1);
}
/* touch devices have no hover, so keep a faint ✕ badge permanently visible on
   filled weapon slots - otherwise new players never discover they can drop
   items. Tapping the slot drops it (with a confirm dialog on touch). */
@media (hover: none) {
  #invweapons .slot.filled .dropx {
    display: block;
    opacity: 0.85;
    transform: scale(0.9);
  }
}
.slot .pips {
  position: absolute;
  bottom: 2px;
  right: 3px;
  font-family: var(--mono);
  font-size: 9px;
  color: var(--gold);
  font-weight: 700;
}
/* weapon tier colour ramp - kept far apart so tiers never read alike:
   T1 grey/white, T2 green, T3 blue, T4 gold. The border and the tier badge
   share each tier's accent. (.slot.max is also used by maxed passives/relics.) */
.slot.wslot.t1 {
  border-color: rgba(214, 220, 228, 0.5);
}
.slot.t2 {
  border-color: rgba(79, 214, 126, 0.85);
}
.slot.t3 {
  border-color: rgba(90, 160, 255, 0.9);
}
.slot.max {
  border-color: var(--gold);
  box-shadow: 0 0 8px rgba(255, 226, 122, 0.4);
}
/* weapon tiles are a touch taller than the base slot so the progress bar sits
   just below the full-size icon (no shrinking, no overlap). Empty weapon slots
   match the height so the row stays aligned. */
#invweapons .slot.wslot,
#invweapons .slot.empty {
  height: 40px;
}
/* colour-coded tier label, top-left corner: plain coloured text (no chip) so it
   stays compact, with a shadow to read over the icon. The grey->green->blue->gold
   ramp makes the tier unmistakable. Only weapon tiles carry it. */
.slot .tbadge {
  position: absolute;
  top: 1px;
  left: 3px;
  font: 700 9px/1 var(--mono);
  color: #d6dce4; /* T1: light grey */
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.95),
    0 0 2px rgba(0, 0, 0, 0.9);
  z-index: 3;
}
.slot.t2 .tbadge {
  color: #5be08a;
}
.slot.t3 .tbadge {
  color: #6fabff;
}
.slot.max .tbadge {
  color: var(--gold);
}
/* RELICS: dungeon-only rewards, set apart from ordinary passives with a warm
   gold frame + faint glow and a tinted tray, so they read as something special
   you earned rather than a normal stat pick. */
.slot.filled.relic {
  border-color: var(--gold);
  background: radial-gradient(
    120% 120% at 50% 0%,
    rgba(255, 215, 107, 0.22),
    rgba(40, 28, 10, 0.85)
  );
  box-shadow:
    0 0 10px rgba(255, 215, 107, 0.45),
    inset 0 0 6px rgba(255, 215, 107, 0.25);
}
.slot.filled.relic .pips {
  color: var(--gold);
}
/* one copy away from a merge - flag it on the HUD WITHOUT recoloring the slot
   (the tier colour must stay readable; a copy count is not a rarity). A soft
   neutral pulse signals "ready to merge" instead of a purple rarity border. */
.slot.merging {
  animation: mergeReady 1.1s ease-in-out infinite;
}
@keyframes mergeReady {
  0%,
  100% {
    box-shadow: 0 0 4px rgba(255, 255, 255, 0.25);
  }
  50% {
    box-shadow: 0 0 9px rgba(255, 255, 255, 0.6);
  }
}
/* merge progress: a segmented bar along the bottom edge (below the icon). Filled
   cells use the weapon accent (same colour as the ACTIVE header and the weapon
   level-up cards, distinct from the gold T4 tier); empty cells a clearly visible
   grey track. */
.slot .wbar {
  position: absolute;
  left: 4px;
  right: 4px;
  bottom: 3px;
  height: 5px;
  display: flex;
  gap: 3px;
  pointer-events: none;
}
.slot .wbar i {
  flex: 1;
  border-radius: 2px;
  background: rgba(255, 255, 255, 0.17);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.3);
}
.slot .wbar i.on {
  background: var(--accent-active);
  box-shadow: 0 0 5px rgba(255, 179, 138, 0.9);
}
/* mid level-up HOTBAR SWAP: the weapon bar glows and each filled slot pulses,
   prompting the player to tap the weapon to replace (number badge shows the key) */
#invweapons.swapping {
  outline: 2px solid rgba(255, 210, 122, 0.85);
  outline-offset: 3px;
  border-radius: 10px;
}
#invweapons.swapping .slot.filled {
  cursor: pointer;
  animation: swapPulse 0.9s ease-in-out infinite;
}
#invweapons.swapping .slot.empty {
  opacity: 0.3;
}
@keyframes swapPulse {
  0%,
  100% {
    box-shadow: 0 0 5px rgba(255, 210, 122, 0.4);
    transform: translateY(0);
  }
  50% {
    box-shadow: 0 0 12px rgba(255, 210, 122, 0.9);
    transform: translateY(-2px);
  }
}
.slot .slotkey {
  position: absolute;
  top: -7px;
  left: -5px;
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  color: #2a1430;
  background: rgba(255, 210, 122, 0.95);
  border-radius: 6px;
  padding: 0 4px;
  line-height: 14px;
}

/* ---------- overlays ---------- */
.overlay {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: none;
  align-items: center;
  justify-content: center;
  background: var(--overlay);
  backdrop-filter: blur(3px);
  padding: 20px;
}
.overlay.show {
  display: flex;
}
/* secondary panels opened on top of pause must sit above it */
#evo,
#opts,
#news,
#confirm,
#stats,
#bestiary,
#achievements,
#guide {
  z-index: 30;
}
/* ---------- bestiary ---------- */
.bestiary-panel {
  max-width: 720px;
}
.bestiary-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 10px;
  max-height: 60vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  margin: 14px 0 18px;
  text-align: left;
}
.best-card {
  background: rgba(var(--card-rgb), 0.9);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  padding: 12px;
}
/* inline coin glyph (the 🪙 emoji is missing on some systems) */
.coin-ico {
  width: 1.05em;
  height: 1.05em;
  vertical-align: -0.18em;
}
/* achievements overlay */
.ach-list {
  max-height: 60vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  text-align: left;
}
.ach-cat {
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--gold, #ffd76b);
  font-size: 13px;
  margin: 14px 2px 6px;
}
.ach-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 8px;
}
.ach-card {
  display: flex;
  align-items: center;
  gap: 10px;
  background: rgba(var(--card-rgb), 0.9);
  border: 1px solid rgba(255, 215, 107, 0.35);
  border-radius: 10px;
  padding: 8px 10px;
}
.ach-card.locked {
  border-color: rgba(255, 255, 255, 0.1);
  opacity: 0.55;
}
.ach-ico {
  font-size: 24px;
  flex: 0 0 auto;
  width: 30px;
  text-align: center;
}
.ach-name {
  font-weight: 700;
  font-size: 13px;
}
.ach-desc {
  font-size: 11px;
  color: var(--ink-3, #8a86a8);
}
.best-card.locked {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  opacity: 0.5;
  min-height: 90px;
}
.best-card.locked .best-spr {
  font-size: 30px;
  font-family: var(--mono);
  color: var(--ink-3);
}
.best-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.best-name {
  font-weight: 700;
  font-size: 15px;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 6px;
}
.best-tag {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 1px;
  padding: 1px 5px;
  border-radius: 5px;
  background: #ff4d6d;
  color: #1a0b2b;
}
.best-tag.elite {
  background: var(--gold);
}
.best-stats {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-2);
  margin-top: 2px;
}
.best-lore {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--lav);
  margin: 8px 0 6px;
}
.best-weak {
  font-size: 12px;
  line-height: 1.45;
  color: #9effc4;
}
.best-weak b {
  color: var(--ink);
}
.confirm-panel {
  max-width: 420px;
}
.confirm-panel h1 {
  color: #ff6b81;
  text-shadow: 0 0 24px rgba(255, 90, 110, 0.5);
}
/* destructive confirm button (red instead of the usual purple) */
.btn-danger {
  background: linear-gradient(#ff7a8c, #e23b54);
  color: #fff;
  box-shadow: 0 6px 20px rgba(226, 59, 84, 0.45);
}
.panel {
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--panel-radius);
  padding: clamp(24px, 5vw, 40px);
  text-align: center;
  max-width: 540px;
  width: 100%;
  box-shadow: 0 16px 60px rgba(0, 0, 0, 0.85);
}
.panel h1 {
  font-size: clamp(30px, 7vw, 46px);
  letter-spacing: 1.5px;
  color: var(--accent);
  text-shadow: none;
  margin-bottom: 10px;
}
.panel h1.win {
  color: var(--accent-2);
  text-shadow: 0 0 28px rgba(94, 255, 166, 0.6);
}
.panel p {
  font-size: 15px;
  line-height: 1.7;
  color: #c9bce4;
  margin-bottom: 18px;
}
.panel .keys {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--ink-2);
  margin: 14px 0 22px;
  line-height: 2;
}
.panel .keys kbd {
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 5px;
  padding: 2px 7px;
  font-size: 12px;
  color: #fff;
}
.stats-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 18px;
  margin-bottom: 22px;
  font-family: var(--mono);
  font-size: 14px;
}
.stats-grid div {
  display: flex;
  justify-content: space-between;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  padding-bottom: 4px;
}
.stats-grid span {
  color: var(--ink-3);
}
.stats-grid b {
  color: var(--ink);
}
/* attribute deltas: gain / loss / no change */
.stat-up {
  color: var(--accent-2);
}
.stat-down {
  color: var(--danger);
}
.stat-zero {
  color: var(--ink-4);
}
button {
  pointer-events: auto;
  font-family: var(--display);
  background: linear-gradient(var(--btn-grad-top), var(--accent));
  color: #1a0b2b;
  border: none;
  border-radius: 11px;
  padding: 14px 36px;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 0.5px;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(var(--accent-rgb), 0.4);
  /* iOS Safari: kill the 300ms tap delay + grey tap flash so buttons feel
     responsive and reliably register a tap */
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  -webkit-appearance: none;
  appearance: none;
}
button:hover {
  filter: brightness(1.08);
}
button:focus-visible {
  outline: 3px solid var(--gold);
}

/* ---------- level up choices ---------- */
#levelup .panel {
  max-width: 760px;
  /* small phones (e.g. iPhone mini) can overflow with 3 tall choices + the
     arsenal strip - let the panel scroll instead of clipping the buttons */
  max-height: 92vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
/* during a level-up we show the real HUD hotbar (not an in-panel copy), so lift
   the whole HUD above the modal backdrop. #hud establishes its own stacking
   context, so the bump has to be on #hud itself rather than on #inv. */
body.lu-open #hud {
  z-index: 25;
}
/* keep only the hotbar interactive during a level-up - the topbar buttons would
   otherwise become clickable over the modal and let a stray tap pause/close it */
body.lu-open #topbar {
  pointer-events: none;
}
/* heads-up banner on the level-up screen (e.g. weapon slots full) */
.lu-note {
  margin: 0 auto 14px;
  max-width: 520px;
  padding: 10px 14px;
  border-radius: 12px;
  background: rgba(255, 90, 90, 0.12);
  border: 1px solid rgba(255, 120, 120, 0.45);
  color: #ffd2c4;
  font-size: 13px;
  line-height: 1.4;
}
.lu-note[hidden] {
  display: none;
}
.choices {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  margin-top: 8px;
}
.choice {
  position: relative;
  pointer-events: auto;
  background: rgba(var(--card-rgb), 0.9);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 14px;
  padding: 18px 16px;
  cursor: pointer;
  text-align: left;
  /* these are cards, not raised action buttons - drop the inherited button shadow */
  box-shadow: none;
  transition:
    transform 0.12s ease,
    border-color 0.12s ease;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.choice:hover,
.choice:focus-visible {
  transform: translateY(-4px);
  border-color: var(--c, var(--accent));
  outline: none;
}
.choice .keynum {
  position: absolute;
  top: 8px;
  right: 8px;
  min-width: 20px;
  height: 22px;
  padding: 0 5px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-bottom-width: 3px;
  border-radius: 6px;
}
.choice .head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-right: 26px;
}
.choice .ic {
  font-size: 26px;
  width: 42px;
  height: 42px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.3);
  flex-shrink: 0;
}
.choice .nm {
  font-weight: 700;
  font-size: 16px;
  color: var(--ink);
}
.choice .tag {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--c, var(--accent));
}
.choice .lv {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--gold);
}
.choice .ds {
  font-size: 13px;
  color: var(--lav);
  line-height: 1.5;
}
/* physical / spell damage-type badge on weapon cards + arsenal rows */
.dtype {
  display: inline-block;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.5px;
  padding: 1px 6px;
  border-radius: 5px;
  white-space: nowrap;
}
.choice .dtype {
  margin-top: 5px;
}
/* damage-type badges use their own crimson/violet palette so they
   don't read as the orange "Active" / blue "Passive" kind tags */
.dtype.phys {
  background: rgba(255, 107, 107, 0.16);
  color: #ff6b6b;
}
.dtype.spell {
  background: rgba(91, 200, 255, 0.16);
  color: #5bc8ff;
}
/* a copy that completes a 3-of-a-kind merge - make it pop in the RESULTING
   tier's colour (--c is set to tierColor(mergeTo) in JS): T2 green, T3 blue,
   T4 gold, so the card always reads as the tier it's upgrading to. */
.choice.merge {
  border-color: color-mix(in srgb, var(--c, #7fe07f) 85%, transparent);
  background: linear-gradient(
    160deg,
    color-mix(in srgb, var(--c, #7fe07f) 30%, #11140f),
    color-mix(in srgb, var(--c, #7fe07f) 14%, #0d100c)
  );
  box-shadow: 0 0 18px color-mix(in srgb, var(--c, #7fe07f) 45%, transparent);
  animation: mergePulse 1.1s ease-in-out infinite;
}
.choice.merge .tag {
  color: var(--c, #b3ffcf);
}
.choice.merge .lv {
  color: var(--c, #b3ffcf);
}
@keyframes mergePulse {
  0%,
  100% {
    box-shadow: 0 0 14px color-mix(in srgb, var(--c, #7fe07f) 35%, transparent);
  }
  50% {
    box-shadow: 0 0 24px color-mix(in srgb, var(--c, #7fe07f) 65%, transparent);
  }
}
/* secondary "skip" action - spans the row, quieter than the picks */
.choice.skip {
  grid-column: 1 / -1;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  padding: 10px 16px;
  background: rgba(var(--tray-rgb), 0.7);
  opacity: 0.8;
}
.choice.skip:hover,
.choice.skip:focus-visible {
  opacity: 1;
  transform: none;
}
.choice.skip .keynum {
  position: static;
  margin-right: 8px;
}
.choice.skip b {
  color: var(--gold);
}

.hint {
  position: absolute;
  bottom: 8px;
  left: 0;
  right: 0;
  text-align: center;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-4);
  pointer-events: none;
  z-index: 4;
}
/* on touch devices the hotbar sits along the bottom and covers this
   desktop keyboard hint, so hide it there - it's not relevant anyway */
@media (pointer: coarse) {
  .hint {
    display: none;
  }
  /* fat-finger friendly dropdowns: a ~44px tall target is the touch minimum */
  .opt-row select {
    padding: 11px 14px;
    font-size: 15px;
    min-height: 44px;
  }
}
/* ---------- menu: bank, characters, shop ---------- */
.menu-panel {
  max-width: 620px;
  text-align: center;
  position: relative; /* anchor for the top-right .menu-controls */
}
/* masthead: logo sits beside the title (vertically centred on it), tagline below */
.menu-head {
  margin-bottom: 16px;
}
.menu-title-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
}
.menu-head h1 {
  margin: 0;
  line-height: 1;
}
.menu-logo {
  width: clamp(56px, 12vw, 80px);
  height: auto;
  flex-shrink: 0;
  filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.7));
}
.menu-head .tagline {
  margin: 8px 0 0;
}
/* language + audio/fullscreen controls, pinned to the panel corner */
.menu-controls {
  position: absolute;
  top: 4px;
  right: 12px;
  display: flex;
  align-items: center;
  gap: 6px;
  z-index: 2;
}
.bank {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  flex-wrap: wrap;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--ink-2);
  margin-bottom: 18px;
}
.bank b {
  color: var(--gold);
  font-size: 15px;
}
.section-t {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--faint, var(--ink-4));
  text-align: left;
  margin: 16px 0 8px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  padding-bottom: 5px;
}
.charsel {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.char {
  pointer-events: auto;
  background: rgba(var(--card-rgb), 0.9);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 12px;
  padding: 12px 8px;
  cursor: pointer;
  text-align: center;
  /* let the grid track shrink to the panel width: without this a wide child (the
     nowrap unlock button on a narrow phone) forces the column wider and the whole
     row overflows the menu panel */
  min-width: 0;
  overflow: hidden;
  transition:
    border-color 0.12s ease,
    transform 0.12s ease;
}
.char:hover {
  transform: translateY(-3px);
}
.char.sel {
  border-color: var(--accent);
  box-shadow:
    0 0 0 1px var(--accent),
    0 0 18px rgba(var(--accent-rgb), 0.35);
}
.char .ci {
  font-size: 26px;
}
.char .cn {
  font-weight: 700;
  font-size: 14px;
  margin-top: 4px;
}
.char .cd {
  font-size: 11px;
  color: var(--ink-3);
  line-height: 1.4;
  margin-top: 4px;
}
.shop {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  gap: 8px;
  margin: 14px 0 18px;
  /* scroll the grid instead of letting a tall list overflow the centred panel
     (which left the top + bottom rows clipped with no way to reach them) */
  max-height: 62vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding-right: 4px; /* a little room so cards don't sit under the scrollbar */
}
.buy {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  background: rgba(var(--card-rgb), 0.9);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  padding: 8px 10px;
  cursor: pointer;
  text-align: left;
  color: var(--ink);
  font-family: var(--display);
  /* card, not a raised action button - drop the inherited button shadow */
  box-shadow: none;
  transition: border-color 0.12s ease;
}
.buy:hover:not(:disabled) {
  border-color: var(--gold);
}
.buy:disabled {
  cursor: default;
  opacity: 0.55;
}
.buy .bi {
  font-size: 20px;
  flex-shrink: 0;
}
/* the middle text column must be allowed to shrink (min-width:0) so a long row
   of level dots or a big cost can't force the card wider and shove the gold
   past the right edge */
.buy .bt {
  flex: 1;
  min-width: 0;
}
.buy .bn {
  font-size: 13px;
  font-weight: 700;
  line-height: 1.1;
}
.buy .bx {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--ink-3);
}
.buy .bc {
  margin-left: auto;
  flex-shrink: 0;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--gold);
  white-space: nowrap;
}
.buy.maxed .bc {
  color: var(--accent-2);
}
.buy .dots {
  font-family: var(--mono);
  font-size: 15px;
  color: var(--accent);
  letter-spacing: 2px;
  line-height: 1.2;
  display: block;
  /* high-max upgrades (15 dots) must wrap inside the card, not force its width */
  white-space: normal;
  word-break: break-all;
  margin-top: 2px;
}
/* Bare Skull owned card: an ON/OFF switch + a row of skull-style chips */
.hood-styles {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin-top: 5px;
}
.hood-style {
  pointer-events: auto;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: var(--ink-3);
  box-shadow: none;
}
.hood-style:hover:not(:disabled) {
  border-color: var(--gold);
}
.hood-style.sel {
  border-color: var(--gold);
  color: var(--gold);
  background: rgba(255, 215, 107, 0.12);
}
.hood-toggle {
  margin-left: auto;
  flex-shrink: 0;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.16);
  color: var(--ink-3);
  box-shadow: none;
}
.hood-toggle:hover:not(:disabled) {
  border-color: var(--gold);
}
.hood-toggle.on {
  color: var(--accent-2);
  border-color: var(--accent-2);
  background: rgba(120, 230, 150, 0.1);
}
.keys2 {
  font-family: var(--mono);
  font-size: 12px;
  color: #8b7fb0;
  margin-top: 14px;
}
/* social / contact links under the main menu actions */
.menu-social {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 16px;
  margin-top: 16px;
  font-family: var(--mono);
  font-size: 12px;
}
.menu-social a {
  color: var(--lav);
  text-decoration: none;
  opacity: 0.85;
}
.menu-social a:hover {
  color: var(--accent);
  opacity: 1;
  text-decoration: underline;
}
.keys2 kbd {
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 5px;
  padding: 1px 6px;
  font-size: 11px;
  color: #fff;
}
.ghost {
  background: transparent;
  color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  padding: 6px 10px;
  font-size: 16px;
  box-shadow: none;
  cursor: pointer;
}
/* a step up from .ghost: a solid, clearly-visible secondary action (Upgrades)
   that sits between the plain ghost buttons and the primary gradient Start */
.secondary {
  background: rgba(var(--accent-rgb), 0.18);
  color: var(--ink);
  border: 1px solid var(--accent);
  border-radius: 10px;
  padding: 11px 24px;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.5px;
  box-shadow: 0 3px 12px rgba(var(--accent-rgb), 0.22);
  cursor: pointer;
}
/* menu nav buttons (Arsenal/Bestiary/Guide/...) are ghost-styled for low
   emphasis, but should match the primary/secondary footprint so the menu reads
   as one consistent row instead of big buttons + tiny links. The compact .ghost
   sizing stays for icon-only buttons (audio/lang) outside .menu-actions. */
.menu-actions .ghost {
  padding: 11px 20px;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.5px;
  border-radius: 10px;
  border-color: rgba(255, 255, 255, 0.28);
}
.menu-actions .ghost:hover {
  background: rgba(255, 255, 255, 0.08);
}
.secondary:hover {
  background: rgba(var(--accent-rgb), 0.3);
  filter: none;
}
/* primary action row: Start leads (grows to fill), Upgrades sits beside it */
.primary-actions {
  gap: 10px;
}
.primary-actions #startBtn {
  flex: 1 1 auto;
  min-width: 160px;
  padding: 13px 24px;
  font-size: 17px;
}
/* utility grid: codex/reference screens as tidy icon-above-label chips, lower
   emphasis than the primary row so the eye lands on Start first */
.menu-nav {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-top: 10px;
}
.menu-nav .ghost {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 9px 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.3px;
  border-radius: 10px;
  border-color: rgba(255, 255, 255, 0.16);
  min-width: 0;
}
.menu-nav .ghost:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.3);
}
/* the grid's display:flex outweighs the UA [hidden]{display:none}, so a hidden
   button (the dev-tools entry when dev mode is off) would still show - keep it
   hidden explicitly */
.menu-nav .ghost[hidden] {
  display: none;
}
.menu-nav .nav-ico {
  font-size: 19px;
  line-height: 1;
}
/* pause panel: anchor for the corner controls, plus the set-apart Quit row */
.pause-panel {
  position: relative;
}
.pause-quit {
  margin-top: 10px;
}
.pause-quit .ghost {
  border-color: rgba(255, 120, 120, 0.4);
  color: #ffb4b4;
}
.pause-quit .ghost:hover {
  background: rgba(255, 80, 80, 0.12);
  border-color: rgba(255, 120, 120, 0.7);
}
#muteBtn {
  pointer-events: auto;
  margin-left: auto;
  background: rgba(var(--tray-rgb), 0.8);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: var(--ink);
  border-radius: 8px;
  padding: 4px 8px;
  font-size: 15px;
  box-shadow: none;
  cursor: pointer;
}

/* ---------- chest reveal ---------- */
.chest-panel {
  position: relative;
  overflow: hidden;
  max-width: 460px;
}
.rays {
  position: absolute;
  left: 50%;
  top: 38%;
  width: 900px;
  height: 900px;
  transform: translate(-50%, -50%);
  background: repeating-conic-gradient(
    from 0deg,
    rgba(255, 226, 122, 0.16) 0deg 9deg,
    transparent 9deg 18deg
  );
  animation: spin 14s linear infinite;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s ease;
}
.chest.show .rays {
  opacity: 1;
}
@keyframes spin {
  to {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}
.chest-ico {
  position: relative;
  font-size: 84px;
  line-height: 1;
  margin-bottom: 6px;
  filter: drop-shadow(0 0 22px rgba(255, 226, 122, 0.6));
  animation: chestShake 0.5s ease-in-out;
  transform-origin: 50% 80%;
}
@keyframes chestShake {
  0%,
  100% {
    transform: rotate(0);
  }
  20% {
    transform: rotate(-7deg) scale(1.04);
  }
  40% {
    transform: rotate(7deg) scale(1.04);
  }
  60% {
    transform: rotate(-5deg);
  }
  80% {
    transform: rotate(5deg);
  }
}
.chest-panel h1 {
  position: relative;
}
.reveal {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin: 14px 0 22px;
  min-height: 90px;
  justify-content: center;
}
.reveal .evo-card {
  animation: pop 0.45s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}
.reveal .evo-ico {
  font-size: 56px;
  filter: drop-shadow(0 0 18px var(--c, var(--accent)));
}
.reveal .evo-tag {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 3px;
  color: var(--accent);
  text-transform: uppercase;
}
.reveal .evo-name {
  font-size: 22px;
  font-weight: 700;
  color: var(--ink);
  margin-top: 4px;
}
.reveal .loot-row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  justify-content: center;
}
.reveal .loot {
  background: rgba(var(--card-rgb), 0.9);
  border: 1px solid rgba(255, 226, 122, 0.35);
  border-radius: 12px;
  padding: 12px 16px;
  font-family: var(--mono);
  font-size: 14px;
  color: var(--ink);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  animation: pop 0.4s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}
.reveal .loot .li {
  font-size: 28px;
}
.reveal .loot.reward {
  border-color: rgba(255, 179, 138, 0.6);
}
.reveal .loot small {
  opacity: 0.6;
  font-size: 11px;
}
.reveal .chest-note {
  margin-top: 12px;
  font-size: 12px;
  color: var(--lav);
  opacity: 0.85;
}
@keyframes pop {
  from {
    transform: scale(0.3);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}

/* ---------- audio / language controls ---------- */
.hudbtns {
  display: flex;
  gap: 8px;
  margin-left: auto;
}
/* music track + its music/SFX toggles, kept tight and set apart from pause/fullscreen */
.hud-audio {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: 16px;
}
/* the now-playing music icon reads larger than the other HUD toggles so it's
   clearly tied to the track name beside it */
#musicBtn {
  font-size: 22px;
  padding: 3px 9px;
}
#musicBtn,
#sfxBtn,
#pauseBtn,
#fsBtn {
  pointer-events: auto;
  background: rgba(var(--tray-rgb), 0.8);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: var(--ink);
  border-radius: 8px;
  padding: 4px 8px;
  font-size: 15px;
  box-shadow: none;
  cursor: pointer;
}
button.off {
  opacity: 0.38;
}
.langsel {
  display: inline-flex;
}
.langsel-dd {
  pointer-events: auto;
  background: rgba(var(--tray-rgb), 0.85);
  color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 7px;
  padding: 4px 10px;
  font-size: 12px;
  font-family: var(--mono);
  cursor: pointer;
}
.langsel-dd:focus-visible {
  outline: none;
  border-color: var(--accent);
}
/* native option list renders in the OS theme; keep text readable on platforms
   that honour these */
.langsel-dd option {
  background: #140c22;
  color: var(--ink);
}
.menu-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 18px;
}
#buffs {
  display: flex;
  gap: 6px;
  margin-top: 6px;
  flex-wrap: wrap;
}
.buff {
  display: flex;
  align-items: center;
  gap: 5px;
  background: rgba(var(--tray-rgb), 0.85);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 8px;
  padding: 3px 8px 4px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink);
}
.buff .bmini {
  position: relative;
  width: 36px;
  height: 4px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.15);
  overflow: hidden;
}
.buff .bmini > i {
  position: absolute;
  inset: 0;
  transform-origin: left;
}
#trackBtn {
  pointer-events: none;
  background: rgba(var(--tray-rgb), 0.75);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: var(--ink);
  border-radius: 8px;
  padding: 4px 10px;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.5px;
  white-space: nowrap;
  box-shadow: none;
}
#trackBtn #trackName {
  color: var(--accent);
}
#trackBtn:disabled {
  opacity: 0.5;
  cursor: default;
}
/* debug / FPS readout: pinned to the bottom-right, out of the topbar flow (so it
   never squeezes the control-button row). Off by default, so overlapping the
   hotbar corner is fine; a faint plate keeps it legible when it's on.
   z-index sits ABOVE the overlays (20/30) so the counter also shows on the
   menu / pause screens - that's where you flip quality tiers to compare. */
.fps {
  position: fixed;
  right: 8px;
  bottom: 8px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--accent-2);
  text-align: right;
  background: rgba(10, 6, 20, 0.55);
  padding: 3px 6px;
  border-radius: 6px;
  z-index: 35;
  pointer-events: none;
}
.opt-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 14px 0;
  font-size: 14px;
  color: var(--ink);
}
/* an option label + its little "i" info bubble, kept together as one flex
   child so the row's space-between doesn't strand the icon in the middle.
   The explanation rides the bubble's title attribute as a hover tooltip
   (data-i18n-title keeps it translated). */
.opt-label {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.opt-info {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.35);
  color: var(--lav);
  font-size: 10px;
  font-style: italic;
  font-family: Georgia, serif;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: help;
  user-select: none;
}
.opt-info:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.opt-row input[type="range"] {
  flex: 1;
  max-width: 220px;
  accent-color: var(--accent);
}
.opt-row input[type="checkbox"] {
  width: 20px;
  height: 20px;
  accent-color: var(--accent);
}
.opt-row input[type="password"] {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  color: var(--ink);
  padding: 6px 10px;
  font-family: var(--mono);
}
/* the option dropdowns (auto-pick, graphics quality). Native selects render tiny
   by default - give them a real tap target (padding + min-height); the coarse-
   pointer block below grows it further for touch. */
.opt-row select {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  color: var(--ink);
  padding: 8px 12px;
  font-size: 14px;
  font-family: inherit;
  min-height: 38px;
  cursor: pointer;
  /* the closed box is styled above, but the native OPEN dropdown list is drawn
     by the OS. color-scheme nudges it dark where honored; the explicit option
     colours below are the actual fix (Chrome/Firefox render a white popup and
     ignore color-scheme, but they DO honour an opaque option background). */
  color-scheme: dark;
}
/* the open dropdown rows - must be an OPAQUE colour (the popup can't composite a
   translucent fill), so use a solid dark rather than a --*-rgb token with alpha. */
.opt-row select option {
  background: #12151c;
  color: var(--ink);
}
/* drop the options Close well below the last row so it reads as the panel's
   footer action rather than crowding the final setting. */
#optsClose {
  margin-top: 26px;
}
/* developer tools block - quietly set off from the player-facing options */
#optDevPanel {
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px dashed rgba(255, 255, 255, 0.18);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.03);
}
.opt-devhint {
  font-size: 12px;
  color: var(--lav);
  margin: 0 0 6px;
  text-align: left;
}
/* dev-tools overlay: a label + a wrapping grid of compact action buttons */
.dev-section {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--lav);
  text-align: left;
  margin: 14px 0 6px;
  opacity: 0.8;
}
.dev-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.dev-grid .ghost {
  padding: 8px 12px;
  font-size: 13px;
  font-weight: 600;
}
/* the Dev tools entry in the pause menu - a faint dashed accent so it reads as
   a hidden/developer affordance, not a normal menu action */
.dev-btn {
  border: 1px dashed rgba(155, 255, 122, 0.5) !important;
  color: #9bff7a !important;
}
.news-body {
  text-align: left;
  font-size: 13.5px;
  line-height: 1.6;
  color: #c9bce4;
  margin: 8px 0 20px;
  max-height: 55vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
.news-body h4 {
  color: var(--accent);
  margin: 12px 0 4px;
  font-size: 14px;
}
.news-body ul {
  margin: 0 0 6px 18px;
}
.char.locked {
  opacity: 0.5;
  cursor: default;
}
/* coin-locked card: subdue the icon + name but keep the unlock button bright
   (parent opacity would dim a child button too, so we never use .locked here) */
.char.coinlock {
  cursor: default;
}
.char.coinlock .ci,
.char.coinlock .cn,
.char.coinlock .mapdot {
  opacity: 0.5;
}
.unlockbtn {
  margin-top: 2px;
  padding: 3px 8px;
  font: inherit;
  font-size: 11px;
  font-weight: 700;
  color: var(--bg-0, #0c0a18);
  background: var(--gold, #ffd76b);
  border: none;
  border-radius: 6px;
  cursor: pointer;
  /* wrap rather than force the card wider on a narrow phone (was nowrap, which
     overflowed the char-select grid). Stay within the card and wrap if needed. */
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
}
.unlockbtn:hover {
  filter: brightness(1.1);
}
.unlockbtn[disabled] {
  background: rgba(255, 215, 107, 0.22);
  color: var(--ink-3, #8a86a8);
  cursor: default;
}
/* "Not available in DEMO" note on a teaser card (locked survivor/world) */
.char .demo-lock {
  color: var(--gold);
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}
/* "Not available in DEMO" banner over the (disabled) upgrades shop */
.shop-demo-note {
  grid-column: 1 / -1;
  margin: 0 0 4px;
  text-align: center;
  color: var(--gold);
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}
.char.map .ci {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 30px;
}
.mapdot {
  display: inline-block;
  width: 22px;
  height: 22px;
  border-radius: 6px;
}

/* ---------- on-screen joystick ---------- */
#stick {
  position: fixed;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  display: none;
}
#stick.on {
  display: block;
}
#stickBase {
  position: absolute;
  width: 120px;
  height: 120px;
  margin: -60px 0 0 -60px;
  border-radius: 50%;
  background: rgba(var(--accent-rgb), 0.12);
  border: 2px solid rgba(var(--accent-rgb), 0.45);
}
#stickKnob {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 54px;
  height: 54px;
  margin: -27px 0 0 -27px;
  border-radius: 50%;
  background: rgba(205, 214, 255, 0.5);
  border: 2px solid rgba(205, 214, 255, 0.85);
  box-shadow: 0 0 16px rgba(var(--accent-rgb), 0.6);
}

/* ---------- evolution reference ---------- */
/* tall centred panels: cap to the viewport and scroll INSIDE, so a long body
   (e.g. Options with the dev panel revealed) can't push its Close button off
   the bottom edge where the centred flexbox clips it with no way to reach it. */
.menu-panel,
.evo-panel,
#opts .panel {
  max-height: 92vh;
  max-height: 92dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
.evo-panel {
  max-width: 640px;
}
.evo-intro {
  font-size: 13px;
  color: var(--ink-2);
  margin-bottom: 16px;
}
.evo-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 20px;
}
.evo-subhead {
  margin: 14px 0 2px;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-passive);
  opacity: 0.9;
}
.evo-row {
  display: flex;
  align-items: center;
  gap: 8px;
  background: rgba(var(--card-rgb), 0.9);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 10px;
  padding: 8px 10px;
}
.evo-part {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  min-width: 0;
  text-align: left;
}
.evo-part .ei {
  font-size: 22px;
  flex-shrink: 0;
}
.evo-part .et {
  font-size: 12px;
  line-height: 1.2;
}
.evo-part .et small {
  color: var(--ink-3);
}
.evo-part.res {
  color: var(--c, var(--accent));
  font-weight: 700;
}
.evo-op {
  color: #8b7fb0;
  font-family: var(--mono);
  flex-shrink: 0;
}
.evo-badge.ready {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 1px;
  color: #1a0b2b;
  background: var(--accent-2);
  border-radius: 5px;
  padding: 2px 6px;
  flex-shrink: 0;
}

/* ---------- mobile ---------- */
@media (max-width: 560px) {
  .clock {
    font-size: 24px;
    min-width: 70px;
  }
  /* on phones the corner controls would crowd the title, so flow them normally
     as a centered row above the masthead instead of absolute-positioning them */
  .menu-controls {
    position: static;
    justify-content: center;
    margin-bottom: 10px;
  }
  .menu-head {
    gap: 12px;
  }
  /* PHONE HUD LAYOUT: the topbar holds the timer + stat chips (left). The music,
     SFX and fullscreen toggles move into the PAUSE menu, so the only always-on
     control on the play field is the pause button - pinned just below the minimap
     on the right edge. Less chrome over the game. */
  #topbar {
    gap: 8px;
    /* keep the timer + stat chips clear of the top-right minimap */
    padding: 8px var(--mmw, 140px) 8px 10px;
  }
  .hudbtns {
    position: fixed;
    right: 14px; /* right edge lines up with the minimap (which sits at margin 14) */
    top: var(--mmh, 190px);
    margin: 0;
    z-index: 8;
  }
  #fsBtn,
  .hud-audio {
    display: none;
  }
  .hudbtns button {
    width: 36px;
    height: 36px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .fps {
    font-size: 10px;
  }
  .stat {
    font-size: 11px;
  }
  /* LEVEL-UP on phones: anchor the panel to the TOP and let it run ALMOST full
     height. We only reserve a slim bottom band, so the hotbar (lifted above this
     modal during a level-up) overlaps the very bottom of the panel a little -
     that's fine; the choices live up top and the panel scrolls if needed. */
  #levelup {
    align-items: flex-start;
  }
  #levelup .panel {
    /* ride right up near the top edge / notch; max-height grows to match so the
       panel's bottom edge (and the hotbar band below it) stays put */
    margin-top: 10px;
    max-height: calc(100vh - 92px);
    max-height: calc(100dvh - 92px);
    /* a little breathing room at the bottom so the last card isn't flush against
       the slice of hotbar that overlaps it */
    padding-bottom: 26px;
  }
  /* the HUD is lifted above the level-up modal so the hotbar stays usable for
     weapon swaps - but that also put the frozen timer and the XP bar ON TOP of
     the panel. Hide those two so the panel can sit above them and use the top
     space; the hotbar (#inv) stays visible. */
  body.lu-open #topbar,
  body.lu-open #bars {
    visibility: hidden;
  }
  /* COMPACT level-up cards on phones so all three fit with little/no scrolling:
     tighter header, smaller icon, snugger padding + line-height. (!important to
     beat the inline font-size/margin on the heading + subtitle.) */
  #levelup h1 {
    font-size: 23px !important;
    margin-bottom: 2px;
  }
  #levelup #luSub {
    font-size: 13px;
    margin-bottom: 8px !important;
  }
  .choices {
    gap: 8px;
    margin-top: 4px;
  }
  .choice {
    padding: 10px 12px;
    gap: 4px;
    border-radius: 12px;
  }
  .choice .ic {
    width: 34px;
    height: 34px;
    font-size: 21px;
    border-radius: 9px;
  }
  .choice .head {
    gap: 8px;
  }
  .choice .nm {
    font-size: 15px;
  }
  .choice .ds {
    font-size: 12px;
    line-height: 1.35;
  }
  /* the "Skip - keep slots free" row is a thin single line, so the compact card
     padding left it too short to tap reliably. Give it a real touch height. */
  .choice.skip {
    padding: 14px 16px;
    min-height: 50px;
    opacity: 0.9;
  }
  .panel {
    padding: 20px 16px;
  }
  .evo-row {
    flex-wrap: wrap;
  }
  .evo-part {
    flex-basis: 40%;
  }
  /* compact hotbar so 8+8 slots don't eat the screen on phones */
  #inv {
    padding: 6px 8px 8px;
    gap: 8px;
  }
  .invgroup {
    padding: 15px 5px 5px;
    gap: 4px;
    border-radius: 9px;
  }
  .invgroup::before {
    font-size: 7px;
    top: 4px;
    left: 6px;
  }
  .slot {
    width: 27px;
    height: 27px;
    border-radius: 7px;
    font-size: 14px;
  }
  .slot .pips {
    font-size: 7px;
    bottom: 1px;
    right: 2px;
  }
  .slot .dropx {
    width: 13px;
    height: 13px;
    line-height: 13px;
    font-size: 8px;
    top: -4px;
    right: -4px;
  }
  #invweapons .slot.wslot,
  #invweapons .slot.empty {
    height: 31px;
  }
  .slot .tbadge {
    top: 1px;
    left: 2px;
    font-size: 8px;
  }
  .slot .wbar {
    left: 3px;
    right: 3px;
    bottom: 2px;
    height: 4px;
    gap: 2px;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
  }
}

/* feedback / bug-report form */
.feedback-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 12px;
  text-align: left;
}
.feedback-form select,
.feedback-form textarea,
.feedback-form input {
  width: 100%;
  box-sizing: border-box;
  background: rgba(0, 0, 0, 0.35);
  color: var(--ink);
  border: 1px solid var(--ink-4);
  border-radius: 8px;
  padding: 10px 12px;
  font: inherit;
  font-size: 14px;
}
.feedback-form textarea {
  resize: vertical;
  min-height: 90px;
}
.feedback-form select:focus,
.feedback-form textarea:focus,
.feedback-form input:focus {
  outline: none;
  border-color: var(--accent);
}
.feedback-form .menu-actions {
  margin-top: 4px;
}
#fbStatus {
  min-height: 1em;
  margin: 4px 0 0;
}

/* ---------- menu polish ----------
   Atmosphere + focus cues for the main menu. All accent-driven (so they follow
   the active theme) and all animation-based, so the global reduced-motion rule
   at the bottom quietly disables the motion for users who ask for it. */

/* keep the panel above the ambient haze layer below */
.menu-panel {
  z-index: 1;
}
/* clip the menu to the viewport: the roaming treasure-goblin sprite and its FX
   canvas are absolute children here, and on mobile a descendant that reaches the
   screen edge can grow the layout viewport (body overflow:hidden isn't enough
   there) - which dragged the fixed bottom-right FPS readout outward and spawned
   a phantom scroll until the goblin despawned. */
#start {
  overflow: hidden;
}
/* ambient drifting haze behind the main-menu panel - very faint, accent-tinted,
   sits under the panel and never eats clicks. #start is display:none when
   hidden, so the pseudo only lives while the menu is up. */
#start::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(
      38% 42% at 22% 28%,
      rgba(var(--accent-rgb), 0.1),
      transparent 70%
    ),
    radial-gradient(
      42% 46% at 80% 72%,
      rgba(var(--accent-rgb), 0.08),
      transparent 72%
    );
  filter: blur(8px);
  animation: menuHaze 26s ease-in-out infinite alternate;
}
@keyframes menuHaze {
  from {
    transform: translate3d(-3%, -2%, 0) scale(1.05);
    opacity: 0.65;
  }
  to {
    transform: translate3d(3%, 2%, 0) scale(1.16);
    opacity: 1;
  }
}

/* the primary Start action gently breathes so the eye lands on it before
   Upgrades / the nav chips */
#startBtn {
  animation: startPulse 2.6s ease-in-out infinite;
}
@keyframes startPulse {
  0%,
  100% {
    box-shadow: 0 6px 20px rgba(var(--accent-rgb), 0.4);
  }
  50% {
    box-shadow:
      0 6px 20px rgba(var(--accent-rgb), 0.4),
      0 0 22px rgba(var(--accent-rgb), 0.55);
  }
}

/* menus + dialogs fade/scale in when shown (skip the frequent level-up so it
   never feels laggy) */
.overlay:not(#levelup).show .panel {
  animation: panelIn 0.26s cubic-bezier(0.2, 1, 0.4, 1) both;
}
@keyframes panelIn {
  from {
    opacity: 0;
    transform: translateY(8px) scale(0.97);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* nav chips lift + take an accent edge on hover (matches the char-card feel).
   Later in the file than the base .menu-nav rule, so this hover wins. */
.menu-nav .ghost {
  transition:
    transform 0.12s ease,
    border-color 0.12s ease,
    background 0.12s ease;
}
.menu-nav .ghost:hover {
  transform: translateY(-2px);
  border-color: rgba(var(--accent-rgb), 0.6);
  background: rgba(var(--accent-rgb), 0.08);
}

/* section labels get a themed leading tick + a faint accent underline instead
   of the plain grey rule */
.section-t {
  display: flex;
  align-items: center;
  gap: 8px;
  border-bottom-color: rgba(var(--accent-rgb), 0.18);
}
.section-t::before {
  content: "";
  width: 10px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
  flex: 0 0 auto;
}

/* a few slow-rising motes over the haze (behind the panel). Reduced-motion
   halts the rise via the global rule; the static dots stay as faint texture. */
#start::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-repeat: no-repeat;
  background-image:
    radial-gradient(2px 2px at 15% 82%, rgba(var(--accent-rgb), 0.5), transparent 60%),
    radial-gradient(1.5px 1.5px at 32% 66%, rgba(var(--accent-rgb), 0.32), transparent 60%),
    radial-gradient(2px 2px at 54% 88%, rgba(var(--accent-rgb), 0.42), transparent 60%),
    radial-gradient(1.5px 1.5px at 71% 72%, rgba(var(--accent-rgb), 0.3), transparent 60%),
    radial-gradient(2px 2px at 87% 84%, rgba(var(--accent-rgb), 0.4), transparent 60%);
  animation: emberRise 12s ease-in-out infinite alternate;
}
@keyframes emberRise {
  from {
    transform: translateY(6px);
    opacity: 0.5;
  }
  to {
    transform: translateY(-14px);
    opacity: 1;
  }
}

/* ---------- quality tiers (body.q-medium / body.q-low, set by applyQuality)
   The menu's blur effects are the whole menu frame cost on a weak GPU: the
   infinite haze/ember/button animations keep the overlay stack compositing
   every frame, and each composite re-runs the full-screen backdrop-filter
   blur AND the screen-size blur(8px) haze layer - which is how the MENU could
   run slower than the game itself. Medium keeps the moving haze and embers
   (transform/opacity only - cheap to composite) but drops both blurs; low
   drops the animated layers entirely. */
body.q-medium .overlay,
body.q-low .overlay {
  backdrop-filter: none;
}
body.q-medium #start::before {
  filter: none;
}
body.q-low #start::before,
body.q-low #start::after {
  display: none;
}
body.q-low #startBtn {
  animation: none; /* the box-shadow pulse repaints its blur region each frame */
  box-shadow: 0 6px 20px rgba(var(--accent-rgb), 0.4);
}

/* EASTER EGG: a treasure goblin (the game's real drawTreasureGoblin sprite,
   painted onto a small canvas) leaps out of a portal, wanders a patch of the
   menu backdrop and bolts from the mouse cursor. The outer .goblin is positioned
   by JS (maybeSpawnGoblin) each frame; CSS handles the spawn hop, despawn fade
   and the caught poof. Click it to catch it. */
.goblin {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0; /* behind the panel (z-index:1), over the haze */
  cursor: pointer;
  pointer-events: auto; /* clickable even though it's a backdrop layer */
  will-change: transform;
  transition: opacity 0.6s ease; /* despawn fade (transform is set inline, no lag) */
}
.gob-inner {
  display: block;
}
.gob-canvas {
  display: block;
}
/* spawn: hop up out of the portal + scale in, once */
.gob-inner.spawn {
  animation: goblinHop 0.55s cubic-bezier(0.2, 1.35, 0.4, 1) both;
}
@keyframes goblinHop {
  0% {
    transform: translateY(10px) scale(0.2);
    opacity: 0;
  }
  45% {
    transform: translateY(-20px) scale(1.05);
    opacity: 1;
  }
  100% {
    transform: translateY(0) scale(1);
    opacity: 1;
  }
}
/* caught: poof away (on the inner element so the outer keeps its position) */
.gob-inner.caught {
  animation: goblinPoof 0.45s ease forwards;
}
@keyframes goblinPoof {
  to {
    transform: scale(1.7) rotate(14deg);
    opacity: 0;
  }
}
/* full-menu canvas the goblin's dropped coins + spawn portal are painted on
   (same drawCoin / drawPortalSwirl as in-game). Behind the panel, never eats
   clicks. */
.gob-fx {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
/* on phones the panel fills the viewport, so the goblin (and its dropped coins
   + spawn portal) roam ABOVE the panel instead of in the thin backdrop strip
   behind it - otherwise there's no visible "area around the menu" for it. */
.goblin.over {
  z-index: 2;
}
.gob-fx.over {
  z-index: 2;
}
