/* ==========================================================================
   WordFall
   --------------------------------------------------------------------------
   1.  Tokens
   2.  Base & primitives
   3.  Screens — start
   4.  HUD
   5.  Bombs
   6.  Board, tiles, effects
   7.  Overlays & panels
   8.  Toasts
   9.  Responsive & motion preferences
   ========================================================================== */

/* ------------------------------------------------------------------ *
 * 1. Tokens                                                          *
 * ------------------------------------------------------------------ */

:root {
  /* Surfaces: cool ink, so the warm tiles read as physical objects on it. */
  --ink-900: #0d0f16;
  --ink-800: #12141c;
  --ink-700: #1a1d28;
  --ink-600: #232735;
  --ink-500: #2f3446;
  --line:    #333a4d;

  --text:        #eef0f6;
  --text-muted:  #98a0b5;
  --text-dim:    #6b7488;

  /* Tile face: aged ivory with a warm shadow, like a real letter tile. */
  --tile-top:    #fdf4e4;
  --tile-face:   #f2e6cf;
  --tile-edge:   #d9c8a8;
  --tile-text:   #23252f;
  --tile-value:  #8d8064;

  --rock-top:    #5b6379;
  --rock-face:   #4a5165;
  --rock-edge:   #5e6a80;

  /* Empty cell wells. Tokenised because the light theme needs them darker
     than the page, where the dark theme needs them lighter. */
  --well:        rgba(255,255,255,.028);
  --well-hover:  rgba(255,255,255,.055);
  --well-line:   rgba(255,255,255,.03);

  /* Accents */
  --gold:        #f2b544;
  --gold-soft:   #f7d18a;
  --mint:        #4fd1a5;
  --mint-soft:   #8ce8c8;
  --ember:       #ff6b52;
  --ember-soft:  #ff9d8b;
  --violet:      #8b7cf6;

  --danger:      #ff5b5b;

  /* Elevation. Layered rather than single-blur — reads softer. */
  --shadow-sm: 0 1px 2px rgba(0,0,0,.28);
  --shadow-md: 0 2px 4px rgba(0,0,0,.24), 0 6px 16px rgba(0,0,0,.28);
  --shadow-lg: 0 8px 24px rgba(0,0,0,.34), 0 24px 60px rgba(0,0,0,.42);

  --radius-sm: 8px;
  --radius:    14px;
  --radius-lg: 22px;

  /* Board geometry. Every tile and overlay position derives from these two,
     so the board stays square and aligned at any viewport size. */
  --cols: 5;
  --rows: 7;
  --gap: 6px;
  --cell: clamp(44px, min(15.5vw, 8.2vh), 66px);
  --board-w: calc(var(--cols) * var(--cell) + (var(--cols) - 1) * var(--gap));
  --board-h: calc(var(--rows) * var(--cell) + (var(--rows) - 1) * var(--gap));

  /* Motion. One easing curve throughout: a gentle overshoot-free ease-out. */
  --ease: cubic-bezier(.22,.61,.36,1);
  --ease-soft: cubic-bezier(.4,0,.2,1);
  --step-time: 150ms;

  --font: ui-rounded, "SF Pro Rounded", -apple-system, BlinkMacSystemFont,
          "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  color-scheme: dark;
}

/* Warm paper variant for anyone whose system runs light. */
@media (prefers-color-scheme: light) {
  :root {
    --ink-900: #e7e3da;
    --ink-800: #efece4;
    --ink-700: #f7f4ee;
    --ink-600: #fffdf8;
    --ink-500: #e4dfd3;
    --line:    #d6cfc0;

    --text:       #23252f;
    --text-muted: #6b6656;
    --text-dim:   #938d7c;

    --tile-top:   #fffdf7;
    --tile-face:  #fbf1dc;
    --tile-edge:  #ddcfae;
    --tile-text:  #23252f;
    --tile-value: #8d8264;

    --rock-top:   #b9b2a0;
    --rock-face:  #a49b86;
    --rock-edge:  #8d8471;

    --well:       rgba(90,78,54,.075);
    --well-hover: rgba(90,78,54,.13);
    --well-line:  rgba(90,78,54,.07);

    --gold:       #d9962a;
    --mint:       #17a67a;
    --ember:      #e2503a;

    --shadow-sm: 0 1px 2px rgba(80,70,50,.14);
    --shadow-md: 0 2px 4px rgba(80,70,50,.10), 0 6px 16px rgba(80,70,50,.14);
    --shadow-lg: 0 8px 24px rgba(80,70,50,.16), 0 24px 60px rgba(80,70,50,.20);

    color-scheme: light;
  }
}

/* The --*-soft accents are tuned to sit on dark surfaces. On the light
   theme's pale grounds they wash out, so the handful of places that put them
   on a light background get a saturated value instead. Everything painted on
   a coloured surface keeps the soft tone. */
@media (prefers-color-scheme: light) {
  .bombs__hint[data-state="armed"] { color: #c8402a; background: rgba(226,80,58,.12); }
  .word-flash      { color: #0f8f66; text-shadow: 0 4px 24px rgba(255,255,255,.9); }
  .word-flash small { color: #b8791f; }
  .toast           { background: #fffdf8; }

  /* An armed bomb must stay unmistakable: over a pale panel the translucent
     red reads as a tint, and its white label loses contrast. Go solid. */
  .bomb--armed {
    background: linear-gradient(180deg, #ef5a40, #d8462f);
    border-color: #c8402a;
  }
  .bomb--armed .bomb__cost { color: #ffe3dc; }
  .bomb--armed .bomb__glyph .g-hit { fill: rgba(255,255,255,.55); }
  .bomb--armed .bomb__glyph .g-cell { fill: rgba(0,0,0,.14); }
}

/* ------------------------------------------------------------------ *
 * 2. Base & primitives                                               *
 * ------------------------------------------------------------------ */

*, *::before, *::after { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  color: var(--text);
  background: var(--ink-900);
  /* Two offset glows lift the centre of the screen without a flat gradient. */
  background-image:
    radial-gradient(120% 80% at 50% -10%, rgba(139,124,246,.16), transparent 60%),
    radial-gradient(90% 60% at 50% 110%, rgba(79,209,165,.10), transparent 60%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
  overscroll-behavior: none;
  touch-action: manipulation;
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

[hidden] { display: none !important; }

.app {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: max(10px, env(safe-area-inset-top)) 12px max(10px, env(safe-area-inset-bottom));
  /* The board is sized to fit by _fitBoard(); this only catches viewports so
     short that even the minimum cell size cannot fit. */
  overflow-y: auto;
}

.screen {
  width: 100%;
  max-width: 460px;
  animation: screen-in .38s var(--ease) both;
}

@keyframes screen-in {
  from { opacity: 0; transform: translateY(10px) scale(.985); }
  to   { opacity: 1; transform: none; }
}

/* --- Buttons --- */

.btn {
  appearance: none;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 12px 26px;
  font: inherit;
  font-weight: 650;
  font-size: .95rem;
  letter-spacing: .01em;
  cursor: pointer;
  transition: transform .16s var(--ease), background-color .2s, box-shadow .2s, opacity .2s;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}

.btn:active { transform: scale(.965); }
.btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

.btn--primary {
  background: linear-gradient(180deg, var(--gold-soft), var(--gold));
  color: #2a1e06;
  box-shadow: 0 1px 0 rgba(255,255,255,.4) inset, var(--shadow-md);
}
.btn--primary:hover { box-shadow: 0 1px 0 rgba(255,255,255,.5) inset, var(--shadow-lg); }

.btn--ghost {
  background: transparent;
  border-color: var(--line);
  color: var(--text-muted);
}
.btn--ghost:hover { color: var(--text); border-color: var(--text-dim); background: rgba(255,255,255,.04); }

.btn--lg { padding: 15px 46px; font-size: 1.05rem; }

.icon-btn {
  appearance: none;
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: rgba(255,255,255,.03);
  color: var(--text-muted);
  cursor: pointer;
  transition: color .18s, border-color .18s, background-color .18s, transform .16s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.icon-btn:hover { color: var(--text); border-color: var(--text-dim); }
.icon-btn:active { transform: scale(.92); }
.icon-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.icon-btn svg { width: 19px; height: 19px; fill: currentColor; stroke: currentColor; }

/* The sound button swaps glyphs from its pressed state. */
#btn-sound .icon-sound-off { display: none; }
#btn-sound[aria-pressed="true"] .icon-sound-on { display: none; }
#btn-sound[aria-pressed="true"] .icon-sound-off { display: block; }
#btn-sound[aria-pressed="true"] { color: var(--text-dim); }

/* ------------------------------------------------------------------ *
 * 3. Start screen                                                    *
 * ------------------------------------------------------------------ */

.start { text-align: center; padding: 20px 8px; }

.wordmark {
  margin: 0 0 14px;
  font-size: clamp(2.6rem, 13vw, 3.9rem);
  font-weight: 800;
  letter-spacing: .1em;
  line-height: 1;
}
.wordmark__word { color: var(--text); }
.wordmark__fall {
  background: linear-gradient(180deg, var(--gold-soft), var(--gold));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* Nudged down a hair — the mark falls, like the tiles. */
  display: inline-block;
  transform: translateY(.055em);
}

.start__tagline {
  margin: 0 auto 26px;
  max-width: 21em;
  color: var(--text-muted);
  font-size: .98rem;
  line-height: 1.6;
}

.start__best {
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  padding: 9px 20px;
  margin-bottom: 26px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(255,255,255,.03);
}
.start__best-label {
  font-size: .7rem; text-transform: uppercase; letter-spacing: .13em;
  color: var(--text-dim);
}
.start__best-score {
  font-size: 1.3rem; font-weight: 750; color: var(--gold);
  font-variant-numeric: tabular-nums;
}

.start__actions { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.back-link { margin: 28px 0 0; font-size: .9rem; }
.back-link a { color: var(--text-muted); text-decoration: none; }
.back-link a:hover, .back-link a:focus-visible { color: var(--gold); }

/* ------------------------------------------------------------------ *
 * 4. HUD                                                            *
 * ------------------------------------------------------------------ */

.screen--game { display: flex; flex-direction: column; gap: 10px; }

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

.hud__stats { display: flex; align-items: baseline; gap: 18px; }

.stat { display: flex; flex-direction: column; gap: 1px; }
.stat__label {
  font-size: .62rem; text-transform: uppercase; letter-spacing: .14em;
  color: var(--text-dim);
}
.stat__value {
  font-size: 1.7rem; font-weight: 750; line-height: 1;
  color: var(--text); font-variant-numeric: tabular-nums;
  transition: color .3s;
}
.stat__value--muted { font-size: 1.05rem; color: var(--text-muted); align-self: flex-end; }

/* Score briefly takes on the accent when it changes. */
.stat--score.is-bumped .stat__value { color: var(--mint); animation: score-bump .42s var(--ease); }
@keyframes score-bump {
  0%   { transform: none; }
  35%  { transform: scale(1.16); }
  100% { transform: none; }
}
.stat--score.is-spent .stat__value { color: var(--ember); }

/* A debit is the mirror of the gain bump: the score dips instead of swelling,
   so the two are never mistaken for each other at a glance. */
.stat--score.is-debited .stat__value { animation: score-debit .45s var(--ease); }
@keyframes score-debit {
  0%   { transform: none; }
  35%  { transform: scale(.86) translateY(2px); }
  100% { transform: none; }
}

.hud__tools { display: flex; gap: 7px; }

/* ------------------------------------------------------------------ *
 * 5. Bombs                                                           *
 * ------------------------------------------------------------------ *
 * The central usability fix. Each button carries four signals at once:
 *   - a glyph of the exact blast footprint, generated from the same
 *     function the engine detonates with;
 *   - the price;
 *   - when unaffordable, the shortfall and a progress fill;
 *   - when armed, a lit border, a badge and a pulse.
 */

.bombs { display: flex; flex-direction: column; gap: 7px; }

.bombs__rail {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 7px;
}

.bomb {
  position: relative;
  appearance: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 9px 4px 8px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: linear-gradient(180deg, var(--ink-700), var(--ink-800));
  color: var(--text-muted);
  font: inherit;
  cursor: pointer;
  overflow: hidden;
  transition: transform .16s var(--ease), border-color .2s, color .2s,
              background-color .2s, box-shadow .2s, opacity .2s;
  -webkit-tap-highlight-color: transparent;
}

.bomb:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.bomb:active { transform: scale(.955); }

.bomb__glyph { display: block; line-height: 0; }
.bomb__glyph svg { width: 30px; height: 22px; display: block; }

/* Glyph parts, coloured through currentColor-independent tokens so the
   footprint stays legible in every button state. */
.bomb__glyph .g-cell { fill: rgba(255,255,255,.09); }
.bomb__glyph .g-hit  { fill: var(--text-dim); transition: fill .2s; }
.bomb__glyph .g-core { fill: var(--text-muted); transition: fill .2s; }

.bomb__name {
  font-size: .68rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase;
}

.bomb__cost {
  display: flex; align-items: center; gap: 3px;
  font-size: .72rem; font-weight: 700;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  transition: color .2s;
}
.bomb__cost { letter-spacing: .01em; }

/* Progress toward affording this bomb, as a quiet fill behind the content. */
.bomb__progress {
  position: absolute; inset: auto 0 0 0;
  height: 2.5px;
  background: var(--text-dim);
  opacity: .5;
  transform-origin: left;
  transform: scaleX(0);
  transition: transform .4s var(--ease);
}

/* -- Locked: dimmed, and explicit about what is still needed. The button is
   also `disabled`, so this state has to carry the whole message on its own —
   there is no refused tap to explain it any more. -- */
.bomb--locked { opacity: .45; cursor: not-allowed; }
.bomb--locked .bomb__cost { color: var(--text-dim); }
.bomb:disabled { pointer-events: none; }
.bomb:disabled .bomb__glyph .g-core { fill: var(--text-dim); }

.bomb__short {
  position: absolute; top: 4px; right: 5px;
  font-size: .56rem; font-weight: 750; letter-spacing: .02em;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* -- Ready: affordable but not armed. Reads as live without shouting. -- */
.bomb--ready {
  color: var(--text);
  border-color: rgba(242,181,68,.4);
  background: linear-gradient(180deg, var(--ink-600), var(--ink-700));
}
.bomb--ready .bomb__cost  { color: var(--gold); }
.bomb--ready .bomb__glyph .g-hit  { fill: rgba(242,181,68,.5); }
.bomb--ready .bomb__glyph .g-core { fill: var(--gold); }
.bomb--ready:hover { border-color: var(--gold); transform: translateY(-1px); }

/* First moment a bomb becomes affordable: one attention-getting sweep. */
.bomb--just-unlocked { animation: bomb-unlock 1.1s var(--ease) 2; }
@keyframes bomb-unlock {
  0%, 100% { box-shadow: 0 0 0 0 rgba(242,181,68,0); }
  40%      { box-shadow: 0 0 0 5px rgba(242,181,68,.22); }
}

/* -- Armed: unmistakable. -- */
.bomb--armed {
  color: #fff;
  border-color: var(--ember);
  background: linear-gradient(180deg, rgba(255,107,82,.28), rgba(255,107,82,.12));
  box-shadow: 0 0 0 1px rgba(255,107,82,.4), 0 4px 18px rgba(255,107,82,.3);
  animation: bomb-armed 1.5s ease-in-out infinite;
}
@keyframes bomb-armed {
  0%, 100% { box-shadow: 0 0 0 1px rgba(255,107,82,.4), 0 4px 18px rgba(255,107,82,.26); }
  50%      { box-shadow: 0 0 0 1px rgba(255,107,82,.6), 0 4px 26px rgba(255,107,82,.5); }
}
.bomb--armed .bomb__cost { color: var(--ember-soft); }
.bomb--armed .bomb__glyph .g-hit  { fill: var(--ember-soft); }
.bomb--armed .bomb__glyph .g-core { fill: #fff; }
.bomb--armed .bomb__progress { display: none; }

.bomb__badge {
  position: absolute; top: 0; left: 0; right: 0;
  padding: 2px 0 2.5px;
  font-size: .5rem; font-weight: 800; letter-spacing: .14em;
  text-align: center;
  background: var(--ember);
  color: #fff;
}
.bomb:not(.bomb--armed) .bomb__badge { display: none; }

/* The one comped bomb: its price stays visible, struck through, so the waiver
   reads as a discount on a real cost rather than the bomb simply being free. */
.bomb--free .bomb__cost { text-decoration: line-through; opacity: .6; }
.bomb--free .bomb__badge { background: var(--gold); color: #1b1d27; }
.bomb--armed { padding-top: 18px; }

/* Paid for: the button empties out as its price leaves for the score. */
.bomb--spent { animation: bomb-spent .6s var(--ease); }
@keyframes bomb-spent {
  0%   { transform: scale(1); }
  22%  { transform: scale(.9); filter: brightness(1.6); }
  100% { transform: scale(1); }
}

/* Refused tap: a short shake, no colour change — colour would read as armed. */
.bomb--refused { animation: bomb-shake .34s var(--ease-soft); }
@keyframes bomb-shake {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-4px); }
  55%      { transform: translateX(3px); }
  80%      { transform: translateX(-1.5px); }
}

/* --- Hint strip: says what an armed bomb is waiting for. --- */

.bombs__hint {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 26px;
  padding: 4px 12px;
  border-radius: var(--radius-sm);
  font-size: .74rem; line-height: 1.35;
  color: var(--text-dim);
  text-align: center;
  transition: color .25s, background-color .25s;
}

.bombs__hint[data-state="armed"] {
  color: var(--ember-soft);
  background: rgba(255,107,82,.11);
  animation: hint-in .3s var(--ease);
}
@keyframes hint-in { from { opacity: 0; transform: translateY(-3px); } }

.bombs__cancel {
  appearance: none; border: 1px solid currentColor; background: none;
  color: inherit; font: inherit; font-size: .68rem; font-weight: 700;
  padding: 2px 9px; border-radius: 999px; cursor: pointer; flex: none;
  transition: background-color .18s;
}
.bombs__cancel:hover { background: rgba(255,107,82,.16); }

/* ------------------------------------------------------------------ *
 * 6. Board, tiles, effects                                           *
 * ------------------------------------------------------------------ */

.stage { display: flex; flex-direction: column; align-items: center; gap: 8px; }

/* --- Queue: the strip above the board holding the next tile. --- */

.queue { width: var(--board-w); }

.queue__cells {
  display: grid;
  grid-template-columns: repeat(var(--cols), var(--cell));
  gap: var(--gap);
}

.queue__cell {
  height: calc(var(--cell) * .62);
  border-radius: var(--radius-sm);
  border: 1.5px dashed var(--line);
  display: grid; place-items: center;
  font-size: calc(var(--cell) * .34);
  font-weight: 750;
  color: var(--text-dim);
  cursor: pointer;
  transition: border-color .2s, color .2s, background-color .2s, transform .16s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.queue__cell:hover:not(.queue__cell--blocked) { border-color: var(--text-dim); }

.queue__cell--active {
  border-style: solid;
  border-color: var(--gold);
  color: var(--gold);
  background: rgba(242,181,68,.1);
}

.queue__cell--blocked { border-color: transparent; cursor: default; opacity: .35; }

/* --- Board --- */

.board {
  position: relative;
  width: var(--board-w);
  height: var(--board-h);
}

.board__cells,
.board__blast,
.board__tiles,
.board__fx { position: absolute; inset: 0; }

.board__cells {
  display: grid;
  grid-template-columns: repeat(var(--cols), var(--cell));
  grid-template-rows: repeat(var(--rows), var(--cell));
  gap: var(--gap);
}

/* Empty wells. Kept very low contrast so the tiles carry the composition. */
.cell {
  border-radius: var(--radius-sm);
  background: var(--well);
  box-shadow: inset 0 0 0 1px var(--well-line);
  cursor: pointer;
  transition: background-color .18s;
}
.cell:hover { background: var(--well-hover); }

/* The top row turns red once it is occupied — the stack is at the ceiling. */
.cell--capped { box-shadow: inset 0 0 0 1.5px rgba(255,91,91,.5); }

/* All input is handled by the cell wells underneath; the tile, blast and
   effect layers sit on top and must not swallow taps. */
.board__blast, .board__tiles, .board__fx { pointer-events: none; }

/* --- Blast preview: where an armed bomb will do its damage. --- */

.blast-cell {
  --x: 0px; --y: 0px;
  position: absolute;
  width: var(--cell); height: var(--cell);
  border-radius: var(--radius-sm);
  border: 2px dashed var(--ember);
  background:
    repeating-linear-gradient(45deg,
      rgba(255,107,82,.16) 0 5px,
      rgba(255,107,82,.04) 5px 10px);
  animation: blast-preview 1.15s ease-in-out infinite;
  /* Positioned from the same grid maths as the tiles, so it lines up exactly. */
  transform: translate(var(--x), var(--y));
  transition: transform var(--step-time) var(--ease);
}
@keyframes blast-preview {
  0%, 100% { opacity: .55; }
  50%      { opacity: 1; }
}

/* --- Tiles --- */

.tile {
  --x: 0px; --y: 0px;
  position: absolute;
  width: var(--cell); height: var(--cell);
  border-radius: var(--radius-sm);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  background: linear-gradient(170deg, var(--tile-top), var(--tile-face) 55%, var(--tile-edge));
  color: var(--tile-text);
  box-shadow:
    0 1px 0 rgba(255,255,255,.7) inset,
    0 -2px 0 rgba(0,0,0,.09) inset,
    var(--shadow-sm);
  user-select: none;
  /* The single most important line for feel: tiles glide between cells
     rather than teleporting when the grid repaints. */
  transform: translate(var(--x), var(--y));
  transition: transform var(--step-time) var(--ease), opacity .2s;
  will-change: transform;
}

.tile__letter {
  font-size: calc(var(--cell) * .46);
  font-weight: 750;
  line-height: 1;
  letter-spacing: .01em;
}

.tile__value {
  position: absolute;
  right: calc(var(--cell) * .1);
  bottom: calc(var(--cell) * .06);
  font-size: calc(var(--cell) * .17);
  font-weight: 700;
  color: var(--tile-value);
  font-variant-numeric: tabular-nums;
}

.tile--new { animation: tile-in .28s var(--ease); }
@keyframes tile-in {
  from { opacity: 0; transform: translate(var(--x), calc(var(--y) - 10px)) scale(.86); }
}

/* The live tile: warm halo so the eye never loses it. */
.tile--falling {
  background: linear-gradient(170deg, #fff0cf, var(--gold-soft) 55%, var(--gold));
  box-shadow:
    0 1px 0 rgba(255,255,255,.8) inset,
    0 -2px 0 rgba(0,0,0,.1) inset,
    0 0 0 1.5px rgba(242,181,68,.55),
    0 4px 20px rgba(242,181,68,.32);
  z-index: 3;
}

/* A falling rock keeps its stone face but still needs the live-tile halo,
   or the player loses track of which piece they are steering. */
.tile--rock.tile--falling {
  background: linear-gradient(170deg, var(--rock-top), var(--rock-face) 55%, var(--rock-edge));
  box-shadow:
    0 1px 0 rgba(255,255,255,.12) inset,
    0 -2px 0 rgba(0,0,0,.25) inset,
    0 0 0 2px var(--gold),
    0 4px 20px rgba(242,181,68,.34);
}

.tile--landing { animation: tile-land .26s var(--ease); }
@keyframes tile-land {
  0%   { transform: translate(var(--x), var(--y)) scaleY(1); }
  40%  { transform: translate(var(--x), var(--y)) scale(1.06, .9); }
  100% { transform: translate(var(--x), var(--y)) scale(1, 1); }
}

.tile--rock {
  background: linear-gradient(170deg, var(--rock-top), var(--rock-face) 55%, var(--rock-edge));
  box-shadow: 0 1px 0 rgba(255,255,255,.12) inset, 0 -2px 0 rgba(0,0,0,.25) inset, var(--shadow-sm);
}
/* Rocks get a chiselled face so they read as "not a letter" instantly. */
.tile--rock::after {
  content: "";
  position: absolute; inset: 22%;
  border-radius: 3px;
  background:
    linear-gradient(135deg, rgba(255,255,255,.14) 0 45%, transparent 45%),
    linear-gradient(315deg, rgba(0,0,0,.2) 0 40%, transparent 40%);
}

/* Word about to clear: lights up, then lifts away. */
.tile--clearing {
  animation: tile-clear .42s var(--ease) forwards;
  z-index: 4;
}
@keyframes tile-clear {
  0%   { background: linear-gradient(170deg, var(--tile-top), var(--tile-face)); }
  30%  {
    background: linear-gradient(170deg, var(--mint-soft), var(--mint));
    box-shadow: 0 0 0 2px rgba(79,209,165,.6), 0 0 26px rgba(79,209,165,.55);
    transform: translate(var(--x), var(--y)) scale(1.08);
  }
  100% {
    background: linear-gradient(170deg, var(--mint-soft), var(--mint));
    transform: translate(var(--x), var(--y)) scale(.4);
    opacity: 0;
  }
}

/* Caught in a blast. */
.tile--blasting {
  --spin: 12deg;   /* ui.js randomises this per tile */
  animation: tile-blast .5s var(--ease) forwards;
  z-index: 4;
}
@keyframes tile-blast {
  0%  { transform: translate(var(--x), var(--y)) scale(1); }
  22% {
    transform: translate(var(--x), var(--y)) scale(1.14);
    background: linear-gradient(170deg, var(--ember-soft), var(--ember));
    box-shadow: 0 0 0 2px rgba(255,107,82,.7), 0 0 30px rgba(255,107,82,.6);
  }
  100% {
    transform: translate(var(--x), var(--y)) scale(.25) rotate(var(--spin, 12deg));
    background: linear-gradient(170deg, var(--ember-soft), var(--ember));
    opacity: 0;
  }
}

/* The fuse badge on the falling tile while a bomb is armed. Ties the button
   to the tile to the blast zone, which is the link testers never made. */
.tile__fuse {
  position: absolute;
  top: calc(var(--cell) * -.14);
  left: calc(var(--cell) * -.1);
  width: calc(var(--cell) * .42);
  height: calc(var(--cell) * .42);
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #4c5468, #1b1f2b 70%);
  box-shadow: 0 0 0 2px rgba(255,107,82,.85), 0 0 14px rgba(255,107,82,.7);
  display: grid; place-items: center;
  z-index: 5;
  animation: fuse-pulse 1.05s ease-in-out infinite;
}
.tile__fuse::after {
  content: "";
  width: 26%; height: 26%;
  border-radius: 50%;
  background: var(--gold-soft);
  box-shadow: 0 0 8px 3px rgba(242,181,68,.9);
  animation: fuse-spark .55s ease-in-out infinite alternate;
}
@keyframes fuse-pulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(255,107,82,.75), 0 0 12px rgba(255,107,82,.55); }
  50%      { box-shadow: 0 0 0 3px rgba(255,107,82,1),   0 0 20px rgba(255,107,82,.85); }
}
@keyframes fuse-spark { to { transform: scale(1.45); opacity: .75; } }

/* --- Effects layer --- */

/* Expanding ring from the epicentre of a detonation. */
.shockwave {
  --x: 0px; --y: 0px;
  position: absolute;
  width: var(--cell); height: var(--cell);
  border-radius: 50%;
  border: 2px solid var(--ember);
  transform: translate(var(--x), var(--y));
  animation: shockwave .55s var(--ease) forwards;
}
@keyframes shockwave {
  0%   { opacity: .9; scale: .5; }
  100% { opacity: 0;  scale: 4.2; }
}

/* Points floating up from where they were scored. */
.float-score {
  --x: 0px; --y: 0px;
  position: absolute;
  transform: translate(var(--x), var(--y));
  font-size: calc(var(--cell) * .34);
  font-weight: 800;
  color: var(--mint);
  text-shadow: 0 2px 10px rgba(0,0,0,.5);
  animation: float-score 1s var(--ease) forwards;
  white-space: nowrap;
}
@keyframes float-score {
  0%   { opacity: 0; translate: 0 6px; }
  18%  { opacity: 1; translate: 0 0; }
  100% { opacity: 0; translate: 0 -46px; }
}

/* The word itself, called out over the board when it scores. */
.word-flash {
  position: absolute;
  left: 50%; top: 42%;
  translate: -50% -50%;
  font-size: clamp(1.5rem, 7vw, 2.1rem);
  font-weight: 800;
  letter-spacing: .14em;
  color: var(--mint-soft);
  text-shadow: 0 4px 30px rgba(79,209,165,.6), 0 2px 12px rgba(0,0,0,.6);
  animation: word-flash 1.05s var(--ease) forwards;
  white-space: nowrap;
}
.word-flash small {
  display: block;
  font-size: .42em;
  letter-spacing: .1em;
  color: var(--gold);
  margin-top: 4px;
}
@keyframes word-flash {
  0%   { opacity: 0; scale: .82; }
  20%  { opacity: 1; scale: 1; }
  70%  { opacity: 1; scale: 1; }
  100% { opacity: 0; scale: 1.08; translate: -50% -80%; }
}

.controls-hint {
  margin: 0;
  text-align: center;
  font-size: .7rem;
  color: var(--text-dim);
  transition: opacity .4s;
}
.controls-hint.is-faded { opacity: 0; }

/* ------------------------------------------------------------------ *
 * 7. Overlays & panels                                               *
 * ------------------------------------------------------------------ */

.overlay {
  position: fixed; inset: 0;
  display: grid; place-items: center;
  padding: 20px;
  background: rgba(8,9,14,.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  z-index: 50;
  animation: overlay-in .24s var(--ease);
}
@keyframes overlay-in { from { opacity: 0; } }

.panel {
  width: 100%;
  max-width: 380px;
  max-height: 88vh;
  overflow-y: auto;
  padding: 28px 26px 24px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: linear-gradient(180deg, var(--ink-700), var(--ink-800));
  box-shadow: var(--shadow-lg);
  text-align: center;
  animation: panel-in .3s var(--ease);
}
@keyframes panel-in {
  from { opacity: 0; transform: translateY(14px) scale(.97); }
}

.panel__eyebrow {
  margin: 0 0 4px;
  font-size: .65rem; text-transform: uppercase; letter-spacing: .16em;
  color: var(--text-dim);
}

.panel__title { margin: 0 0 20px; font-size: 1.5rem; font-weight: 750; letter-spacing: .01em; }

.panel__actions { display: flex; flex-direction: column; gap: 10px; margin-top: 22px; }

.result { margin-bottom: 22px; }
.result__score {
  font-size: 3.4rem; font-weight: 800; line-height: 1;
  color: var(--gold); font-variant-numeric: tabular-nums;
}
.result__record {
  margin-top: 8px;
  display: inline-block;
  padding: 4px 14px;
  border-radius: 999px;
  background: rgba(79,209,165,.16);
  color: var(--mint);
  font-size: .72rem; font-weight: 750; letter-spacing: .1em; text-transform: uppercase;
  animation: record-in .5s var(--ease) both .2s;
}
@keyframes record-in { from { opacity: 0; transform: scale(.8); } }

.result__grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 4px; margin: 0;
  padding: 14px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.result__item { display: flex; flex-direction: column; gap: 4px; }
.result__item dt {
  font-size: .6rem; text-transform: uppercase; letter-spacing: .12em; color: var(--text-dim);
}
.result__item dd {
  margin: 0; font-size: 1.05rem; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ *
 * 8. Toasts                                                          *
 * ------------------------------------------------------------------ */

/* Effects that travel between components live above everything and never
   take a pointer event; positions are written in viewport pixels by ui.js. */
.fx-layer {
  position: fixed; inset: 0;
  z-index: 55;
  pointer-events: none;
  overflow: hidden;
}

.spend-chip {
  position: absolute;
  translate: -50% -50%;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--ember);
  color: #fff;
  font-size: .78rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 4px 16px rgba(255,107,82,.45);
  white-space: nowrap;
  will-change: transform, opacity;
  animation: spend-fly .44s var(--ease) forwards;
}

/* Full strength for most of the trip, so the eye can actually follow the
   figure from the button it was spent on to the score it comes out of. */
@keyframes spend-fly {
  0%   { transform: translate(0, 0) scale(1); opacity: 0; }
  14%  { transform: translate(calc(var(--dx) * .14), calc(var(--dy) * .14)) scale(1.06); opacity: 1; }
  72%  { opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)) scale(.7); opacity: 0; }
}

.toasts {
  position: fixed;
  left: 50%; bottom: max(20px, env(safe-area-inset-bottom));
  translate: -50% 0;
  display: flex; flex-direction: column-reverse; align-items: center; gap: 7px;
  z-index: 60;
  pointer-events: none;
}

.toast {
  padding: 9px 17px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--ink-600);
  box-shadow: var(--shadow-md);
  font-size: .8rem; font-weight: 600; color: var(--text);
  white-space: nowrap;
  animation: toast-in .3s var(--ease);
}
.toast--leaving { animation: toast-out .3s var(--ease) forwards; }
.toast--bomb { border-color: var(--gold); color: var(--gold); }

/* The one-off bomb lesson. It carries a full sentence rather than a label,
   so it drops the pill's nowrap and sits wider and squarer than a toast that
   is only confirming something the player already did. */
.toast--tutor {
  white-space: normal;
  max-width: min(320px, calc(100vw - 32px));
  border-radius: 14px;
  padding: 11px 16px;
  text-align: center;
  line-height: 1.35;
  border-color: var(--gold);
  color: var(--gold);
  box-shadow: var(--shadow-md), 0 0 0 3px rgba(242,181,68,.14);
}
.toast--warn { border-color: var(--ember); color: var(--ember); }

@keyframes toast-in  { from { opacity: 0; transform: translateY(10px) scale(.94); } }
@keyframes toast-out { to   { opacity: 0; transform: translateY(-6px) scale(.97); } }

/* ------------------------------------------------------------------ *
 * 9. Responsive & motion preferences                                 *
 * ------------------------------------------------------------------ */

/* Short viewports: shrink the cell and drop the least essential chrome. */
@media (max-height: 700px) {
  :root { --gap: 5px; --cell: clamp(38px, min(14vw, 7.4vh), 56px); }
  .screen--game { gap: 7px; }
  .stat__value { font-size: 1.45rem; }
  .controls-hint { display: none; }
}

@media (max-height: 600px) {
  .bomb { padding: 6px 3px 6px; }
  .bomb__glyph svg { width: 26px; height: 19px; }
}

/* Wide/desktop: give the board a touch more presence. */
@media (min-width: 520px) and (min-height: 760px) {
  :root { --cell: 66px; --gap: 7px; }
}

@media (hover: none) {
  /* Hover styling on touch devices sticks after a tap; suppress it. */
  .cell:hover { background: var(--well); }
  .bomb--ready:hover { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  /* Motion is how an armed bomb announces itself, so replace the pulse with
     a static high-contrast treatment rather than removing the signal. */
  .bomb--armed { box-shadow: 0 0 0 2px var(--ember); }
  .blast-cell { opacity: .85; }
  /* The chip exists to be followed. With motion off there is no journey to
     watch, so hold it at the bomb long enough to be read, then drop it. */
  .spend-chip { animation: spend-hold .9s steps(1, end) forwards !important; }
  @keyframes spend-hold { 0% { opacity: 1; } 100% { opacity: 0; } }
  .tile__fuse::after { background: var(--ember); }
}
