/* ==========================================================================
   Cannon Strike — chrome around the canvas.

   The game itself is drawn entirely on <canvas>; this stylesheet only handles
   the HUD, the coach line, the integrity meter and the end-of-level card.
   Everything is sized in rem/vmin so the same layout works on an iPad and on
   a desktop browser without a media-query maze.
   ========================================================================== */

:root {
  /* Retro-arcade palette. Shared with src/render.js — keep the two in sync. */
  --ink:        #17121F;
  --night:      #1A1030;
  --night-2:    #2B1B4B;
  --plum:       #6B2D63;
  --ember:      #E0623B;
  --gold:       #FFC63F;
  --cyan:       #46E0D2;
  --paper:      #FFF3E0;

  --ui-font: "Verdana", "DejaVu Sans", "Trebuchet MS", system-ui, sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--night);
  color: var(--paper);
  font-family: var(--ui-font);
  /* A game played by dragging should never select text or bounce-scroll. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
  touch-action: none;
}

.stage {
  position: relative;
  width: 100%;
  height: 100%;
}

.stage__canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

/* ------------------------------------------------------------------ HUD -- */

.hud {
  position: absolute;
  inset: 0 0 auto 0;
  display: flex;
  align-items: flex-start;
  gap: clamp(0.6rem, 2vw, 1.6rem);
  padding: clamp(0.6rem, 1.8vmin, 1.2rem) clamp(0.8rem, 2.4vmin, 1.6rem);
  padding-top: max(clamp(0.6rem, 1.8vmin, 1.2rem), env(safe-area-inset-top));
  pointer-events: none;
}

.hud__block {
  background: rgba(23, 18, 31, 0.62);
  border: 3px solid var(--ink);
  border-radius: 12px;
  padding: 0.35rem 0.7rem 0.45rem;
  box-shadow: 0 4px 0 rgba(23, 18, 31, 0.5);
}

.hud__block--wallet .hud__value--scrap {
  color: var(--cyan);
}

.hud__block--right {
  margin-left: auto;
  display: flex;
  gap: 0.5rem;
  background: none;
  border: none;
  box-shadow: none;
  padding: 0;
  pointer-events: auto;
}

.hud__label {
  margin: 0;
  font-size: clamp(0.52rem, 1.25vmin, 0.7rem);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.65;
}

.hud__value {
  margin: 0;
  font-size: clamp(1rem, 2.6vmin, 1.6rem);
  font-weight: 700;
  line-height: 1.1;
  color: var(--gold);
}

.hud__name {
  margin: 0;
  font-size: clamp(0.6rem, 1.4vmin, 0.85rem);
  letter-spacing: 0.06em;
  opacity: 0.8;
}

/* Ammo pips: one chunky shell per shot, dimmed as they are spent. */
.ammo {
  display: flex;
  gap: 0.3rem;
  padding-top: 0.28rem;
  min-height: 1.4rem;
}

.ammo__pip {
  width: clamp(0.62rem, 1.5vmin, 0.95rem);
  height: clamp(0.62rem, 1.5vmin, 0.95rem);
  border-radius: 50%;
  background: var(--gold);
  border: 2px solid var(--ink);
  transition: background-color 140ms ease, transform 140ms ease;
}

.ammo__pip.is-spent {
  background: rgba(255, 243, 224, 0.16);
  transform: scale(0.72);
}

/* --------------------------------------------------------------- buttons -- */

.btn {
  font-family: inherit;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper);
  background: rgba(23, 18, 31, 0.72);
  border: 3px solid var(--ink);
  border-radius: 12px;
  box-shadow: 0 4px 0 rgba(23, 18, 31, 0.55);
  cursor: pointer;
  transition: transform 90ms ease, background-color 120ms ease;
}

.btn:active { transform: translateY(3px); box-shadow: 0 1px 0 rgba(23, 18, 31, 0.55); }

.btn--icon {
  width: clamp(2.1rem, 5.2vmin, 2.9rem);
  height: clamp(2.1rem, 5.2vmin, 2.9rem);
  font-size: clamp(1rem, 2.4vmin, 1.4rem);
  line-height: 1;
  padding: 0;
}

.btn--icon.is-off { opacity: 0.45; }

.btn--primary {
  background: var(--gold);
  color: var(--ink);
  padding: 0.7rem 1.8rem;
  font-size: clamp(0.85rem, 2vmin, 1.05rem);
}

.btn--ghost {
  padding: 0.7rem 1.4rem;
  font-size: clamp(0.85rem, 2vmin, 1.05rem);
}

/* ----------------------------------------------------------- coach line -- */

.coach {
  position: absolute;
  left: 50%;
  /* Clears the ammo selector, which sits below it once a second type is
     unlocked. They overlapped at the original 2.6rem. */
  bottom: clamp(3.2rem, 8vmin, 4.4rem);
  transform: translateX(-50%);
  margin: 0;
  padding: 0.5rem 1.1rem;
  background: rgba(23, 18, 31, 0.72);
  border: 3px solid var(--ink);
  border-radius: 999px;
  font-size: clamp(0.72rem, 1.8vmin, 1rem);
  font-weight: 700;
  letter-spacing: 0.05em;
  pointer-events: none;
  transition: opacity 400ms ease;
}

.coach.is-hidden { opacity: 0; }

/* -------------------------------------------------------- ammo selector -- */

.ammobar {
  position: absolute;
  left: 50%;
  bottom: clamp(0.5rem, 1.6vmin, 1rem);
  transform: translateX(-50%);
  display: flex;
  gap: 0.35rem;
  max-width: min(94vw, 44rem);
  flex-wrap: wrap;
  justify-content: center;
}

.ammobar[hidden] { display: none; }

.ammochip {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.35rem 0.6rem;
  font-family: inherit;
  font-size: clamp(0.6rem, 1.4vmin, 0.78rem);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255, 243, 224, 0.72);
  background: rgba(23, 18, 31, 0.72);
  border: 3px solid var(--ink);
  border-radius: 10px;
  box-shadow: 0 3px 0 rgba(23, 18, 31, 0.5);
  cursor: pointer;
  transition: transform 90ms ease, background-color 130ms ease, color 130ms ease;
}

.ammochip:active { transform: translateY(2px); }

.ammochip.is-active {
  background: var(--gold);
  color: var(--ink);
  box-shadow: 0 3px 0 rgba(23, 18, 31, 0.7);
}

.ammochip__slot {
  display: grid;
  place-items: center;
  min-width: 1.05rem;
  height: 1.05rem;
  font-size: 0.85em;
  border-radius: 4px;
  background: rgba(255, 243, 224, 0.16);
}

.ammochip.is-active .ammochip__slot { background: rgba(23, 18, 31, 0.22); }

/* The cluster's mid-air prompt. Pulses so it cannot be mistaken for chrome. */
.splitcue {
  position: absolute;
  left: 50%;
  top: 42%;
  transform: translateX(-50%);
  margin: 0;
  padding: 0.45rem 1rem;
  background: var(--cyan);
  color: var(--ink);
  border: 3px solid var(--ink);
  border-radius: 999px;
  font-size: clamp(0.75rem, 1.9vmin, 1.05rem);
  font-weight: 700;
  letter-spacing: 0.06em;
  pointer-events: none;
  animation: cue-pulse 620ms ease-in-out infinite;
}

.splitcue[hidden] { display: none; }

@keyframes cue-pulse {
  0%, 100% { transform: translateX(-50%) scale(1); }
  50% { transform: translateX(-50%) scale(1.09); }
}

/* -------------------------------------------------------- defend mode -- */

/* Ammo pips and par mean nothing while defending. */
body.is-defending .hud__block--shots { display: none; }

.buildbar,
.abilitybar {
  position: absolute;
  left: 50%;
  bottom: clamp(0.5rem, 1.8vmin, 1.1rem);
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 0.45rem;
  max-width: min(97vw, 54rem);
  /* Scrolls rather than wrapping: on a narrow screen a wrapped bar became a
     three-row slab that covered the build grid it is meant to serve. */
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  justify-content: flex-start;
  padding: 0.45rem 0.6rem;
  background: rgba(23, 18, 31, 0.72);
  border: 3px solid var(--ink);
  border-radius: 16px;
}

.buildbar[hidden],
.abilitybar[hidden] { display: none; }

.buildbar__budget { text-align: center; padding-right: 0.4rem; }

.buildbar__value {
  margin: 0;
  font-size: clamp(0.8rem, 1.9vmin, 1.05rem);
  font-weight: 700;
  color: var(--cyan);
}

.buildbar__value .is-empty { color: var(--ember); }

.buildbar__pieces { display: flex; gap: 0.35rem; flex-wrap: nowrap; }
.buildbar::-webkit-scrollbar, .abilitybar::-webkit-scrollbar { display: none; }
.buildbar > *, .abilitybar > * { flex: 0 0 auto; }

.piecechip {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.32rem 0.5rem;
  font-family: inherit;
  font-size: clamp(0.58rem, 1.35vmin, 0.75rem);
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: rgba(255, 243, 224, 0.75);
  background: rgba(23, 18, 31, 0.6);
  border: 3px solid var(--ink);
  border-radius: 10px;
  cursor: pointer;
}

.piecechip.is-active { background: var(--gold); color: var(--ink); }

.piecechip__swatch {
  width: 0.85rem;
  height: 0.85rem;
  border: 2px solid var(--ink);
  border-radius: 3px;
}

/* Swatches mirror MATERIAL_SKIN in render.js. */
.piecechip__swatch[data-material="wood"] { background: #C98A4B; }
.piecechip__swatch[data-material="brick"] { background: #C4553D; }
.piecechip__swatch[data-material="steel"] { background: #8FA0AE; }
.piecechip__swatch[data-material="reinforced"] { background: #6D6A86; }

.piecechip__cost {
  padding: 0 0.28rem;
  border-radius: 4px;
  background: rgba(255, 243, 224, 0.16);
}

.piecechip.is-active .piecechip__cost { background: rgba(23, 18, 31, 0.2); }

.btn--start { padding: 0.5rem 1.1rem; font-size: clamp(0.7rem, 1.7vmin, 0.9rem); }

/* --- siege abilities */

.ability {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
  min-width: 5.2rem;
  padding: 0.4rem 0.6rem;
  font-family: inherit;
  color: var(--paper);
  background: rgba(23, 18, 31, 0.6);
  border: 3px solid var(--ink);
  border-radius: 12px;
  cursor: pointer;
  overflow: hidden;
  transition: background-color 130ms ease, transform 90ms ease;
}

.ability:active { transform: translateY(2px); }

.ability__name {
  font-size: clamp(0.66rem, 1.55vmin, 0.85rem);
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.ability__meta {
  font-size: clamp(0.54rem, 1.3vmin, 0.68rem);
  opacity: 0.75;
}

.ability.is-ready { background: rgba(70, 224, 210, 0.22); }
.ability.is-ready .ability__name { color: var(--cyan); }

.ability.is-cooling { opacity: 0.55; }
.ability.is-cooling .ability__name { color: rgba(255, 243, 224, 0.7); }

/* Intercept is driven by tapping the shell itself, not this button. */
.ability:disabled { cursor: default; }

.abilityprompt {
  position: absolute;
  left: 50%;
  top: 38%;
  transform: translateX(-50%);
  margin: 0;
  padding: 0.45rem 1rem;
  background: var(--cyan);
  color: var(--ink);
  border: 3px solid var(--ink);
  border-radius: 999px;
  font-size: clamp(0.72rem, 1.8vmin, 1rem);
  font-weight: 700;
  pointer-events: none;
  animation: cue-pulse 620ms ease-in-out infinite;
}

.abilityprompt[hidden] { display: none; }

/* --------------------------------------------------- fortress editor -- */

/* The editor has no shots, no par and no fortress meter to speak of. */
body.is-editing .hud__block--shots,
body.is-editing .hud__block--par,
body.is-editing .integrity { display: none; }

.editorbar {
  position: absolute;
  left: 50%;
  bottom: clamp(0.4rem, 1.4vmin, 0.9rem);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  max-width: min(97vw, 56rem);
  padding: 0.45rem 0.7rem 0.35rem;
  background: rgba(23, 18, 31, 0.78);
  border: 3px solid var(--ink);
  border-radius: 16px;
}

.editorbar[hidden] { display: none; }

.editorbar__row {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
}

.editorbar__row::-webkit-scrollbar { display: none; }
.editorbar__row > * { flex: 0 0 auto; }

.editorbar__pieces { display: flex; gap: 0.3rem; }

.editorbar__msg {
  margin: 0;
  font-size: clamp(0.55rem, 1.3vmin, 0.7rem);
  letter-spacing: 0.04em;
  opacity: 0.8;
  text-align: center;
}

/* Role swatches override the material colour, matching the canvas art. */
.piecechip__swatch[data-role="core"] { background: #FFD23F; }
.piecechip__swatch[data-role="fuel"] { background: #E8632A; }
.piecechip__swatch[data-role="support"] {
  background: repeating-linear-gradient(45deg, #C98A4B 0 4px, #17121F 4px 7px);
}

.btn--mini {
  padding: 0.4rem 0.65rem;
  font-size: clamp(0.58rem, 1.35vmin, 0.75rem);
}

.slots { display: flex; gap: 0.25rem; }

.slot {
  width: clamp(1.5rem, 3.6vmin, 2rem);
  height: clamp(1.5rem, 3.6vmin, 2rem);
  font-family: inherit;
  font-size: clamp(0.6rem, 1.4vmin, 0.78rem);
  font-weight: 700;
  color: rgba(255, 243, 224, 0.6);
  background: rgba(23, 18, 31, 0.6);
  border: 3px solid var(--ink);
  border-radius: 8px;
  cursor: pointer;
}

/* Filled slots read as "there is something here"; active as "Save goes here". */
.slot.is-filled { color: var(--cyan); border-color: var(--cyan); }
.slot.is-active { background: var(--gold); color: var(--ink); border-color: var(--ink); }

/* --------------------------------------------------------- share dialog -- */

.field { display: block; margin: 0 0 0.6rem; }

.field__label {
  display: block;
  margin-bottom: 0.25rem;
  font-size: clamp(0.55rem, 1.3vmin, 0.7rem);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.7;
}

.field__input {
  width: 100%;
  padding: 0.55rem 0.7rem;
  font-family: "SFMono-Regular", "Consolas", "Liberation Mono", monospace;
  font-size: clamp(0.7rem, 1.6vmin, 0.85rem);
  color: var(--paper);
  background: rgba(23, 18, 31, 0.65);
  border: 3px solid var(--ink);
  border-radius: 10px;
  /* Codes are pasted and copied, so this is the one place text selection is
     not only allowed but required. */
  -webkit-user-select: text;
  user-select: text;
}

.field__input:focus { outline: 3px solid var(--cyan); outline-offset: 1px; }

/* ------------------------------------------------------- wind + dodge -- */

.hud__block--wind[hidden] { display: none; }
.hud__value--wind { color: var(--cyan); font-size: clamp(1.1rem, 2.8vmin, 1.7rem); }

.dodgebar {
  position: absolute;
  /* Bottom-right, above the fortress meter. The bottom-left is where the
     cannon itself stands, and the buttons were sitting on top of it — which
     also put them exactly where a dragging finger already is. */
  right: clamp(0.8rem, 2.4vmin, 1.6rem);
  bottom: clamp(4.2rem, 11vmin, 6rem);
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.5rem;
  background: rgba(23, 18, 31, 0.7);
  border: 3px solid var(--ink);
  border-radius: 14px;
}

.dodgebar[hidden] { display: none; }

.dodgebar__label {
  font-size: clamp(0.5rem, 1.2vmin, 0.65rem);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.6;
}

/* Press-and-hold, so they are sized for a thumb rather than a cursor. */
.btn--dodge {
  width: clamp(2.4rem, 6vmin, 3.2rem);
  height: clamp(2.4rem, 6vmin, 3.2rem);
  font-size: clamp(0.9rem, 2.2vmin, 1.2rem);
  padding: 0;
  background: rgba(70, 224, 210, 0.2);
  touch-action: none;
}

.btn--dodge:active { background: var(--cyan); color: var(--ink); }

/* A turret hit costs a shell; the pips flinch so the loss is not silent. */
.ammo.is-hit { animation: ammo-hit 420ms ease; }

@keyframes ammo-hit {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  45% { transform: translateX(5px); }
  70% { transform: translateX(-3px); }
}

/* ----------------------------------------------------- integrity meter -- */

.integrity {
  position: absolute;
  right: clamp(0.8rem, 2.4vmin, 1.6rem);
  bottom: clamp(1rem, 3.4vmin, 2rem);
  width: clamp(7rem, 22vmin, 12rem);
  pointer-events: none;
}

.integrity__track {
  position: relative;
  height: clamp(0.7rem, 1.8vmin, 1.05rem);
  background: rgba(23, 18, 31, 0.72);
  border: 3px solid var(--ink);
  border-radius: 999px;
  overflow: hidden;
}

.integrity__fill {
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, var(--ember), var(--gold));
  transform-origin: left center;
  transition: width 260ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* Marks the rubble threshold — cross it and the level is won. */
.integrity__goal {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--cyan);
}

.integrity__label {
  margin: 0.25rem 0 0;
  text-align: right;
  font-size: clamp(0.52rem, 1.2vmin, 0.68rem);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.6;
}

/* ------------------------------------------------------------- overlay -- */

.overlay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(16, 10, 28, 0.68);
  backdrop-filter: blur(3px);
  animation: fade-in 220ms ease both;
}

.overlay[hidden] { display: none; }

.card {
  width: min(24rem, 86vw);
  text-align: center;
  padding: clamp(1.2rem, 4vmin, 2rem);
  background: var(--night-2);
  border: 4px solid var(--ink);
  border-radius: 20px;
  box-shadow: 0 10px 0 rgba(23, 18, 31, 0.6);
  animation: pop-in 320ms cubic-bezier(0.18, 1.4, 0.4, 1) both;
}

.card__kicker {
  margin: 0 0 0.2rem;
  font-size: clamp(0.6rem, 1.5vmin, 0.8rem);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--cyan);
}

.card__title {
  margin: 0 0 0.6rem;
  font-size: clamp(1.3rem, 3.4vmin, 2rem);
  letter-spacing: 0.04em;
}

.card__line {
  margin: 0 0 1.2rem;
  font-size: clamp(0.75rem, 1.8vmin, 0.95rem);
  opacity: 0.8;
}

.card__body {
  font-size: 0.9rem;
  line-height: 1.6;
}

.card__body code {
  background: rgba(0, 0, 0, 0.35);
  padding: 0.1rem 0.35rem;
  border-radius: 5px;
}

.card__actions {
  display: flex;
  gap: 0.7rem;
  justify-content: center;
}

/* Stars land one at a time — the small delay is what makes 3 stars feel
   earned rather than merely reported. */
.stars {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin: 0.4rem 0 0.8rem;
  font-size: clamp(2rem, 6vmin, 3.2rem);
  line-height: 1;
}

.star {
  color: rgba(255, 243, 224, 0.15);
  text-shadow: 0 3px 0 rgba(23, 18, 31, 0.6);
}

.star.is-earned {
  color: var(--gold);
  animation: star-pop 380ms cubic-bezier(0.18, 1.6, 0.4, 1) both;
}

.star.is-earned[data-i="1"] { animation-delay: 180ms; }
.star.is-earned[data-i="2"] { animation-delay: 360ms; }

/* -------------------------------------------------------- payout block -- */

.payout {
  margin: 0 0 1.1rem;
  padding: 0.7rem 0.9rem 0.6rem;
  background: rgba(23, 18, 31, 0.45);
  border: 3px solid var(--ink);
  border-radius: 12px;
  text-align: left;
}

.payout__rows {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.18rem 0.8rem;
  margin: 0 0 0.45rem;
  font-size: clamp(0.65rem, 1.5vmin, 0.82rem);
}

.payout__rows dt { opacity: 0.75; }

.payout__rows dd {
  margin: 0;
  text-align: right;
  font-weight: 700;
  color: var(--cyan);
}

.payout__rows dd.is-note {
  color: var(--ember);
  font-weight: 400;
  opacity: 0.85;
}

.payout__total {
  margin: 0;
  padding-top: 0.4rem;
  border-top: 2px solid rgba(255, 243, 224, 0.16);
  text-align: right;
  font-size: clamp(1rem, 2.4vmin, 1.35rem);
  font-weight: 700;
  color: var(--cyan);
}

.payout__unit {
  font-size: 0.6em;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0.7;
}

.payout__gem {
  margin: 0.35rem 0 0;
  text-align: right;
  font-size: clamp(0.6rem, 1.4vmin, 0.75rem);
  color: var(--gold);
}

.payout__gem[hidden] { display: none; }

/* ------------------------------------------------------- screen overlays -- */

.overlay--screen .screen {
  width: min(30rem, 92vw);
  max-height: 88vh;
  overflow-y: auto;
  padding: clamp(1.1rem, 3.6vmin, 1.8rem);
  background: var(--night-2);
  border: 4px solid var(--ink);
  border-radius: 20px;
  box-shadow: 0 10px 0 rgba(23, 18, 31, 0.6);
  animation: pop-in 300ms cubic-bezier(0.18, 1.4, 0.4, 1) both;
}

.screen__head { text-align: center; margin-bottom: 1rem; }

.screen__title {
  margin: 0 0 0.25rem;
  font-size: clamp(1.2rem, 3vmin, 1.7rem);
  letter-spacing: 0.06em;
}

.screen__stats {
  margin: 0;
  font-size: clamp(0.66rem, 1.5vmin, 0.85rem);
  letter-spacing: 0.05em;
  opacity: 0.8;
}

.screen__note {
  margin: 0.9rem 0 0;
  text-align: center;
  font-size: clamp(0.62rem, 1.4vmin, 0.78rem);
  color: var(--cyan);
  opacity: 0.85;
}

.screen__actions {
  display: flex;
  gap: 0.7rem;
  justify-content: center;
  margin-top: 1.1rem;
}

/* ----------------------------------------------------------- level map -- */

.levels {
  display: grid;
  gap: 0.6rem;
}

.level-node {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-areas:
    "id name stars"
    "id par  stars";
  align-items: center;
  gap: 0 0.8rem;
  width: 100%;
  padding: 0.65rem 0.9rem;
  font-family: inherit;
  color: var(--paper);
  text-align: left;
  background: rgba(23, 18, 31, 0.5);
  border: 3px solid var(--ink);
  border-radius: 14px;
  box-shadow: 0 4px 0 rgba(23, 18, 31, 0.5);
  cursor: pointer;
  transition: transform 90ms ease, background-color 140ms ease;
}

.level-node:not(:disabled):hover { background: rgba(107, 45, 99, 0.55); }
.level-node:not(:disabled):active { transform: translateY(3px); box-shadow: 0 1px 0 rgba(23, 18, 31, 0.5); }

.level-node.is-locked {
  opacity: 0.45;
  cursor: not-allowed;
  grid-template-areas: "id lock name";
}

.level-node__id {
  grid-area: id;
  font-size: clamp(1rem, 2.4vmin, 1.35rem);
  font-weight: 700;
  color: var(--gold);
}

.level-node__name {
  grid-area: name;
  font-size: clamp(0.78rem, 1.8vmin, 0.98rem);
  font-weight: 700;
}

.level-node__par {
  grid-area: par;
  font-size: clamp(0.6rem, 1.4vmin, 0.75rem);
  opacity: 0.7;
}

.level-node__lock { grid-area: lock; font-size: 1rem; }

.level-node__stars {
  grid-area: stars;
  display: flex;
  gap: 0.12rem;
  font-size: clamp(0.9rem, 2.2vmin, 1.2rem);
}

.ministar { color: rgba(255, 243, 224, 0.18); }
.ministar.is-earned { color: var(--gold); }

/* Defence missions read differently from attack levels at a glance. */
.level-node--defense .level-node__id { color: var(--cyan); font-size: clamp(0.7rem, 1.7vmin, 0.95rem); }
.level-node--defense { border-color: var(--ink); background: rgba(70, 224, 210, 0.09); }
.level-node--defense:not(:disabled):hover { background: rgba(70, 224, 210, 0.2); }

.levels__world {
  margin: 0.7rem 0 0.1rem;
  font-size: clamp(0.6rem, 1.4vmin, 0.78rem);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.6;
}

.levels__world:first-child { margin-top: 0; }

/* Daily and endless: not part of any world, so they look different. */
.level-node--special { background: rgba(255, 198, 63, 0.12); }
.level-node--special .level-node__id { color: var(--gold); }
.level-node--special:not(:disabled):hover { background: rgba(255, 198, 63, 0.24); }

/* -------------------------------------------------------- upgrade tree -- */

.upgrades {
  display: grid;
  gap: 0.6rem;
}

.upgrade {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding: 0.65rem 0.9rem;
  background: rgba(23, 18, 31, 0.5);
  border: 3px solid var(--ink);
  border-radius: 14px;
}

.upgrade__info { flex: 1; min-width: 0; }

.upgrade__label {
  margin: 0;
  font-size: clamp(0.85rem, 2vmin, 1.05rem);
  font-weight: 700;
  color: var(--gold);
}

.upgrade__blurb {
  margin: 0.1rem 0 0.35rem;
  font-size: clamp(0.62rem, 1.4vmin, 0.78rem);
  opacity: 0.78;
}

.upgrade__pips { display: flex; gap: 0.22rem; }

.pip {
  width: clamp(0.7rem, 1.7vmin, 1rem);
  height: clamp(0.34rem, 0.8vmin, 0.48rem);
  border-radius: 3px;
  background: rgba(255, 243, 224, 0.16);
  border: 2px solid var(--ink);
}

.pip.is-filled { background: var(--cyan); }

.btn--buy {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.05rem;
  min-width: 4.6rem;
  padding: 0.45rem 0.7rem;
  background: var(--gold);
  color: var(--ink);
}

.btn__cost { font-size: clamp(0.85rem, 2vmin, 1.05rem); }

.btn__unit {
  font-size: 0.55em;
  letter-spacing: 0.14em;
}

/* Unaffordable, but still clickable so tapping explains itself with a blip. */
.btn--buy.is-poor {
  background: rgba(255, 243, 224, 0.14);
  color: rgba(255, 243, 224, 0.55);
}

.btn--buy.is-maxed {
  background: transparent;
  color: var(--cyan);
  border-style: dashed;
  cursor: default;
  box-shadow: none;
}

.btn--gem {
  background: var(--cyan);
  color: var(--ink);
}

.btn--gem.is-poor {
  background: rgba(70, 224, 210, 0.16);
  color: rgba(70, 224, 210, 0.6);
}

/* A locked ammo row still shows what it does — that is what makes saving a
   gem for it a decision rather than a gamble. */
.upgrade--ammo.is-locked .upgrade__label,
.upgrade--ammo.is-locked .upgrade__blurb { opacity: 0.6; }

.upgrade__stat {
  margin-left: 0.45rem;
  font-size: clamp(0.55rem, 1.3vmin, 0.7rem);
  letter-spacing: 0.05em;
  color: var(--cyan);
  opacity: 0.9;
}

.screen__section {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: 1.1rem 0 0.55rem;
  padding-top: 0.7rem;
  border-top: 2px solid rgba(255, 243, 224, 0.14);
  font-size: clamp(0.7rem, 1.7vmin, 0.9rem);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.85;
}

.screen__section-note {
  font-size: 0.85em;
  letter-spacing: 0.08em;
  color: var(--cyan);
}

.btn--buy.shake { animation: nudge 260ms ease; }

@keyframes nudge {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

/* -------------------------------------------------------------- boot -- */

.bootfail {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--night);
  z-index: 10;
}

.bootfail[hidden] { display: none; }

/* Matter.js failed to load from the CDN — same remedy, different cause. */
.is-offline .bootfail { display: grid !important; }

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

@keyframes pop-in {
  from { opacity: 0; transform: translateY(18px) scale(0.9); }
  to   { opacity: 1; transform: none; }
}

@keyframes star-pop {
  0%   { transform: scale(0) rotate(-40deg); }
  70%  { transform: scale(1.25) rotate(6deg); }
  100% { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .card, .star.is-earned, .overlay { animation: none; }
}
