/* ==========================================================================
   Bond Burst - mobile-first styling
   ========================================================================== */

:root {
  --theme-a: #1b2a6b;
  --theme-b: #0a1030;
  --theme-accent: #5FD4FF;

  --ink: #F4F8FF;
  --ink-dim: #A9B7DA;
  --panel: rgba(12, 16, 44, 0.72);
  --panel-line: rgba(255, 255, 255, 0.14);
  --rim: rgba(77, 124, 255, 0.5);
  --glass-hi: rgba(255, 255, 255, 0.16);

  --hot: #FF4D8D;
  --gold: #FFD84D;
  --lime: #9CF25B;
  --cyan: #4FE3FF;
  --violet: #B478FF;

  --app-max: 520px;
  --radius: 18px;
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);

  --font: "Baloo 2", "Nunito", "Trebuchet MS", system-ui, -apple-system, sans-serif;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  background: #070a1c;
  color: var(--ink);
  font-family: var(--font);
  font-weight: 700;
  overscroll-behavior: none;
}

body::before {
  /* animated cosmos backdrop -- V1 DARK: a deep-navy nebula. The world
     behind the board no longer carries the contrast job alone; the
     board's neon rim and lighter tray interior do the figure/ground
     work, so the cosmos can stay moody. Per-world tint (--theme-a/b)
     still breathes through under the navy scrim. */
  content: "";
  position: fixed;
  inset: -20%;
  z-index: 0;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='520' height='520'%3E%3Cg fill='none' stroke='rgba(165,195,255,0.12)' stroke-width='2'%3E%3Cpath d='M110 40 L143 59 L143 97 L110 116 L77 97 L77 59 Z'/%3E%3Ccircle cx='110' cy='40' r='5'/%3E%3Ccircle cx='143' cy='59' r='5'/%3E%3Ccircle cx='143' cy='97' r='5'/%3E%3Ccircle cx='110' cy='116' r='5'/%3E%3Ccircle cx='77' cy='97' r='5'/%3E%3Ccircle cx='77' cy='59' r='5'/%3E%3Cpath d='M430 126 L451 138 L451 162 L430 174 L409 162 L409 138 Z'/%3E%3Ccircle cx='370' cy='395' r='7'/%3E%3Ccircle cx='408' cy='420' r='7'/%3E%3Ccircle cx='446' cy='398' r='7'/%3E%3Cpath d='M376 400 L402 415 M414 415 L440 403'/%3E%3Ccircle cx='250' cy='260' r='4'/%3E%3Ccircle cx='60' cy='430' r='4'/%3E%3C/g%3E%3C/svg%3E"),
    radial-gradient(52% 40% at 18% 12%, rgba(53, 208, 255, 0.40), transparent 62%),
    radial-gradient(46% 36% at 84% 22%, rgba(138, 92, 255, 0.50), transparent 64%),
    radial-gradient(60% 48% at 50% 92%, rgba(255, 110, 199, 0.30), transparent 66%),
    radial-gradient(38% 30% at 12% 78%, rgba(77, 124, 255, 0.34), transparent 68%),
    linear-gradient(180deg, rgba(10, 10, 30, 0.40), rgba(7, 8, 24, 0.55)),
    linear-gradient(170deg, var(--theme-a), var(--theme-b) 72%);
  background-size: 520px 520px, auto, auto, auto, auto, auto, auto;
  filter: saturate(1.25);
  animation: drift 26s ease-in-out infinite alternate;
  transition: background 600ms ease;
}

body::after {
  /* fine star dust. (The painted molecules.png tried to live here as
     a tiled layer -- nine chunky glows per tile turned the sky into
     wallpaper. The crisp SVG wireframes on ::before won the job;
     the painting waits for a sparser role.) */
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    radial-gradient(1.6px 1.6px at 20% 30%, rgba(255,255,255,.8), transparent),
    radial-gradient(1.4px 1.4px at 70% 18%, rgba(255,255,255,.7), transparent),
    radial-gradient(1.8px 1.8px at 42% 72%, rgba(255,255,255,.6), transparent),
    radial-gradient(1.3px 1.3px at 88% 64%, rgba(255,255,255,.7), transparent),
    radial-gradient(1.5px 1.5px at 12% 88%, rgba(255,255,255,.55), transparent),
    radial-gradient(1.2px 1.2px at 60% 44%, rgba(255,255,255,.5), transparent);
  background-size: 340px 340px;
  opacity: .55;
  animation: twinkle 6s ease-in-out infinite alternate;
}

@keyframes drift {
  0%   { transform: translate3d(0,0,0) scale(1); }
  100% { transform: translate3d(-3%, 2%, 0) scale(1.06); }
}
@keyframes twinkle { 0% { opacity: .35 } 100% { opacity: .7 } }

@media (prefers-reduced-motion: reduce) {
  body::before, body::after { animation: none; }
  /* the pressure cues stay VISIBLE (colour is information) -- only their
     motion stops. Gate by selector: `warn` is shared with the boss bar. */
  .moves-pod.low .moves-num, .boss-fill.low { animation: none; }
}

#app {
  position: relative;
  z-index: 1;
  height: 100%;
  width: 100%;
}

/* ------------------------------------------------------------- screens */

.screen {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  padding: calc(var(--safe-t) + 6px) 0 var(--safe-b);
  opacity: 0;
  transition: opacity 260ms ease;
}
.screen.active { display: flex; opacity: 1; animation: screenIn 320ms ease both; }
@keyframes screenIn { from { opacity: 0; transform: translateY(10px) } to { opacity: 1; transform: none } }

/* -------------------------------------------------------------- buttons */

.btn {
  font-family: var(--font);
  font-weight: 800;
  font-size: 1.05rem;
  color: #fff;
  border: 0;
  border-radius: 999px;
  /* A press that lands slightly long selects the LABEL instead of pressing
   * the button, which on a touch screen leaves the word sitting there
   * highlighted with a pair of selection handles over it. Buttons are not
   * text. */
  user-select: none;
  -webkit-user-select: none;
  padding: 14px 30px;
  cursor: pointer;
  letter-spacing: .02em;
  transition: transform 120ms ease, filter 160ms ease, box-shadow 160ms ease;
  position: relative;
  touch-action: manipulation;
}
.btn-primary {
  background: linear-gradient(180deg, #6BE8FF, #2B8BFF 55%, #2461E6);
  box-shadow: 0 6px 0 #1B45A8, 0 12px 26px rgba(43, 139, 255, .45);
  text-shadow: 0 2px 0 rgba(0,0,0,.22);
}
.btn-primary:active { transform: translateY(4px); box-shadow: 0 2px 0 #1B45A8, 0 6px 14px rgba(43,139,255,.4); }
.btn-ghost {
  background: rgba(255,255,255,.10);
  border: 2px solid rgba(255,255,255,.24);
  box-shadow: 0 4px 0 rgba(0,0,0,.24);
}
.btn-ghost:active { transform: translateY(3px); box-shadow: 0 1px 0 rgba(0,0,0,.24); }
/* Destructive answers read hot, never inviting -- the primary stays on "keep". */
.btn-danger {
  background: rgba(255, 77, 141, .12);
  border-color: rgba(255, 77, 141, .5);
  color: #FFC2D6;
}
.btn:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }

.icon-btn {
  width: 40px; height: 40px;
  flex: 0 0 auto;
  display: grid; place-items: center;
  border-radius: 14px;
  border: 2px solid rgba(255,255,255,.22);
  background:
    linear-gradient(180deg, rgba(255,255,255,.14), rgba(255,255,255,.03));
  color: #fff;
  font-size: 1.25rem;
  font-family: var(--font);
  font-weight: 800;
  cursor: pointer;
  backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 4px 10px rgba(0,0,0,.3);
  transition: transform 120ms ease, background 160ms ease;
}
.icon-btn:active { transform: scale(.92); }
.icon-btn.off { opacity: .45; }

.chip-btn {
  font-family: var(--font);
  font-weight: 800;
  font-size: .82rem;
  color: var(--ink);
  background:
    linear-gradient(180deg, rgba(255,255,255,.14), rgba(255,255,255,.03));
  border: 1.5px solid rgba(255,255,255,.22);
  border-radius: 999px;
  padding: 7px 13px;
  cursor: pointer;
  white-space: nowrap;
  backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.22), 0 4px 10px rgba(0,0,0,.28);
  transition: transform 120ms ease;
}
.chip-btn:active { transform: scale(.94); }

/* ---------------------------------------------------------------- title */

/* THE TITLE SCREEN MUST NEVER CLIP.
 *
 * This was `justify-content: center` on a flex column inside a screen that
 * is `position: absolute; inset: 0`, so once the content grew past the
 * viewport it overflowed BOTH ENDS AT ONCE and neither was reachable. On an
 * iPhone 16 in Safari -- 393x659 once the URL bar and home indicator are
 * taken out -- the logo was sliced across the middle and the entire share
 * row was simply gone. A centred overflow is the worst kind, because the
 * usual instinct (scroll) does nothing and the usual fallback (shrink the
 * window) hides it: at the nominal 393x852 it very nearly fits, which is
 * why it survived a desktop check.
 *
 * `justify-content: flex-start` plus `margin: auto 0` on the inner is the
 * arrangement that survives both cases -- auto margins still centre it
 * optically when there is spare room, and collapse to the top when there is
 * not, leaving the overflow reachable by scroll instead of severed.
 */
#screen-title {
  align-items: center;
  justify-content: flex-start;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.title-inner {
  width: min(100%, var(--app-max));
  padding: 24px 24px calc(24px + var(--safe-b));
  margin: auto 0;
  text-align: center;
  animation: floatIn 700ms cubic-bezier(.2,.9,.3,1.2) both;
}
@keyframes floatIn { from { opacity: 0; transform: translateY(26px) scale(.94) } to { opacity: 1; transform: none } }

.logo { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.logo-el {
  /* the CSS tile retires: the painted Bb mark takes the stage. Its
     baked glow-on-black halo screens away over the dark cosmos. */
  position: relative;
  width: 168px; height: 168px;
  display: grid; place-items: center;
  animation: bob 3.4s ease-in-out infinite;
}
.logo-img {
  width: 100%; height: 100%;
  object-fit: contain;
  mix-blend-mode: screen;
  filter: drop-shadow(0 12px 28px rgba(70,140,255,.4));
  pointer-events: none;
  user-select: none;
}
@keyframes bob { 0%,100% { transform: translateY(0) rotate(-3deg) } 50% { transform: translateY(-10px) rotate(3deg) } }
.logo-sym { font-size: 3.1rem; font-weight: 800; text-shadow: 0 3px 0 rgba(0,0,0,.25); line-height: 1; }
.logo-z { position: absolute; top: 10px; left: 13px; font-size: .8rem; opacity: .85; }

.logo h1 {
  margin: 0;
  font-size: clamp(2.3rem, 12vw, 3.4rem);
  line-height: .95;
  font-weight: 800;
  letter-spacing: -.01em;
  background: linear-gradient(180deg, #FFFFFF, #A8E8FF 60%, #59B6FF);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  filter: drop-shadow(0 4px 0 rgba(20,40,110,.55)) drop-shadow(0 12px 24px rgba(60,150,255,.4));
}
.logo h1 em {
  display: block;
  font-style: normal;
  background: linear-gradient(180deg, #FFF3B0, #FFB03A 60%, #FF5C8A);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.tagline { color: var(--ink-dim); font-size: 1.05rem; margin: 18px 0 26px; line-height: 1.4; }
.title-actions { display: flex; flex-direction: column; gap: 12px; align-items: center; }
.title-actions .btn { width: min(280px, 78%); }
/* Cabinet and How to Play share a row. Stacked full width they cost 73px of
 * a 659px viewport to say two things nobody taps on first open; side by side
 * they cost none of it and read as the pair they are. PLAY stays alone and
 * full width -- it is the only button on this screen that matters. */
.title-actions .btn-row {
  display: flex;
  gap: 10px;
  width: min(280px, 78%);
}
.title-actions .btn-row .btn {
  width: auto;
  flex: 1 1 0;
  min-width: 0;
  padding-left: 10px;
  padding-right: 10px;
  font-size: .95rem;
}
.title-foot { margin-top: 22px; color: var(--ink-dim); font-size: .82rem; opacity: .8; }

/* Short viewports -- which on a phone means MOST of them, once browser
 * chrome is counted. Everything here is breathing room rather than
 * substance, so it is the first thing to give back. */
@media (max-height: 740px) {
  .logo-el { width: 124px; height: 124px; }
  .logo { gap: 8px; }
  .logo h1 { font-size: clamp(2rem, 10.5vw, 2.8rem); }
  .tagline { font-size: .98rem; margin: 12px 0 16px; }
  .title-inner { padding-top: 14px; }
  .title-actions { gap: 10px; }
  .title-foot { margin-top: 14px; }
  .share-row { margin-top: 10px; }
}
/* An SE with the URL bar showing is 375x553, and no amount of trimming fits
 * a logo, a wordmark, four buttons and six share links into that. The screen
 * scrolls there rather than hiding anything -- but it should scroll as
 * little as possible, so the purely decorative half gives way first. */
@media (max-height: 600px) {
  .logo-el { width: 92px; height: 92px; }
  .logo h1 { font-size: clamp(1.7rem, 9vw, 2.2rem); }
  .tagline { margin: 10px 0 12px; font-size: .92rem; }
  .title-inner { padding-top: 10px; }
  .title-foot { margin-top: 10px; font-size: .76rem; }
  .share-row { margin-top: 8px; gap: 5px; }
  .share-btn { padding: 6px 10px; }
}

/* ------------------------------------------------------------- topbar */

.topbar {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 12px 10px;
  width: min(100%, var(--app-max));
  margin: 0 auto;
  flex: 0 0 auto;
}
.topbar-title { flex: 1; text-align: center; line-height: 1.1; }
.topbar-title strong { display: block; font-size: 1.06rem; }
.topbar-title span { font-size: .78rem; color: var(--gold); }
.topbar-actions { display: flex; gap: 6px; }

/* ---------------------------------------------------------------- map */

.map-scroll {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  width: min(100%, var(--app-max));
  margin: 0 auto;
  padding: 0 8px;
  overscroll-behavior: contain;
}
.map-inner { position: relative; width: 100%; }
/* (The glassware bench footer was tried here and CUT: at cover-scale
   on desktop the flasks blew up cartoon-huge. The asset stays in
   assets/img/map for a future, size-capped role.) */
.map-inner::after {
  /* one violet nebula wisp drifting high on the trail -- real alpha,
     no blend tricks needed, capped so desktop cannot inflate it */
  content: "";
  position: absolute;
  top: 3%; right: 1%;
  width: min(44vw, 380px);
  aspect-ratio: 2 / 3;
  background: url("assets/img/map/wisp-violet.png") center / contain no-repeat;
  opacity: .5;
  pointer-events: none;
  z-index: 0;
}
.map-path { position: absolute; inset: 0; overflow: visible; }
/* The trail is a rope: dark casing, soft glass core, and a slow river of
   dashed light flowing along it. */
.trail-case { fill: none; stroke: rgba(6,10,34,.6); stroke-width: 26; stroke-linecap: round; }
.trail-rope { fill: none; stroke: rgba(150,175,240,.22); stroke-width: 18; stroke-linecap: round; }
.trail-ticks {
  fill: none;
  stroke: rgba(255,255,255,.68);
  stroke-width: 5;
  stroke-linecap: round;
  stroke-dasharray: 3 20;
  filter: drop-shadow(0 0 4px rgba(140,180,255,.9));
  animation: trailFlow 26s linear infinite;
}
@keyframes trailFlow { to { stroke-dashoffset: -460; } }
@media (prefers-reduced-motion: reduce) { .trail-ticks { animation: none; } }
.map-nodes { position: absolute; inset: 0; }

/* faint lab clutter scattered behind the trail */
.map-deco {
  position: absolute;
  z-index: 0;
  opacity: .34;
  filter: saturate(.75);
  pointer-events: none;
  user-select: none;
}

/* Candy-Crush-round nodes: a big element-coloured disc with the level
   number as its face, three star pips hanging beneath. Names live on the
   intro card (and on plates for the frontier, bosses and teasers). */
.map-node {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  background: none; border: 0; padding: 0;
  cursor: pointer;
  font-family: var(--font);
  color: var(--ink);
  z-index: 1;
  transition: transform 160ms ease;
}
.map-node:active { transform: translate(-50%, -50%) scale(.95); }

.node-disc {
  position: relative;
  width: 72px; height: 72px;
  flex: 0 0 auto;
  border-radius: 50%;
  display: grid; place-items: center;
  /* the mockups' glossy sphere: hard top gloss, deep belly, a bright
     glass rim and a colored glow sitting on the cosmos */
  background:
    radial-gradient(90% 65% at 50% -8%, rgba(255,255,255,.62), rgba(255,255,255,0) 52%),
    radial-gradient(120% 120% at 50% 118%, rgba(0,0,0,.4), transparent 55%),
    linear-gradient(180deg, var(--c1, #B9C4DE), var(--c2, #5A6890));
  border: 3px solid color-mix(in srgb, var(--cg, #fff) 70%, #fff);
  box-shadow:
    0 7px 0 rgba(6,10,34,.55),
    0 14px 30px rgba(0,0,0,.45),
    0 0 26px color-mix(in srgb, var(--cg, #fff) 48%, transparent),
    inset 0 -7px 12px rgba(0,0,0,.3),
    inset 0 2px 0 rgba(255,255,255,.5);
}
.map-node.unlocked .node-disc { animation: pulseNode 2.4s ease-in-out infinite; }
@keyframes pulseNode { 0%,100% { transform: scale(1) } 50% { transform: scale(1.05) } }
.map-node.cleared .node-disc { animation: none; }
.map-node.locked .node-disc, .map-node.future .node-disc { filter: grayscale(.8) brightness(.62); }
.map-node.future { opacity: .8; }

.node-num {
  font-size: 1.5rem; font-weight: 800; line-height: 1;
  color: #fff;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 2px 0 rgba(0,0,0,.45), 0 0 12px rgba(0,0,0,.35);
}
.node-num.lk { font-size: 1.05rem; }

.el-badge.node-mini {
  position: absolute; right: -8px; bottom: -5px;
  width: 26px; height: 26px;
  box-shadow: 0 2px 5px rgba(0,0,0,.55);
}
.el-badge.node-mini b { font-size: .6rem; }
.el-badge.node-mini i { display: none; }

.node-pips { display: inline-flex; gap: 3px; }
.pp {
  font-size: 1.08rem; line-height: 1;
  color: rgba(255,255,255,.3);
  text-shadow: 0 2px 2px rgba(0,0,0,.5);
}
.pp.on { color: var(--gold); text-shadow: 0 0 8px rgba(255,216,77,.85), 0 2px 0 rgba(140,90,0,.5); }

.node-plate {
  max-width: min(46vw, 170px);
  padding: 3px 11px;
  border-radius: 999px;
  background: rgba(10,16,44,.86);
  border: 1.5px solid rgba(160,185,255,.38);
  font-size: .68rem; font-weight: 800;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  backdrop-filter: blur(8px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14), 0 4px 10px rgba(0,0,0,.35);
}
.node-plate.boss { border-color: rgba(255,229,102,.55); color: #FFE99C; }

/* world ribbons between trail segments */
.map-band {
  position: absolute;
  left: 0; right: 0;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  pointer-events: none;
  z-index: 3; /* above the glassware bench at the trail's foot */
}
.band-ribbon {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  min-width: 220px; max-width: 86%;
  padding: 9px 36px 10px;
  text-align: center;
  color: #fff;
  /* dark glass plaque, world-tinted NEON edge -- the mockups' banner.
     (The old tint-mixed body went olive on gold worlds. Never mix the
     tint into the body; the tint lives on the rim and the glow.) */
  background:
    linear-gradient(180deg, rgba(255,255,255,.09), rgba(255,255,255,0) 45%),
    linear-gradient(180deg, rgba(20, 26, 66, 0.94), rgba(10, 14, 40, 0.96));
  border: 2px solid color-mix(in srgb, var(--wt, #FFE45E) 80%, transparent);
  box-shadow:
    inset 0 1.5px 0 rgba(255,255,255,.22),
    0 0 26px color-mix(in srgb, var(--wt, #FFE45E) 48%, transparent),
    0 8px 18px rgba(0,0,0,.4);
  clip-path: polygon(0 0, 100% 0, calc(100% - 15px) 50%, 100% 100%, 0 100%, 15px 50%);
}
.band-ribbon i { color: color-mix(in srgb, var(--wt, #FFE45E) 85%, #fff); }

/* a felled boss leaves a mark: the world's plaque keeps its light on */
.map-band.cleared .band-ribbon {
  border-color: color-mix(in srgb, var(--wt, #FFE45E) 95%, #fff);
  box-shadow:
    inset 0 1.5px 0 rgba(255,255,255,.3),
    0 0 34px color-mix(in srgb, var(--wt, #FFE45E) 62%, transparent),
    0 8px 18px rgba(0,0,0,.4);
}
.map-band.cleared .band-ribbon b { text-shadow: 0 0 14px color-mix(in srgb, var(--wt, #FFE45E) 70%, transparent), 0 2px 4px rgba(0,0,0,.45); }
.band-stars.perfect { color: var(--gold); text-shadow: 0 0 10px rgba(255,216,77,.9); }
.band-ribbon i {
  font-style: normal;
  font-size: .58rem; letter-spacing: .24em; text-transform: uppercase;
  opacity: .85;
}
.band-ribbon b { font-size: 1.02rem; letter-spacing: .03em; text-shadow: 0 2px 4px rgba(0,0,0,.45); }
.band-stars {
  font-style: normal; font-size: .7rem; font-weight: 800;
  color: var(--wt, var(--gold));
  padding: 2px 10px;
  border-radius: 999px;
  background: rgba(10,16,44,.78);
  border: 1.5px solid color-mix(in srgb, var(--wt, #FFE45E) 40%, transparent);
}

/* world-boss nodes wear the crown, one size up */
.map-node.boss .node-disc {
  width: 78px; height: 78px;
  border-color: #FFE566;
  box-shadow:
    0 8px 0 rgba(120,80,0,.5),
    0 0 30px rgba(255,229,102,.55),
    inset 0 -6px 10px rgba(0,0,0,.26);
}
.map-node.boss .node-num { font-size: 1.75rem; }
.map-node.boss.locked .node-disc { box-shadow: 0 8px 0 rgba(6,10,34,.5); }
.node-crown {
  position: absolute;
  top: -17px; left: 50%;
  transform: translateX(-50%) rotate(-8deg);
  font-size: 1.15rem;
  color: #FFE566;
  text-shadow: 0 2px 0 rgba(120,80,0,.6), 0 0 12px rgba(255,229,102,.8);
  z-index: 2;
}
.m-kicker.boss { color: #FFE566; border-color: #FFE566; }

.stars { display: inline-flex; gap: 2px; font-size: 1rem; line-height: 1; }
.stars.sm { font-size: .82rem; }
.star { color: rgba(255,255,255,.22); }
.star.on { color: var(--gold); text-shadow: 0 0 8px rgba(255,216,77,.8); }

/* ---------------------------------------------------------------- hud */

.hud {
  flex: 0 0 auto;
  width: min(100%, var(--app-max));
  margin: 0 auto;
  padding: 0 10px 6px;
}
.hud-top { display: flex; align-items: center; gap: 10px; }
.hud-title { flex: 1; text-align: center; line-height: 1.05; min-width: 0; }
.hud-title strong { display: block; font-size: 1.02rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hud-title span { font-size: .72rem; color: var(--ink-dim); }

.hud-main {
  display: flex; align-items: center; gap: 10px;
  margin-top: 8px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,0) 40%),
    linear-gradient(180deg, rgba(28, 38, 92, 0.85), rgba(14, 19, 52, 0.9));
  border: 2px solid color-mix(in srgb, var(--theme-accent) 55%, rgba(120,160,255,.4));
  border-radius: var(--radius);
  padding: 8px 12px;
  backdrop-filter: blur(14px);
  box-shadow:
    inset 0 1px 0 var(--glass-hi),
    0 0 22px color-mix(in srgb, var(--theme-accent) 32%, transparent),
    0 10px 24px rgba(0,0,0,.35);
}
.moves-pod {
  flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center;
  min-width: 58px;
  padding: 2px 8px;
  border-radius: 14px;
  /* the mockups' bright blue pod -- the one HUD element that SHOUTS */
  background: linear-gradient(180deg, #5BC9FF, #2E7DF0 58%, #2156C4);
  border: 1.5px solid rgba(190,230,255,.75);
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.5),
    0 0 16px rgba(80,160,255,.45),
    0 4px 10px rgba(0,0,0,.35);
}
.moves-num { font-size: 1.7rem; line-height: 1; font-weight: 800; }
.moves-lbl { font-size: .62rem; letter-spacing: .12em; text-transform: uppercase; color: rgba(255,255,255,.85); }
.moves-pod.low .moves-num { color: #FF6B8B; animation: warn 900ms ease-in-out infinite; }
@keyframes warn { 0%,100% { transform: scale(1) } 50% { transform: scale(1.14) } }

.score-pod { flex: 1 1 auto; min-width: 0; }
.star-track {
  position: relative;
  height: 12px;
  border-radius: 999px;
  background: rgba(4,6,20,.7);
  border: 1.5px solid rgba(255,255,255,.28);
  box-shadow: inset 0 2px 4px rgba(0,0,0,.5);
  overflow: visible;
}
.star-fill {
  position: absolute; inset: 1px auto 1px 1px;
  width: 0;
  border-radius: 999px;
  background: linear-gradient(90deg, #6BE8FF, #9CF25B 45%, #FFD84D);
  box-shadow: 0 0 12px rgba(255,216,77,.6);
  transition: width 340ms cubic-bezier(.2,.8,.3,1);
}
.star-pips { position: absolute; inset: 0; }
.pip {
  position: absolute; top: 50%;
  transform: translate(-50%, -50%);
  font-size: 1.05rem;
  color: rgba(255,255,255,.5);
  text-shadow: 0 1px 2px rgba(0,0,0,.7);
  transition: color 200ms ease, transform 200ms ease;
}
.pip.on { color: var(--gold); transform: translate(-50%,-50%) scale(1.35); text-shadow: 0 0 10px rgba(255,216,77,.9); }
.score-num { font-size: 1.28rem; font-weight: 800; margin-top: 3px; letter-spacing: .01em; }

.objectives { display: flex; gap: 7px; margin-top: 8px; flex-wrap: wrap; justify-content: center; }
.obj {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 11px 4px 5px;
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.08), rgba(255,255,255,0) 55%),
    var(--panel);
  border: 1.5px solid var(--panel-line);
  font-size: .84rem;
  backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 var(--glass-hi), 0 4px 10px rgba(0,0,0,.28);
  transition: background 200ms ease, border-color 200ms ease;
}
.obj.done { background: rgba(120, 230, 130, .22); border-color: rgba(150,255,160,.6); }
.obj.tick { animation: objTick 460ms cubic-bezier(.2, 1.5, .4, 1); }
@keyframes objTick {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.16); box-shadow: 0 0 16px color-mix(in srgb, var(--theme-accent) 55%, transparent), inset 0 1px 0 var(--glass-hi); }
  100% { transform: scale(1); }
}
.obj.done::after { content: "✓"; color: #9CF25B; font-weight: 800; }
.obj-icon { font-size: 1.05rem; width: 26px; text-align: center; }
.obj-icon .obj-img { width: 22px; height: 22px; object-fit: contain; vertical-align: middle; }
.obj-n { font-variant-numeric: tabular-nums; }

/* -------------------------------------------------------------- board */

.board-wrap {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px;
  width: min(100%, var(--app-max));
  margin: 0 auto;
  /* the coach bar pins itself inside this box; without it the bar would
     escape to the viewport */
  position: relative;
}

/* --- the coach: one line of first-use teaching, on the live board -------
   The bar is pointer-events:none and ONLY the dismiss chip takes taps. The
   board underneath must stay swipeable at every moment this is on screen --
   a swipe starting under the bar still has to move a tile. */
.coach {
  position: absolute;
  left: 8px; right: 8px; bottom: 10px;
  z-index: 2;
  display: flex; align-items: center; gap: 8px;
  padding: 9px 10px;
  border-radius: 14px;
  background: rgba(8, 11, 28, .93);
  border: 1.5px solid rgba(125, 243, 255, .38);
  box-shadow: 0 8px 22px rgba(0, 0, 0, .55);
  pointer-events: none;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 200ms ease, transform 200ms ease;
}
.coach.shown { opacity: 1; transform: none; }
.coach-art {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  background: center / contain no-repeat;
}
.coach-text {
  flex: 1 1 auto;
  font-size: .78rem; font-weight: 800; line-height: 1.3;
  color: var(--ink);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
}
.coach-x {
  flex: 0 0 auto;
  pointer-events: auto;
  padding: 5px 11px;
  border-radius: 999px;
  border: 1.5px solid rgba(255, 255, 255, .2);
  background: rgba(255, 255, 255, .1);
  color: var(--ink);
  font-family: var(--font); font-weight: 800; font-size: .7rem;
  cursor: pointer;
  touch-action: manipulation;
}
@media (prefers-reduced-motion: reduce) { .coach { transition: none; } }

/* --- the tutor: the same bar, wearing the lesson's colours -------------- */
.coach.tutor { border-color: rgba(255, 228, 94, .5); }
.coach.tutor .coach-text { display: block; }
.coach.tutor .coach-text b {
  display: block;
  font-size: .68rem; letter-spacing: .13em; text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 2px;
}
.coach.tutor .coach-text i { font-style: normal; font-weight: 700; }
/* the beat variant: centred, and it owns the whole bar */
.coach.tutor.spot { text-align: center; }
.coach.tutor.spot .coach-text b { font-size: .74rem; }
.tutor-skip { border-color: rgba(255, 228, 94, .35); }
@media (max-width: 380px) {
  .coach { padding: 7px 8px; gap: 6px; }
  .coach-text { font-size: .72rem; }
  .coach-x { padding: 4px 9px; font-size: .66rem; }
}
#board {
  touch-action: none;
  display: block;
  border-radius: 22px;
  filter:
    drop-shadow(0 18px 34px rgba(0,0,0,.55))
    drop-shadow(0 0 22px color-mix(in srgb, var(--theme-accent) 22%, transparent));
}

.game-foot {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 8px;
  padding: 4px 12px 8px;
  width: min(100%, var(--app-max));
  margin: 0 auto;
}
.react-log { flex: 1 1 auto; display: flex; flex-direction: column; gap: 3px; align-items: center; min-width: 0; }
.react-chip {
  font-size: .72rem;
  padding: 3px 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--rc) 26%, transparent);
  border: 1px solid var(--rc);
  color: #fff;
  white-space: nowrap;
  animation: rxIn 260ms cubic-bezier(.2,.9,.3,1.4) both;
  transition: opacity 500ms ease;
}
.react-chip b { color: var(--rc); margin-right: 5px; }
.react-chip.fade { opacity: 0; }
@keyframes rxIn { from { transform: scale(.6); opacity: 0 } to { transform: none; opacity: 1 } }

/* -------------------------------------------------------- element badge */

.el-badge {
  position: relative;
  width: 34px; height: 34px;
  flex: 0 0 auto;
  display: grid; place-items: center;
  background: linear-gradient(160deg, var(--c1), var(--c2));
  box-shadow: inset 0 2px 0 rgba(255,255,255,.55), 0 3px 8px rgba(0,0,0,.35);
  color: #fff;
  font-weight: 800;
  border-radius: 26%;
}
.el-badge b { font-size: .92rem; text-shadow: 0 1px 2px rgba(0,0,0,.55); line-height: 1; }
.el-badge i {
  position: absolute; top: 2px; left: 4px;
  font-style: normal; font-size: .5rem; opacity: .9;
}
.el-badge.big { width: 54px; height: 54px; }
.el-badge.big b { font-size: 1.4rem; }
.el-badge.big i { font-size: .62rem; top: 4px; left: 6px; }
.el-badge.obj-el { width: 26px; height: 26px; }
.el-badge.obj-el b { font-size: .72rem; }
.el-badge.obj-el i { display: none; }
.el-badge.node-el { width: 42px; height: 42px; }
.el-badge.node-el b { font-size: 1.1rem; }

.el-badge[data-shape="circle"] { border-radius: 50%; }
.el-badge[data-shape="blob"]   { border-radius: 58% 42% 52% 48% / 48% 56% 44% 52%; }
.el-badge[data-shape="square"] { border-radius: 26%; }
.el-badge[data-shape="hex"]    { clip-path: polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%); border-radius: 0; }
/* platinum's gauze reads as an octagon at badge size -- the ring's hole is
   canvas-only, since a clip-path cannot punch one without hiding the symbol */
.el-badge[data-shape="gauze"]  { clip-path: polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%); border-radius: 0; }
.el-badge[data-shape="diamond"]{ clip-path: polygon(50% 0%, 94% 50%, 50% 100%, 6% 50%); border-radius: 0; }
.el-badge[data-shape="triangle"]{ clip-path: polygon(50% 4%, 96% 90%, 4% 90%); border-radius: 0; }
.el-badge[data-shape="gear"] {
  clip-path: polygon(43% 0%, 57% 0%, 60% 12%, 74% 17%, 85% 8%, 95% 18%, 87% 29%, 92% 42%, 100% 45%, 100% 57%, 89% 61%, 84% 73%, 92% 84%, 82% 94%, 71% 87%, 58% 92%, 55% 100%, 43% 100%, 40% 89%, 27% 84%, 16% 92%, 6% 82%, 14% 71%, 8% 58%, 0% 55%, 0% 43%, 12% 40%, 17% 27%, 9% 16%, 19% 6%, 30% 14%, 42% 9%);
  border-radius: 0;
}
.el-badge[data-shape="gear"] i { display: none; }
.el-badge[data-shape="gem"] {
  clip-path: polygon(25% 0%, 75% 0%, 100% 34%, 50% 100%, 0% 34%);
  border-radius: 0;
}
.el-badge[data-shape="gem"] i { display: none; }
.el-badge[data-shape="ingot"] {
  clip-path: polygon(18% 12%, 82% 12%, 100% 88%, 0% 88%);
  border-radius: 0;
}
.el-badge[data-shape="ingot"] i { display: none; }
.el-badge[data-shape="puddle"] {
  clip-path: ellipse(49% 36% at 50% 58%);
  border-radius: 0;
}
.el-badge[data-shape="puddle"] i { display: none; }
.el-badge[data-shape="wisp"] {
  clip-path: polygon(42% 100%, 12% 78%, 8% 46%, 30% 38%, 52% 42%, 60% 22%, 42% 8%, 62% 0%, 90% 14%, 94% 42%, 74% 54%, 60% 70%, 68% 88%, 55% 100%);
  border-radius: 0;
}
.el-badge[data-shape="wisp"] i { display: none; }
.el-badge[data-shape="shield"] {
  clip-path: polygon(9% 11%, 91% 11%, 93% 45%, 75% 72%, 50% 100%, 25% 72%, 7% 45%);
  border-radius: 0;
}
.el-badge[data-shape="shield"] i { display: none; }
.el-badge[data-shape="bone"] {
  clip-path: polygon(18% 2%, 34% 12%, 50% 30%, 66% 12%, 82% 2%, 98% 18%, 88% 34%, 70% 50%, 88% 66%, 98% 82%, 82% 98%, 66% 88%, 50% 70%, 34% 88%, 18% 98%, 2% 82%, 12% 66%, 30% 50%, 12% 34%, 2% 18%);
  border-radius: 0;
}
.el-badge[data-shape="bone"] i { display: none; }
.el-badge[data-shape="shard"] {
  clip-path: polygon(40% 0%, 86% 32%, 78% 78%, 48% 99%, 11% 68%, 20% 30%);
  border-radius: 0;
}
.el-badge[data-shape="shard"] i { display: none; }
.el-badge[data-shape="dart"] {
  clip-path: polygon(50% 0%, 94% 89%, 65% 76%, 50% 97%, 35% 76%, 6% 89%);
  border-radius: 0;
}
.el-badge[data-shape="dart"] i { display: none; }
.el-badge[data-shape="moon"] {
  clip-path: polygon(48% 0%, 22% 10%, 5% 32%, 0% 55%, 8% 80%, 28% 96%, 52% 100%, 74% 94%, 60% 82%, 46% 62%, 44% 40%, 52% 18%, 68% 6%);
  border-radius: 0;
}
.el-badge[data-shape="moon"] i { display: none; }
.el-badge[data-shape="sun"] {
  clip-path: polygon(50% 0%, 60% 26%, 85% 15%, 74% 40%, 100% 50%, 74% 60%, 85% 85%, 60% 74%, 50% 100%, 40% 74%, 15% 85%, 26% 60%, 0% 50%, 26% 40%, 15% 15%, 40% 26%);
  border-radius: 0;
}
.el-badge[data-shape="sun"] i { display: none; }
.el-badge[data-shape="bolt"] {
  clip-path: polygon(58% 0%, 20% 57%, 46% 57%, 42% 100%, 80% 43%, 54% 43%);
  border-radius: 0;
}
.el-badge[data-shape="bolt"] i { display: none; }
.el-badge[data-shape="flask"] {
  clip-path: polygon(36% 0%, 64% 0%, 64% 26%, 86% 42%, 96% 62%, 88% 84%, 66% 98%, 34% 98%, 12% 84%, 4% 62%, 14% 42%, 36% 26%);
  border-radius: 0;
}
.el-badge[data-shape="flask"] i { display: none; }
.el-badge[data-shape="spark"] {
  clip-path: polygon(50% 0%, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0% 50%, 40% 40%);
  border-radius: 0;
}
.el-badge[data-shape="spark"] i { display: none; }
.el-badge[data-shape="tube"] {
  clip-path: inset(21% 2% 21% 2% round 999px);
  border-radius: 0;
}
.el-badge[data-shape="tube"] i { display: none; }
.el-badge[data-shape="flame"] {
  clip-path: polygon(50% 0%, 64% 18%, 84% 30%, 93% 55%, 86% 82%, 64% 97%, 36% 97%, 14% 82%, 7% 55%, 16% 30%, 40% 16%);
  border-radius: 0;
}
.el-badge[data-shape="flame"] i { display: none; }
.el-badge[data-shape="star"] {
  clip-path: polygon(50% 0%, 65.5% 23.2%, 93.3% 25%, 81% 50%, 93.3% 75%, 65.5% 76.8%,
                     50% 100%, 34.5% 76.8%, 6.7% 75%, 19% 50%, 6.7% 25%, 34.5% 23.2%);
  border-radius: 0;
}
.el-badge[data-shape="hex"] i, .el-badge[data-shape="diamond"] i,
.el-badge[data-shape="triangle"] i, .el-badge[data-shape="star"] i { display: none; }

/* ------------------------------------------------------------- overlay */

.overlay {
  position: fixed; inset: 0; z-index: 40;
  display: grid; place-items: center;
  padding: calc(10px + var(--safe-t)) 14px calc(10px + var(--safe-b));
  background: rgba(4, 7, 22, .72);
  backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 220ms ease;
}
.overlay.shown { opacity: 1; }
.overlay.hidden { display: none; }

/* The celebration layer: a canvas sibling of the card, painted at full
   brightness ABOVE the scrim (the board canvas behind it is dimmed and
   clipped to the board box, so it can never carry this moment) and BEHIND
   the card, so paper never falls across the score. */
.fx-layer {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.modal {
  /* positioned so it stacks above .fx-layer -- note the transform below
     creates a stacking context only until `.overlay.shown` removes it */
  position: relative;
  z-index: 1;
  width: min(100%, 430px);
  max-height: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: linear-gradient(180deg, rgba(26,36,84,.97), rgba(12,17,46,.98));
  border: 2px solid rgba(255,255,255,.16);
  border-radius: 26px;
  padding: 22px 20px calc(20px + var(--safe-b));
  box-shadow: 0 26px 60px rgba(0,0,0,.6), inset 0 2px 0 rgba(255,255,255,.16);
  transform: translateY(18px) scale(.96);
  opacity: 0;
  transition: transform 280ms cubic-bezier(.2,.9,.3,1.25), opacity 220ms ease;
  overscroll-behavior: contain;
}
.overlay.shown .modal { transform: none; opacity: 1; }
.modal.wide { width: min(100%, 480px); }
/* the intro card runs dense; trim its chrome so it fits a phone screen */
.modal.intro { padding: 16px 18px calc(15px + var(--safe-b)); }
.modal.intro .m-head { margin-bottom: 8px; }
.modal.intro .m-head h2 { margin: 8px 0 3px; font-size: 1.45rem; }
.modal.intro .m-actions { margin-top: 14px; }

/* A PHONE HELD SIDEWAYS IS ABOUT 375px TALL, and the bench card carries
   roughly 700px: plaque, objectives, the shop row, flavour, the element
   meta, then Play. The card scrolls, so nothing was broken -- but PLAY
   sat 300px below the fold, which on the portal build is the one click
   the player is supposed to land on. Pin the action row to the bottom of
   the card's own scrollport: read as much or as little as you like, the
   primary action never moves. Applies to the win and lose cards too,
   where Retry/Next want the same guarantee. */
@media (max-height: 560px) and (orientation: landscape) {
  /* The title screen stacks 480px of logo, tagline, menu and footer down
     the middle -- fine in portrait, but a sideways phone is 375px tall
     and Share and the star count fell off the end. Shrinking type only
     got it to 457. A wide, short screen wants COLUMNS: the mark and its
     tagline take the left, the menu takes the right, the footer runs
     under both. Nothing is hidden and nothing needs scrolling. */
  .title-inner {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: center;
    column-gap: 26px;
    row-gap: 4px;
    max-width: 780px;
    padding-top: 10px;
    padding-bottom: calc(10px + var(--safe-b));
  }
  .logo { grid-column: 1; gap: 4px; }
  .logo-el { width: 92px; height: 92px; animation-duration: 4.2s; }
  .logo h1 { font-size: 2rem; }
  .title-specimen { grid-column: 1; }
  .tagline { grid-column: 1; margin: 0; font-size: .82rem; }
  .title-actions { grid-column: 2; grid-row: 1 / 4; margin: 0; }
  .title-foot { grid-column: 1 / -1; margin: 2px 0 0; }
  .share-row { grid-column: 1 / -1; margin: 0; }

  /* THE BENCH CARD, SIDEWAYS.
     First attempt pinned the button row to the bottom of a scrolling
     card. It reached the goal (Play always tappable) and looked broken
     doing it: the booster row ran on underneath the bar and showed
     through as a strip of half-circles, which reads as a rendering fault
     rather than a design. So the card stops being a tall scroller and
     becomes two columns -- the goal on the left, the shop on the right,
     header and buttons spanning both. Everything fits; nothing scrolls;
     the eye lands on the objective and then on Play. */
  .modal.intro {
    width: min(100%, 780px);
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 18px;
    row-gap: 5px;
    align-content: start;
    padding: 10px 16px calc(6px + var(--safe-b));
  }
  /* Explicit rows, because the shop is the tallest thing on the card and
     a plain two-column grid lets it set the height of a row the left
     side does not need. Spanning it down the right edge lets the goal
     and the flavour line stack beside it for free. */
  .modal.intro .m-head { grid-column: 1 / -1; grid-row: 1; margin-bottom: 0; }
  .modal.intro .m-head h2 { margin: 2px 0 1px; font-size: 1.2rem; }
  .modal.intro .m-head .m-sub { font-size: .74rem; }
  /* the objective panel spends ~52px of its 178 on padding; half of that
     is the difference between a card that fits and one that scrolls */
  .modal.intro .hero-obj { grid-column: 1; grid-row: 2; margin: 0; padding: 10px 12px; }
  .modal.intro .m-flavor { grid-column: 1; grid-row: 3; margin: 0 2px; font-size: .76rem; }
  .modal.intro .prep { grid-column: 2; grid-row: 2 / 4; margin: 0; }
  /* the element badges are the one panel that is reference rather than
     decision -- the only thing worth spending to keep the rest whole */
  .modal.intro .m-meta { display: none; }
  .modal.intro .m-actions { grid-column: 1 / -1; grid-row: 4; margin-top: 2px; }

}

/* ANY short window, either orientation. The bench card stands about
   730px tall, so everything below that height scrolls it -- a 1280x700
   desktop window as surely as a phone -- and the button row is the last
   thing in the card, which means Play is the part that scrolls away.
   Pin it. On a card that already fits (portrait phones, full-height
   desktops, and the two-column landscape above) this does nothing.
   The gradient is opaque well before the buttons so content passing
   underneath fades out instead of showing through as half-shapes. */
/* The shortest phones held sideways (375px) are ~18px short of fitting
   the two-column card outright. The level's subtitle is the cheapest
   thing on it -- the name above it says the same thing louder. */
@media (max-height: 400px) and (orientation: landscape) {
  .modal.intro .m-head .m-sub { display: none; }
}

@media (max-height: 760px) {
  .modal .m-actions {
    position: sticky;
    bottom: 0;
    padding: 8px 0 calc(4px + var(--safe-b));
    background: linear-gradient(180deg, rgba(15,20,50,0), rgba(15,20,50,.99) 34%);
    z-index: 3;
  }
}

.m-head { text-align: center; margin-bottom: 12px; }
.m-kicker {
  display: inline-block;
  font-size: .68rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--m-accent, var(--cyan));
  padding: 3px 12px;
  border: 1.5px solid currentColor;
  border-radius: 999px;
  opacity: .95;
}
/* the level plaque: a gold ribbon riding the top of the card */
.m-plaque {
  display: inline-block;
  padding: 5px 20px;
  font-size: .8rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: #4A2A00;
  background: linear-gradient(180deg, #FFE99C, #F5A623);
  border-radius: 999px;
  box-shadow: 0 3px 0 rgba(120,70,0,.55), 0 6px 14px rgba(0,0,0,.35), inset 0 1.5px 0 rgba(255,255,255,.65);
  transform: translateY(-6px);
}
.m-plaque.boss {
  color: #3A0A0A;
  background: linear-gradient(180deg, #FFE566, #FF8A3D);
  box-shadow: 0 3px 0 rgba(120,30,0,.55), 0 0 22px rgba(255,138,61,.5), inset 0 1.5px 0 rgba(255,255,255,.6);
}

.m-head h2 {
  margin: 10px 0 4px;
  font-size: 1.6rem; font-weight: 800; line-height: 1.1;
  background: linear-gradient(180deg, #fff, #B8DCFF);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.m-head.win h2 { background: linear-gradient(180deg, #FFF6C2, #FFB03A); -webkit-background-clip: text; background-clip: text; }
.m-head.lose h2 { background: linear-gradient(180deg, #FFD9E2, #FF6B8B); -webkit-background-clip: text; background-clip: text; }
.m-sub { margin: 0; color: var(--ink-dim); font-size: .9rem; }
.m-body { color: var(--ink-dim); font-size: .9rem; line-height: 1.5; margin: 8px 0 14px; font-weight: 700; }
.m-body b { color: var(--ink); }
.m-hint { color: var(--gold); font-size: .84rem; text-align: center; margin: 10px 0 0; }

.m-block { margin: 14px 0; }
.m-block h3 {
  margin: 0 0 8px;
  font-size: .74rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-dim);
}
.m-block .m-body { margin: 0 0 8px; }

/* ------------------------------------------------- intro: hero objectives
   The ONE thing the card leads with: what to do, in big icon tiles. */
.hero-obj {
  margin: 12px 0 0;
  padding: 12px 12px 13px;
  border-radius: 18px;
  background: linear-gradient(180deg, rgba(255,255,255,.09), rgba(255,255,255,.03));
  border: 1.5px solid color-mix(in srgb, var(--m-accent, #7CF3FF) 45%, transparent);
  box-shadow: inset 0 1.5px 0 rgba(255,255,255,.12), 0 0 22px color-mix(in srgb, var(--m-accent, #7CF3FF) 14%, transparent);
}
.hero-kicker {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-bottom: 10px;
}
.hero-kicker h3 {
  margin: 0;
  font-size: .72rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-dim);
}
.hero-moves {
  font-size: .72rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--m-accent, var(--cyan));
  padding: 3px 10px;
  border-radius: 999px;
  border: 1.5px solid color-mix(in srgb, var(--m-accent, #7CF3FF) 55%, transparent);
  background: color-mix(in srgb, var(--m-accent, #7CF3FF) 12%, transparent);
  font-variant-numeric: tabular-nums;
}
.hero-tiles { display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; }
.hero-tile {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  min-width: 76px;
  padding: 8px 12px 7px;
  border-radius: 16px;
  background: linear-gradient(180deg, rgba(44,58,112,.85), rgba(16,22,54,.9));
  border: 1.5px solid rgba(160,185,255,.22);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.12), 0 3px 8px rgba(0,0,0,.3);
}
.hero-tile b { font-size: 1.15rem; line-height: 1.1; font-variant-numeric: tabular-nums; }
.hero-tile b .of { font-style: normal; font-size: .78rem; color: var(--ink-dim); }
.hero-tile > i {
  font-style: normal;
  font-size: .64rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-dim);
}
.hero-tile .ht-icon { font-size: 1.7rem; line-height: 1.15; }
.hero-tile .ht-img {
  width: 46px; height: 46px;
  object-fit: contain;
  filter: drop-shadow(0 3px 7px rgba(0,0,0,.45));
}
.el-badge.ht-el { width: 40px; height: 40px; }
.el-badge.ht-el b { font-size: 1rem; }
.el-badge.ht-el i { font-size: .5rem; }
.hero-tile.miss { border-color: rgba(255,107,139,.4); }
.hero-tiles.miss-row { margin: 12px 0 0; }

/* the compact reference strip: elements, reaction formulas, star bands */
.m-meta {
  margin: 10px 0 0;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
}
.meta-line { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }
.el-badge.meta-el { width: 30px; height: 30px; }
.el-badge.meta-el b { font-size: .74rem; }
.el-badge.meta-el i { display: none; }
.rx-mini {
  font-size: .72rem; font-weight: 800;
  color: var(--rc);
  padding: 3px 9px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--rc) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--rc) 55%, transparent);
}
.meta-stars {
  display: flex; gap: 12px;
  font-size: .72rem; font-weight: 800;
  color: var(--gold);
  opacity: .9;
  font-variant-numeric: tabular-nums;
}

.new-el {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px;
  border-radius: 18px;
  background: linear-gradient(120deg, rgba(180,120,255,.28), rgba(80,220,255,.18));
  border: 1.5px solid rgba(200,160,255,.5);
  position: relative;
  margin: 12px 0 0;
}
.new-el-flag {
  position: absolute; top: -9px; left: 14px;
  font-size: .6rem; letter-spacing: .14em; text-transform: uppercase;
  background: linear-gradient(180deg, #FFE27A, #F5A623);
  color: #4A2A00;
  padding: 2px 10px; border-radius: 999px;
  box-shadow: 0 2px 0 rgba(120,70,0,.6);
}
.new-el-txt { display: flex; flex-direction: column; gap: 3px; }
.new-el-txt b { font-size: 1rem; }
.new-el-txt span { font-size: .76rem; color: var(--ink-dim); line-height: 1.3; }

/* one short flavour line; the teaching lives in blurbs and the codex */
.m-flavor {
  margin: 12px 4px 0;
  text-align: center;
  color: var(--ink-dim);
  font-size: .82rem; line-height: 1.45; font-weight: 700;
}

.m-actions { display: flex; gap: 10px; margin-top: 18px; }
.m-actions .btn { flex: 1; padding: 13px 10px; font-size: .98rem; }
/* one dominant CTA; companions shrink to fit */
.m-actions .btn.slim { flex: 0 1 auto; padding: 13px 18px; font-size: .88rem; }
.m-actions .btn.big { flex: 1.6; font-size: 1.08rem; }

/* stars arc over the end card and slam in one by one */
.win-stars { display: flex; justify-content: center; align-items: flex-start; gap: 12px; margin: 10px 0 4px; }
.win-stars.arc .wstar:nth-child(1) { margin-top: 16px; }
.win-stars.arc .wstar:nth-child(3) { margin-top: 16px; }
.wstar {
  position: relative;
  font-size: 3.4rem; line-height: 1;
  color: rgba(255,255,255,.14);
  text-shadow: 0 3px 0 rgba(0,0,0,.4);
  transform: scale(.55);
  transition: transform 420ms cubic-bezier(.2,1.6,.4,1), color 260ms ease;
}
.wstar:nth-child(2) { font-size: 4.4rem; }
.wstar.on {
  color: var(--gold);
  transform: scale(1) rotate(-7deg);
  text-shadow: 0 0 26px rgba(255,216,77,.95), 0 3px 0 rgba(140,90,0,.6);
}
.wstar:nth-child(2).on { transform: scale(1.08) rotate(4deg); }
.wstar:nth-child(3).on { transform: scale(1) rotate(7deg); }
/* impact ring on landing */
.wstar.on::after {
  content: "";
  position: absolute; inset: -6px;
  border-radius: 50%;
  border: 3px solid rgba(255,228,94,.8);
  animation: starRing 480ms ease-out both;
  pointer-events: none;
}
@keyframes starRing {
  0%   { transform: scale(.5); opacity: 1; }
  100% { transform: scale(1.5); opacity: 0; }
}

.win-title {
  margin: 2px 0 10px;
  text-align: center;
  font-size: 1.55rem; font-weight: 800; line-height: 1.1;
  background: linear-gradient(180deg, #FFF6C2, #FFB03A);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* the score reads from a bright pill, Candy Crush style */
.score-pill {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  width: min(78%, 260px);
  margin: 6px auto 0;
  padding: 8px 18px 10px;
  border-radius: 18px;
  background: linear-gradient(180deg, rgba(64,140,255,.38), rgba(20,60,160,.42));
  border: 1.5px solid rgba(140,200,255,.55);
  box-shadow: inset 0 1.5px 0 rgba(255,255,255,.3), inset 0 -3px 8px rgba(0,0,30,.35), 0 6px 16px rgba(0,0,0,.35);
}
.score-pill i {
  font-style: normal;
  font-size: .62rem; letter-spacing: .22em; text-transform: uppercase;
  color: #BFE0FF;
}
/* The consolation line on the lose card: gold, quiet, unmistakably a
 * CREDIT. It shares the coin styling the shop uses (em = the Au mark) so
 * the player reads it as money without a tutorial. */
.lose-bank {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  margin: 8px auto 0;
  padding: 6px 14px;
  width: fit-content;
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(255,214,107,.24), rgba(160,110,10,.28));
  border: 1.5px solid rgba(255,214,107,.5);
  font-size: .82rem; font-weight: 700; color: #FFE2A0;
}
.lose-bank em {
  font-style: normal; font-size: .6rem; font-weight: 800;
  letter-spacing: .04em; color: #FFD66B;
  border: 1.5px solid rgba(255,214,107,.65); border-radius: 999px;
  padding: 1px 5px;
}

/* The goal chips are tappable: cursor + a whisper of affordance. The strip
 * already reads as UI; a loud button treatment would fight the HUD. */
.objectives.tappable { cursor: pointer; }
.objectives.tappable:active { transform: scale(.97); }

/* The Goals modal: one row per objective — icon, the rule in prose, and
 * live progress on the right. */
.goal-help-list {
  display: flex; flex-direction: column; gap: 8px;
  margin: 10px 0 4px;
  max-height: min(52vh, 380px);
  overflow-y: auto;
}
.goal-help {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 9px 10px;
  border-radius: 12px;
  background: rgba(28,40,80,.5);
  border: 1px solid rgba(120,160,255,.25);
  text-align: left;
}
.goal-help.done { opacity: .55; }
.goal-help.done .gh-body b::after { content: " ✓"; color: #7CF3A0; }
.gh-icon { flex: 0 0 auto; width: 34px; display: flex; justify-content: center; padding-top: 2px; }
.gh-img { width: 30px; height: 30px; }
.gh-el { transform: scale(.92); }
.gh-body { flex: 1 1 auto; min-width: 0; }
.gh-body b { display: block; font-size: .88rem; color: #fff; margin-bottom: 2px; }
.gh-body span { font-size: .76rem; line-height: 1.4; color: #B8C8E8; }
.gh-prog {
  flex: 0 0 auto;
  font-weight: 800; font-size: .82rem; color: #FFD66B;
  font-variant-numeric: tabular-nums;
  padding-top: 2px;
}
.gh-prog i { font-style: normal; color: #8A9AC0; font-weight: 600; }
.score-pill b {
  font-size: 2rem; font-weight: 800; line-height: 1.05;
  font-variant-numeric: tabular-nums;
  color: #fff;
  text-shadow: 0 2px 8px rgba(90,160,255,.55);
}
.score-pill.small { width: min(64%, 210px); padding: 6px 16px 8px; }
.score-pill.small b { font-size: 1.5rem; }

/* why the run ended, in one plain sentence */
.lose-reason {
  margin: 4px auto 12px;
  width: fit-content;
  max-width: 100%;
  padding: 8px 16px;
  border-radius: 14px;
  font-size: .92rem; font-weight: 800;
  color: #FFD9E2;
  background: rgba(255,107,139,.14);
  border: 1.5px solid rgba(255,107,139,.45);
  text-align: center;
}
.win-best { text-align: center; font-size: .8rem; color: var(--lime); margin-top: 2px; }
.win-best.dim { color: var(--ink-dim); }
.win-stats { display: flex; gap: 8px; margin-top: 14px; }
.win-stats span {
  flex: 1; text-align: center;
  display: flex; flex-direction: column; gap: 2px;
  padding: 8px 4px;
  border-radius: 14px;
  background: rgba(255,255,255,.07);
  border: 1.5px solid rgba(255,255,255,.12);
  font-size: .66rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-dim);
}
.win-stats b { font-size: 1.2rem; color: var(--ink); }

.sp-list, .rx-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.sp-list li, .rx-list li {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 12px;
  border-radius: 14px;
  background: rgba(255,255,255,.06);
  border: 1.5px solid rgba(255,255,255,.1);
}
.rx-list li { border-color: color-mix(in srgb, var(--rc) 45%, transparent); background: color-mix(in srgb, var(--rc) 10%, rgba(255,255,255,.04)); }
.sp-icon { font-size: 1.6rem; width: 34px; text-align: center; flex: 0 0 auto; }
.sp-list div, .rx-list div { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.sp-list b, .rx-list b { font-size: .92rem; }
.sp-list em { font-style: normal; font-size: .7rem; color: var(--gold); }
.rx-list em { font-style: normal; font-size: .7rem; color: var(--rc, var(--gold)); opacity: .95; }
.sp-list span, .rx-list span { font-size: .78rem; color: var(--ink-dim); line-height: 1.35; }
.rx-eq { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.rx-eq i { font-style: normal; color: var(--ink-dim); font-size: .8rem; }
.rx-eq b { color: var(--rc); font-size: .95rem; }
.rx-eq .el-badge { width: 26px; height: 26px; }
.rx-eq .el-badge b { font-size: .66rem; color: #fff; }

/* ---------------------------------------------------------------- gold */

.gold-pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 12px 5px 6px;
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(255,224,122,.22), rgba(214,154,0,.12));
  border: 1.5px solid rgba(255,216,77,.45);
  font-size: .88rem; font-weight: 800;
  color: #FFE99C;
  white-space: nowrap;
}
.gold-pill i, .gold-inline i, .au-coin {
  /* the painted coin (assets/img/ui/coin-au.png) replaces the CSS
     disc; the Au lettering collapses -- the coin IS the label */
  font-style: normal;
  display: inline-block;
  width: 21px; height: 21px;
  font-size: 0;
  background: url("assets/img/ui/coin-au.png") center / contain no-repeat;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,.45));
}
.gold-inline { color: #FFE99C; font-weight: 800; display: inline-flex; align-items: center; gap: 4px; }
.au-coin { width: 22px; height: 22px; vertical-align: -4px; }

/* every other little Au marker inherits the painted coin too */
.pc-cost u, .cost em {
  font-style: normal;
  display: inline-block;
  width: 15px; height: 15px;
  font-size: 0;
  background: url("assets/img/ui/coin-au.png") center / contain no-repeat;
  vertical-align: -2px;
  border: 0;
  text-decoration: none;
}

/* the intro card's Map button wears the neon map glyph */
.m-actions .btn[data-action="back"]::before {
  content: "";
  display: inline-block;
  width: 17px; height: 17px;
  margin-right: 7px;
  background: url("assets/img/ui/icon-map.png") center / contain no-repeat;
  vertical-align: -3px;
}

.win-gold {
  text-align: center;
  margin-top: 8px;
  font-size: 1.05rem;
  color: #FFE99C;
  animation: rxIn 320ms cubic-bezier(.2,.9,.3,1.4) both;
}

.chip-btn.helper { display: inline-flex; align-items: center; gap: 4px; padding: 7px 10px; }
.chip-btn.helper b { color: #FFE99C; font-size: .72rem; }
.chip-btn.helper.active {
  border-color: #FFE45E;
  background: rgba(255,216,77,.2);
  box-shadow: 0 0 14px rgba(255,216,77,.5);
  animation: pulseNode 1.6s ease-in-out infinite;
}
.chip-btn.helper.poor { opacity: .45; }

/* ---------------------------------------------- intro: prep the bench
   The booster-select moment: tap a special to arm the Catalyst with it.
   Radio behaviour -- one armed at most; tap again to disarm. */
.prep { margin: 12px 0 0; }
.prep-hint { font-size: .66rem; color: var(--ink-dim); font-weight: 700; }
.prep-row { display: flex; gap: 8px; }
.prep-chip {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 8px 6px 7px;
  border-radius: 16px;
  border: 1.5px dashed rgba(255,216,77,.45);
  background: rgba(255,216,77,.06);
  color: var(--ink);
  font-family: var(--font);
  cursor: pointer;
  transition: background 160ms ease, border-color 160ms ease, transform 160ms ease;
}
.prep-chip:active { transform: scale(.95); }
.prep-chip .pc-icon { font-size: 1.55rem; line-height: 1.1; }
.prep-chip .pc-img {
  width: 44px; height: 44px;
  object-fit: contain;
  filter: drop-shadow(0 3px 7px rgba(0,0,0,.45));
}
.prep-chip b { font-size: .78rem; }
.prep-chip .pc-cost {
  display: inline-flex; align-items: center; gap: 4px;
  font-style: normal; font-weight: 800;
  font-size: .7rem; color: #FFE99C;
}
.prep-chip .pc-cost u {
  text-decoration: none;
  display: inline-grid; place-items: center;
  width: 16px; height: 16px; border-radius: 50%;
  background: linear-gradient(160deg, #FFF0A8, #D69A00);
  color: #5A3D00; font-size: .52rem; font-weight: 900;
}
.prep-chip.armed {
  border-style: solid;
  border-color: #9CF25B;
  background: rgba(156,242,91,.14);
  box-shadow: 0 0 16px rgba(156,242,91,.35);
}
.prep-chip.armed .pc-cost { color: #9CF25B; }

.btn-rescue {
  display: block;
  width: 100%;
  margin: 14px 0 0;
  padding: 13px 12px;
  border: 0;
  border-radius: 999px;
  font-family: var(--font);
  font-weight: 800;
  font-size: .98rem;
  color: #04321F;
  background: linear-gradient(180deg, #7CFFD4, #1FA080 60%, #157055);
  box-shadow: 0 5px 0 #0B4A36, 0 10px 22px rgba(31,160,128,.45);
  cursor: pointer;
  transition: transform 120ms ease, box-shadow 120ms ease;
}
.btn-rescue:active { transform: translateY(3px); box-shadow: 0 2px 0 #0B4A36; }
.btn-rescue .cost { margin-left: 6px; color: #FFF6C2; }
.btn-rescue .cost em {
  font-style: normal;
  display: inline-grid; place-items: center;
  width: 18px; height: 18px; border-radius: 50%;
  background: linear-gradient(160deg, #FFF0A8, #D69A00);
  color: #5A3D00; font-size: .56rem; font-weight: 900;
  vertical-align: -3px; margin-right: 2px;
}

.near-miss { display: flex; align-items: center; gap: 12px; margin: 6px 0 4px; }
.near-track {
  flex: 1;
  height: 12px;
  border-radius: 999px;
  background: rgba(0,0,0,.4);
  border: 1.5px solid rgba(255,255,255,.15);
  overflow: hidden;
}
.near-fill {
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, #FF6B8B, #FFD84D);
  box-shadow: 0 0 12px rgba(255,150,100,.6);
  animation: nearGrow 900ms cubic-bezier(.2,.8,.3,1) both;
}
@keyframes nearGrow { from { width: 0; } }
.near-miss b { font-size: .95rem; color: var(--gold); white-space: nowrap; }

/* --------------------------------------------------------------- toast */

.toast {
  position: fixed;
  left: 50%; bottom: calc(84px + var(--safe-b));
  transform: translate(-50%, 20px);
  z-index: 60;
  padding: 10px 20px;
  border-radius: 999px;
  background: rgba(10,16,44,.94);
  border: 1.5px solid rgba(255,255,255,.2);
  color: #fff;
  font-size: .88rem;
  box-shadow: 0 10px 30px rgba(0,0,0,.5);
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease, transform 260ms cubic-bezier(.2,.9,.3,1.3);
  white-space: nowrap;
  max-width: 90vw;
}
.toast.shown { opacity: 1; transform: translate(-50%, 0); }

/* ==========================================================================
   Design-system pass: one card language for every panel.
   Cards share: gradient body, hairline outer border, inset top highlight,
   soft drop. Later-in-file rules override the earlier prototypes.
   ========================================================================== */

:root {
  --card-top: rgba(34, 46, 96, 0.88);
  --card-bot: rgba(11, 16, 44, 0.92);
  --card-line: rgba(160, 185, 255, 0.28);
  --card-hi: rgba(255, 255, 255, 0.14);
  --card-shadow: 0 8px 22px rgba(0, 0, 0, 0.38);
}

.hud-main,
.toast,
.moves-pod {
  background: linear-gradient(180deg, var(--card-top), var(--card-bot));
  border: 1.5px solid var(--card-line);
  box-shadow: inset 0 1.5px 0 var(--card-hi), inset 0 -2px 0 rgba(0, 0, 0, 0.35), var(--card-shadow);
}

.obj,
.chip-btn,
.icon-btn {
  background: linear-gradient(180deg, rgba(44, 58, 112, 0.85), rgba(16, 22, 54, 0.9));
  border: 1.5px solid rgba(160, 185, 255, 0.22);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 3px 8px rgba(0, 0, 0, 0.3);
}
.chip-btn:active, .icon-btn:active {
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.45);
  transform: translateY(1px);
}
.obj.done {
  background: linear-gradient(180deg, rgba(64, 128, 74, 0.6), rgba(26, 74, 40, 0.7));
  border-color: rgba(150, 255, 160, 0.55);
}

.moves-lbl { letter-spacing: 0.16em; }
.moves-num, .score-num, .obj-n, .node-num { font-variant-numeric: tabular-nums; }
.hud-title strong { letter-spacing: 0.01em; }
.m-block h3 { letter-spacing: 0.16em; }

.star-track {
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.3));
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.5);
}
.score-num { text-shadow: 0 2px 8px rgba(90, 160, 255, 0.35); }

.modal {
  border: 1.5px solid var(--card-line);
  box-shadow: inset 0 2px 0 var(--card-hi), 0 26px 60px rgba(0, 0, 0, 0.62);
}

/* world tint flows through the ribbon from --wt (set per world by the map;
   .band-ribbon and .band-stars consume it directly) */

/* ------------------------------------------------------------ boss bar */

.boss-bar {
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px;
  animation: rxIn 400ms cubic-bezier(.2,.9,.3,1.3) both;
}
.boss-bar.hidden { display: none; }
.boss-crown {
  font-size: 1.3rem;
  color: #FFE566;
  text-shadow: 0 2px 0 rgba(120, 80, 0, 0.6), 0 0 12px rgba(255, 229, 102, 0.8);
  animation: bob 2.6s ease-in-out infinite;
}
.boss-track {
  position: relative;
  flex: 1;
  height: 22px;
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.55), rgba(30, 8, 8, 0.45));
  border: 1.5px solid rgba(255, 92, 74, 0.5);
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.55);
  overflow: hidden;
}
.boss-fill {
  position: absolute; inset: 2px auto 2px 2px;
  width: 100%;
  border-radius: 999px;
  background:
    repeating-linear-gradient(-55deg,
      rgba(255, 255, 255, 0.16) 0 8px, rgba(255, 255, 255, 0) 8px 16px),
    linear-gradient(180deg, #FF8A5C, #E0342B 60%, #A81A1A);
  box-shadow: inset 0 1.5px 0 rgba(255, 255, 255, 0.35), 0 0 14px rgba(255, 80, 60, 0.55);
  transition: width 420ms cubic-bezier(.2, .8, .3, 1);
  animation: bossStripes 1.1s linear infinite;
}
@keyframes bossStripes { from { background-position: 0 0, 0 0; } to { background-position: 22px 0, 0 0; } }
.boss-fill.low {
  background:
    repeating-linear-gradient(-55deg,
      rgba(255, 255, 255, 0.2) 0 8px, rgba(255, 255, 255, 0) 8px 16px),
    linear-gradient(180deg, #FFE566, #F0700F 60%, #A83A0A);
  animation: bossStripes 0.55s linear infinite, warn 700ms ease-in-out infinite;
}
.boss-label {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-size: 0.66rem; font-weight: 800; letter-spacing: 0.14em;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8);
  pointer-events: none;
}

/* ------------------------------------------------- frontier + purse FX */

.map-node.frontier .node-disc::after {
  content: "";
  position: absolute; inset: -7px;
  border-radius: 50%;
  border: 2.5px solid rgba(255, 228, 94, 0.85);
  animation: beacon 1.6s ease-out infinite;
  pointer-events: none;
}
@keyframes beacon {
  0%   { transform: scale(0.85); opacity: 0.95; }
  70%  { transform: scale(1.3);  opacity: 0; }
  100% { transform: scale(1.3);  opacity: 0; }
}

.gold-pill.bump { animation: pillBump 520ms cubic-bezier(.2, 1.6, .4, 1) both; }
@keyframes pillBump {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.22); box-shadow: 0 0 18px rgba(255, 216, 77, 0.8); }
  100% { transform: scale(1); }
}

/* --------------------------------------------------- larger viewports */

@media (min-width: 620px) and (min-height: 700px) {
  :root { --app-max: 600px; }
  .moves-num { font-size: 2rem; }
  .score-num { font-size: 1.5rem; }
  .node-disc { width: 80px; height: 80px; }
  .map-node.boss .node-disc { width: 94px; height: 94px; }
  .node-num { font-size: 1.8rem; }
  .node-plate { font-size: .76rem; }
}

/* Desktop: the board was capped at 660px and floated in a sea of empty
   backdrop. Let it grow with the viewport so the screen reads as a composed
   layout rather than a small board lost in space. */
@media (min-width: 900px) and (min-height: 780px) {
  :root { --app-max: 700px; }
}
@media (min-width: 1100px) and (min-height: 950px) {
  :root { --app-max: 820px; }
  .moves-num { font-size: 2.3rem; }
  .score-num { font-size: 1.7rem; }
  .hud-title strong { font-size: 1.2rem; }
}
@media (min-width: 1400px) and (min-height: 1080px) {
  :root { --app-max: 900px; }
}

/* Landscape phones: the viewport is wide and short, so stop stacking. The
   HUD and footer become a left rail and the board takes the full height. */
@media (orientation: landscape) and (max-height: 560px) {
  #screen-game {
    display: none;
    grid-template-columns: minmax(190px, 34vw) 1fr;
    grid-template-rows: 1fr auto;
    grid-template-areas: "hud board" "foot board";
    padding: calc(var(--safe-t) + 4px) 8px 4px;
    gap: 4px;
  }
  #screen-game.active { display: grid; }

  .hud { grid-area: hud; width: 100%; margin: 0; padding: 0; min-height: 0; overflow-y: auto; }
  .board-wrap { grid-area: board; width: 100%; margin: 0; padding: 2px; }
  .game-foot {
    grid-area: foot; width: 100%; margin: 0; padding: 0;
    flex-wrap: wrap; justify-content: center; gap: 6px;
  }
  .react-log { flex: 1 1 100%; order: -1; }

  .hud-main { padding: 5px 9px; margin-top: 5px; }
  .moves-num { font-size: 1.35rem; }
  .moves-lbl { font-size: .54rem; }
  .score-num { font-size: 1.02rem; }
  .objectives { margin-top: 5px; }
  .obj { font-size: .74rem; padding: 2px 9px 2px 4px; }
  .hud-title strong { font-size: .88rem; }
  .hud-title span { font-size: .64rem; }
  .icon-btn { width: 32px; height: 32px; font-size: 1rem; border-radius: 11px; }
  .chip-btn { font-size: .72rem; padding: 5px 10px; }
}

/* ================================================================ cabinet
   Fifteen shelves, world 15 at the top, read the way you climbed. The
   column is DOM (crisp text, real focus targets, cheap layout); each band's
   art is one small canvas that repaints on state change, not per frame.
   ====================================================================== */

.cab-scroll {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  width: min(100%, var(--app-max));
  margin: 0 auto;
  padding: 0 10px 20px;
  overscroll-behavior: contain;
}
.cab-inner { position: relative; width: 100%; }

.shelf {
  position: relative;
  height: 176px;
  margin-bottom: 30px;
  border-radius: 10px 10px 3px 3px;
  /* the frame is furniture, not laboratory: warm wood on every side */
  background: linear-gradient(180deg, #1A120C, #0B0805);
  box-shadow:
    0 0 0 3px #2A1C12,
    0 0 0 4px rgba(255, 214, 150, .10),
    0 12px 26px rgba(0, 0, 0, .55);
  overflow: hidden;
  isolation: isolate;
}
.shelf-art { position: absolute; inset: 0; width: 100%; height: 176px; display: block; }

/* --- the Centre Case, above shelf 15 --------------------------------- */
.centre-case {
  position: relative;
  height: 210px;
  margin: 4px 0 34px;
  border-radius: 6px;
  background: #050608;
  box-shadow:
    0 0 0 3px #2A1C12,
    0 0 0 4px rgba(255, 214, 150, .16),
    0 16px 34px rgba(0, 0, 0, .6);
  overflow: hidden;
}
.centre-case .shelf-art { height: 210px; }
.centre-case.owned { box-shadow: 0 0 0 3px #3A2716, 0 0 0 4px rgba(255,222,160,.3), 0 16px 40px rgba(0,0,0,.66); }
.centre-cap {
  position: absolute; left: 12px; bottom: 10px; right: 96px;
  line-height: 1.25; pointer-events: none;
}
.centre-cap b { display: block; font-size: .86rem; color: #FFE99C; }
.centre-cap span { font-size: .7rem; color: var(--ink-dim); }
.centre-btn {
  position: absolute; right: 10px; bottom: 10px;
  background: none; border: 0; padding: 0;
  font-family: var(--font); cursor: pointer;
  touch-action: manipulation;
}

.promote-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px;
  margin-top: 12px; max-height: 210px; overflow-y: auto;
}
.promote-opt {
  display: grid; justify-items: center; gap: 2px;
  padding: 5px 3px 6px;
  border-radius: 10px;
  background: rgba(255,255,255,.04);
  border: 1.5px solid rgba(255,255,255,.1);
  color: var(--ink); font-family: var(--font); cursor: pointer;
}
.promote-opt.on { border-color: var(--gold); background: rgba(255,216,77,.12); }
.promote-opt canvas { width: 58px; height: 58px; }
.promote-opt span {
  font-size: .58rem; line-height: 1.15; text-align: center;
  color: var(--ink-dim);
}

/* --- sharing ---------------------------------------------------------- */
.share-row {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 6px;
  margin: 14px auto 0;
  max-width: 340px;
}
.share-btn {
  padding: 7px 13px;
  border-radius: 999px;
  background: rgba(255,255,255,.07);
  border: 1.5px solid rgba(255,255,255,.16);
  color: var(--ink-dim);
  font-family: var(--font); font-weight: 800; font-size: .74rem;
  cursor: pointer;
  touch-action: manipulation;
  transition: transform 120ms ease, filter 160ms ease;
}
.share-btn:active { transform: translateY(2px); }
.share-btn.hero {
  flex: 1 0 100%;
  padding: 11px 18px;
  font-size: .88rem;
  color: #08122E;
  background: linear-gradient(180deg, #FFE99C, #FFC63F);
  border-color: rgba(255,255,255,.5);
  box-shadow: 0 4px 0 #A87A0C;
}
.share-btn.hero:active { box-shadow: 0 1px 0 #A87A0C; }

.win-share {
  display: block;
  margin: 10px auto 0;
  padding: 9px 18px;
  border-radius: 999px;
  background: rgba(255,255,255,.07);
  border: 1.5px solid rgba(255,255,255,.18);
  color: var(--ink);
  font-family: var(--font); font-weight: 800; font-size: .8rem;
  cursor: pointer;
  touch-action: manipulation;
}
.win-share:active { transform: translateY(2px); }

/* whatever is in the vitrine, standing beside the logo every launch */
.title-specimen {
  display: block;
  width: 84px; height: 92px;
  margin: 2px auto -4px;
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, .6));
  animation: titleSpec 620ms ease both;
}
/* `display: block` silently beats the HTML `hidden` attribute, and this
 * canvas ships hidden until a specimen is promoted to the centre case. It
 * was therefore holding 92px of EMPTY space open on every first run -- the
 * mystery gap between the wordmark and the tagline, and most of the reason
 * the screen overflowed a phone at all. Any rule that sets `display` on an
 * element that also uses `hidden` needs this partner. */
.title-specimen[hidden] { display: none; }
@keyframes titleSpec { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.shelf.s-sealed { filter: saturate(.5); }
/* the lamp landing: a brief warm flare over the whole band */
.shelf.just-lit::after {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(60% 90% at 14% 14%, rgba(255,196,107,.55), rgba(255,196,107,0) 70%);
  animation: shelfLight 900ms ease-out forwards;
  pointer-events: none;
}
@keyframes shelfLight { 0% { opacity: 0; } 22% { opacity: 1; } 100% { opacity: 0; } }

/* --- the brass world plate, hanging off the front edge --------------- */
.shelf-plate {
  position: absolute;
  left: 8px; bottom: 6px;
  display: flex; align-items: center; gap: 7px;
  max-width: calc(100% - 176px);
  padding: 4px 10px;
  border-radius: 3px;
  background: linear-gradient(180deg, #E4C173, #A8802E 52%, #6E4E12);
  box-shadow: 0 2px 5px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,240,190,.6);
  color: #241703;
  font-size: .7rem; font-weight: 800;
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap; overflow: hidden;
}
.shelf-plate i { font-style: normal; opacity: .62; }
.shelf-plate span { overflow: hidden; text-overflow: ellipsis; }
.shelf-plate b { color: #4A3208; }
.shelf-plate b.done { color: #7A5A00; font-size: .84rem; }
.shelf-plate .sealed-cap { font-weight: 700; opacity: .72; text-transform: none; letter-spacing: 0; }

/* --- niches: invisible hit targets standing over the canvas ---------- */
.niche, .fitting {
  position: absolute;
  background: none; border: 0; padding: 0; margin: 0;
  color: inherit; font-family: var(--font); cursor: pointer;
  display: grid; place-items: end center;
  touch-action: manipulation;
}
.niche { top: 12px; height: 116px; width: 30%; }
.n-find { left: 15%; }
.n-prize { left: 57%; }
.niche.previewing { filter: drop-shadow(0 0 12px rgba(255,214,120,.8)); }

.fitting { bottom: 6px; height: 26px; width: 74px; place-items: center; }
.f-lamp { right: 84px; }
.f-case { right: 8px; }

.tag {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(6, 8, 18, .78);
  border: 1px solid rgba(255, 216, 77, .42);
  color: #FFE99C;
  font-size: .68rem; font-weight: 800;
  white-space: nowrap;
}
.tag i {
  font-style: normal; display: inline-block;
  width: 13px; height: 13px; font-size: 0;
  background: url("assets/img/ui/coin-au.png") center / contain no-repeat;
}
/* A sealed niche names the world that opens it, and those names run long --
 * "sealed until The Flotation Plant" is 170px of nowrap text inside a 112px
 * button, so at phone width it ran off its own parent AND off the screen.
 * Price tags stay on one line (they are three characters); the sealed
 * captions wrap. Three short lines you can read beats one line whose end is
 * somewhere past the bezel. */
.niche .tag.locked {
  max-width: 100%;
  white-space: normal;
  line-height: 1.15;
  text-align: center;
  padding: 4px 8px;
}
/* a gate is a target, not a wall -- so it stays legible and says the number */
.tag.locked {
  border-color: rgba(169, 183, 218, .32);
  color: var(--ink-dim);
  background: rgba(6, 8, 18, .68);
}
.tag.ghosted { border-color: rgba(255,216,77,.2); color: rgba(255,233,156,.55); font-weight: 700; }

/* the engraved formula plate the Case pays for */
.plaque {
  padding: 2px 7px;
  border-radius: 2px;
  background: linear-gradient(180deg, #D8B76A, #8A6720);
  color: #2A1C04;
  font-size: .62rem; font-weight: 800; letter-spacing: .04em;
  box-shadow: 0 1px 3px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,240,190,.55);
}

/* --- the preview sheet ----------------------------------------------- */
.cab-sheet {
  position: absolute;
  left: 50%; bottom: 0;
  transform: translate(-50%, 100%);
  width: min(100%, var(--app-max));
  padding: 8px 16px calc(16px + var(--safe-b));
  background: linear-gradient(180deg, rgba(16, 20, 44, .97), rgba(8, 10, 26, .99));
  border-top: 1.5px solid var(--panel-line);
  border-radius: 20px 20px 0 0;
  box-shadow: 0 -14px 40px rgba(0, 0, 0, .6);
  transition: transform 220ms cubic-bezier(.2, .9, .3, 1);
  z-index: 3;
}
.cab-sheet.shown { transform: translate(-50%, 0); }
.sheet-grip {
  width: 42px; height: 4px; margin: 0 auto 10px;
  border-radius: 999px; background: rgba(255,255,255,.22);
}
.sheet-head { display: flex; gap: 12px; align-items: flex-start; }
.sheet-art {
  width: 132px; height: 132px; flex: 0 0 auto;
  border-radius: 12px;
  background: radial-gradient(70% 70% at 40% 30%, rgba(255,196,107,.14), rgba(0,0,0,.5));
}
.sheet-head h3 { margin: 2px 0 4px; font-size: 1.06rem; }
.sheet-hook { margin: 0; font-size: .8rem; line-height: 1.42; color: var(--ink-dim); }
.sheet-gate {
  margin: 10px 0 0; font-size: .8rem; font-weight: 800; color: var(--gold);
  text-align: center;
}
.sheet-actions { display: flex; gap: 10px; margin-top: 12px; }
.sheet-actions .btn { flex: 1; padding: 12px 14px; font-size: .92rem; }
.sheet-actions .btn i {
  font-style: normal; display: inline-block;
  width: 16px; height: 16px; font-size: 0; vertical-align: -3px;
  background: url("assets/img/ui/coin-au.png") center / contain no-repeat;
}
.sheet-actions .btn.poor { filter: grayscale(.7) brightness(.75); }

.lamp-picker { display: grid; gap: 6px; margin-top: 12px; }
.lamp-opt {
  text-align: left; padding: 8px 12px;
  border-radius: 12px;
  background: rgba(255,255,255,.05);
  border: 1.5px solid rgba(255,255,255,.12);
  color: var(--ink); font-family: var(--font); cursor: pointer;
}
.lamp-opt.on { border-color: var(--gold); background: rgba(255,216,77,.12); }
.lamp-opt b { display: block; font-size: .86rem; }
.lamp-opt span { font-size: .72rem; color: var(--ink-dim); line-height: 1.35; }

.gold-pill.shake { animation: goldShake 360ms ease; }
@keyframes goldShake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-5px); }
  45% { transform: translateX(4px); }
  70% { transform: translateX(-2px); }
}

/* --- the win card's saving line -------------------------------------- */
.win-goal {
  position: relative;
  margin: 10px 0 0;
  padding: 9px 12px 12px;
  border-radius: 12px;
  background: rgba(255,255,255,.05);
  border: 1.5px solid rgba(255,255,255,.12);
  font-size: .78rem; color: var(--ink-dim);
  text-align: center;
}
.win-goal b { color: var(--ink); }
.win-goal .goal-bar {
  position: absolute; left: 10px; right: 10px; bottom: 6px;
  height: 3px; border-radius: 999px;
  background: rgba(255,255,255,.14);
  display: block;
}
.win-goal .goal-bar::after {
  content: ""; position: absolute; inset: 0 auto 0 0;
  width: var(--p, 0%);
  border-radius: 999px;
  background: linear-gradient(90deg, #FFD84D, #FF9C5B);
}
/* when the purse already covers it, the line stops reporting and starts asking */
.win-goal.ready {
  cursor: pointer;
  color: #08122E;
  background: linear-gradient(180deg, #FFE99C, #FFC63F);
  border-color: rgba(255,255,255,.5);
  font-weight: 800;
}
.win-goal.ready b { color: #08122E; }

@media (max-width: 380px) {
  .shelf { height: 168px; }
  .shelf-art { height: 168px; }
  .sheet-art { width: 104px; height: 104px; }
  .fitting { width: 66px; }
  .f-lamp { right: 74px; }
}

/* ---- the bench, second axis -------------------------------------------
 * Prep sells a forged special (radio row) AND the Night Shift (its own
 * toggle), so the two rows must not read as one radio group of five. The
 * shift sits on its own line, wider and quieter; the Fusion Core is the
 * expensive one and says so in cyan rather than in the bench's gold. */
.prep-row-2 { margin-top: 8px; }
.prep-chip.prep-shift {
  flex-direction: row; justify-content: center; gap: 10px;
  padding: 9px 6px;
  border-style: solid;
  border-color: rgba(160,200,255,.4);
  background: rgba(120,170,255,.07);
}
.prep-chip.prep-shift .pc-glyph {
  font-size: 1.15rem; font-weight: 900; color: #BFE0FF;
  text-shadow: 0 0 12px rgba(140,200,255,.6);
}
.prep-chip.prep-shift.armed {
  border-color: rgba(160,200,255,.95);
  background: rgba(120,170,255,.2);
}
.prep-chip.prep-core {
  border-color: rgba(155,231,255,.55);
  background: rgba(120,200,255,.08);
}
.prep-chip.prep-core b { color: #C9F0FF; }
.prep-chip.prep-core.armed {
  border-color: rgba(155,231,255,1);
  background: rgba(120,200,255,.22);
}
/* four chips on one row need to survive a narrow phone */
.prep-row .prep-chip { min-width: 0; }
.prep-row .prep-chip b { text-align: center; line-height: 1.15; }
@media (max-width: 380px) {
  .prep-chip .pc-img { width: 36px; height: 36px; }
  .prep-chip b { font-size: .68rem; }
  .prep-chip .pc-cost { font-size: .62rem; }
}
