/* ==================================================================
   Double Dots
   Calm, warm, board-game-card minimalism. One flourish: the
   connection line and the tint it pushes through the HUD and board.
   Everything else stays quiet.
   ================================================================== */

:root {
  /* --- surface & ink ------------------------------------------- */
  --paper: #FAF7F2;
  --ink: #2B3A55;
  --ink-rgb: 43, 58, 85;

  /* --- dot palette --------------------------------------------- */
  --dot-red:    #E8463C;
  --dot-yellow: #F5C542;
  --dot-green:  #6CC24A;
  --dot-blue:   #3F8EE8;
  --dot-purple: #9B59B6;

  /* anchors: a neutral warm-grey disc with a darker glyph */
  --anchor-disc: #D8D3CB;
  --anchor-ink:  #6E6A64;
  --anchor-rgb:  140, 138, 134;

  /* Live tint pushed by the current chain. Ink when idle. */
  --tint-rgb: var(--ink-rgb);

  /* --- type ----------------------------------------------------- */
  --display: "Fredoka", ui-rounded, "SF Pro Rounded", "Segoe UI Variable Display",
             system-ui, sans-serif;
  --body: "Nunito Sans", ui-rounded, "SF Pro Rounded", system-ui,
          -apple-system, "Segoe UI", sans-serif;

  /* --- motion --------------------------------------------------- */
  --fall: 300ms;
  --ease-fall: cubic-bezier(.3, 1.28, .52, 1);
  --ease-pop: cubic-bezier(.34, 1.62, .64, 1);

  /* --- HUD chips ------------------------------------------------ */
  --surface: #FFFFFF;
  --chip-shadow: 0 1px 2px rgba(43, 58, 85, .06), 0 0 0 1px rgba(43, 58, 85, .05);

  /* --- board geometry (JS keeps these in sync with the viewport) - */
  --board-w: 320px;
  --board-h: 320px;
  --pad: 16px;
  --cell: 48px;
  --dot-size: 32px;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;
  overscroll-behavior: none;
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.4;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

button {
  font: inherit;
  color: inherit;
  margin: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* ==================================================================
   Shell
   ================================================================== */

.app {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 480px;
  margin-inline: auto;
  padding:
    calc(env(safe-area-inset-top) + 12px)
    calc(env(safe-area-inset-right) + 16px)
    calc(env(safe-area-inset-bottom) + 10px)
    calc(env(safe-area-inset-left) + 16px);
}

/* ==================================================================
   HUD — wordmark / tools / level strip / status
   ================================================================== */

.hud {
  position: relative;
  flex: 0 0 auto;
  margin: -12px -16px 0;
  padding: 12px 16px 10px;
}

/* The HUD wash. A solid tint faded out by a static mask, so the color
   transition is a plain background-color animation. */
.hud::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: rgba(var(--tint-rgb), .11);
  -webkit-mask-image: linear-gradient(180deg, #000, transparent 84%);
  mask-image: linear-gradient(180deg, #000, transparent 84%);
  transition: background-color .4s ease;
  pointer-events: none;
}

.bar, .levels, .status { position: relative; }

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

.wordmark {
  display: flex;
  align-items: baseline;
  margin: 0;
  font-family: var(--display);
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: -.015em;
  white-space: nowrap;
}

.wm-dots {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin: 0 5px;
  transform: translateY(-.06em);
}
.wm-dots i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
}
.wm-dots i:first-child { background: var(--dot-red); }
.wm-dots i:last-child  { background: var(--dot-blue); }

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

.tools {
  display: flex;
  gap: 2px;
}

.tool {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 11px;
  background: transparent;
  transition: background-color .18s ease;
}
.tool:active { background: rgba(var(--ink-rgb), .09); }
.tool-label {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.tool svg {
  width: 21px;
  height: 21px;
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .6;
  transition: opacity .18s ease;
}
.tool:hover svg { opacity: .95; }
.tool svg .ic-fill { fill: var(--ink); stroke: none; }
.tool svg .ic-body { fill: var(--ink); stroke: none; }

/* sound: swap the waves for a cross when off */
#btn-sound .ic-off { display: none; }
#btn-sound[aria-pressed="false"] .ic-on { display: none; }
#btn-sound[aria-pressed="false"] .ic-off { display: block; }
#btn-sound[aria-pressed="false"] svg { opacity: .32; }

/* shapes toggle lights up when on */
#btn-glyph[aria-pressed="true"] svg { opacity: 1; stroke: var(--dot-purple); }
#btn-glyph[aria-pressed="true"] svg .ic-fill { fill: var(--dot-purple); }

/* --- level strip -------------------------------------------------- */

.levels {
  display: flex;
  gap: 6px;
  margin: 12px -16px 0;
  padding: 0 16px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.levels::-webkit-scrollbar { display: none; }

.chip {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  flex: 0 0 auto;
  width: 38px;
  padding: 5px 0 4px;
  border: 0;
  border-radius: 11px;
  background: transparent;
  font-family: var(--display);
  font-size: .9rem;
  font-weight: 500;
  line-height: 1;
  color: rgba(var(--ink-rgb), .55);
  transition: background-color .2s ease, color .2s ease, opacity .2s ease;
}
.chip.is-cleared {
  background: rgba(var(--ink-rgb), .07);
  color: rgba(var(--ink-rgb), .8);
}
.chip.is-current {
  background: var(--ink);
  color: var(--paper);
}
.chip[disabled] {
  opacity: .3;
  cursor: default;
}

/* star pips — only shown once a level has been cleared */
.pips {
  display: flex;
  gap: 2.5px;
  height: 4px;
}
.pips i {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: rgba(var(--ink-rgb), .22);
}
.pips i.on { background: var(--dot-yellow); }
.chip.is-current .pips i { background: rgba(250, 247, 242, .3); }
.chip.is-current .pips i.on { background: var(--dot-yellow); }

/* --- status row ---------------------------------------------------
   Two flat chips on one line: the move count on the left, every colour
   target in a single pill on the right. Both sit on the same tokens as the
   level strip so the header reads as one quiet band above the board. */

.status {
  display: flex;
  align-items: stretch;
  gap: 8px;
  margin-top: 12px;
}

.stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  min-width: 62px;
  padding: 8px 10px;
  border-radius: 16px;
  background: var(--surface);
  box-shadow: var(--chip-shadow);
}

.stat-num {
  font-family: var(--display);
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  transition: transform .2s var(--ease-pop);
}
.stat-num.bump { transform: scale(1.22); }

.stat-label {
  margin-top: 3px;
  font-size: .55rem;
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: rgba(var(--ink-rgb), .45);
  white-space: nowrap;
}

/* --- colour targets ----------------------------------------------- */

.goals {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  justify-content: space-around;
  gap: 4px;
  margin: 0;
  padding: 8px 10px;
  border-radius: 16px;
  background: var(--surface);
  box-shadow: var(--chip-shadow);
  list-style: none;
}

.goal {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  transition: opacity .3s ease;
}

.goal-dot {
  display: grid;
  place-items: center;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 33% 26%,
      rgba(255,255,255,.5) 0%, rgba(255,255,255,.1) 38%, rgba(255,255,255,0) 56%),
    var(--c);
  box-shadow: inset 0 -1px 2px rgba(0,0,0,.14);
  font-size: 10px;
  line-height: 1;
  color: rgba(255,255,255,.9);
  transition: transform .2s var(--ease-pop);
}
.goal.bump .goal-dot { transform: scale(1.28); }

.goal-dot.is-anchor { background: var(--anchor-disc); }
.goal-dot .anchor-ic { width: 13px; height: 13px; stroke: var(--anchor-ink); }

.goal-glyph { opacity: 0; }
body.glyphs .goal-glyph { opacity: 1; }

/* Counts up, never down: "7 / 20" is progress made, not work left. */
.goal-num {
  font-family: var(--display);
  font-size: .78rem;
  font-weight: 500;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: rgba(var(--ink-rgb), .62);
}

/* A finished target keeps its colour and gains a tick — done, not dimmed
   away, so the row still reads as five things you are working through. */
.goal.is-done .goal-dot {
  box-shadow: inset 0 -1px 2px rgba(0,0,0,.14), 0 0 0 3px rgba(var(--ink-rgb), .1);
}
.goal.is-done .goal-num {
  font-size: 1rem;
  color: var(--c);
}

/* --- coins & score ------------------------------------------------- */

.ticker {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: 8px;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .04em;
  color: rgba(var(--ink-rgb), .5);
}

.coins {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-variant-numeric: tabular-nums;
}
.coin {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--dot-yellow);
  box-shadow: inset 0 -1px 1px rgba(0,0,0,.2);
}

.score-chip { font-variant-numeric: tabular-nums; }
.score-label { font-weight: 700; letter-spacing: .1em; text-transform: uppercase; opacity: .7; }

/* ==================================================================
   Stage & board
   ================================================================== */

.stage {
  flex: 1 1 0;
  min-height: 0;
  display: grid;
  place-items: center;
  padding: 12px 0;
  overflow: hidden;
}

.board {
  position: relative;
  width: var(--board-w);
  height: var(--board-h);
  border-radius: 24px;
  background-color: rgba(var(--tint-rgb), .05);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .8),
    0 14px 34px -22px rgba(var(--ink-rgb), .55);
  transition: background-color .4s ease;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}

/* Recessed wells: the board reads as a physical card with holes, which is
   only visible in the gap between a dot clearing and the next one landing.
   One well per playable cell — cells cut out of the board's shape get none. */
.wells .well {
  position: absolute;
  left: calc(var(--pad) + var(--wc) * var(--cell) + (var(--cell) - var(--dot-size)) / 2);
  top: calc(var(--pad) + var(--wr) * var(--cell) + (var(--cell) - var(--dot-size)) / 2);
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  background: rgba(var(--ink-rgb), .05);
}

/* A held run that will wipe its color tints the whole board. */
.board.is-wiping { background-color: rgba(var(--tint-rgb), .13); }

/* A booster is armed and waiting for a dot. */
.board.is-armed { box-shadow: inset 0 0 0 2px rgba(var(--ink-rgb), .28), 0 14px 34px -22px rgba(var(--ink-rgb), .55); cursor: crosshair; }

.board.is-loop-hit { animation: boardPulse .5s ease-out; }
@keyframes boardPulse {
  0%   { background-color: rgba(var(--tint-rgb), .05); }
  35%  { background-color: rgba(var(--tint-rgb), .22); }
  100% { background-color: rgba(var(--tint-rgb), .05); }
}

.wells, .link, .dots, .fx {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.link { z-index: 1; }
.dots { z-index: 2; }
.fx   { z-index: 3; }

/* A released line dissolves rather than vanishing. `forwards` holds the line at
   zero until the class is dropped, so a new drag comes back at full strength. */
.link.is-fading { animation: linkFade .3s ease-out forwards; }
@keyframes linkFade {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* --- dots --------------------------------------------------------- */

.dot {
  position: absolute;
  left: 0;
  top: 0;
  display: grid;
  place-items: center;
  width: var(--cell);
  height: var(--cell);
  transition: transform var(--fall) var(--ease-fall);
  will-change: transform;
}
.dot.no-anim { transition: none; }

.dot[data-color="red"]    { --c: var(--dot-red);    --c-rgb: 232, 70, 60; }
.dot[data-color="yellow"] { --c: var(--dot-yellow); --c-rgb: 245, 197, 66; }
.dot[data-color="green"]  { --c: var(--dot-green);  --c-rgb: 108, 194, 74; }
.dot[data-color="blue"]   { --c: var(--dot-blue);   --c-rgb: 63, 142, 232; }
.dot[data-color="purple"] { --c: var(--dot-purple); --c-rgb: 155, 89, 182; }
.dot.is-anchor            { --c: var(--anchor-disc); --c-rgb: var(--anchor-rgb); }

.dot-face {
  display: grid;
  place-items: center;
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  /* Candy button: a tight highlight near the top-left and a soft dark rim,
     so the dot reads as domed without desaturating the hue. */
  background:
    radial-gradient(circle at 33% 26%,
      rgba(255, 255, 255, .52) 0%,
      rgba(255, 255, 255, .12) 36%,
      rgba(255, 255, 255, 0) 54%,
      rgba(0, 0, 0, .12) 100%),
    var(--c);
  box-shadow:
    0 2px 3px -1px rgba(var(--ink-rgb), .26),
    inset 0 -2px 3px rgba(0, 0, 0, .12);
  transition:
    transform .17s var(--ease-pop),
    opacity .2s ease,
    box-shadow .17s ease;
}

.dot-glyph {
  font-size: calc(var(--dot-size) * .44);
  line-height: 1;
  color: rgba(255, 255, 255, .85);
  opacity: 0;
  transition: opacity .2s ease;
  pointer-events: none;
}
body.glyphs .dot-glyph { opacity: 1; }

/* Anchors: a flat grey disc with a drawn anchor; never linkable, and no
   colour glyph in Shapes mode (the anchor already is a shape). */
.dot.is-anchor .dot-face {
  position: relative;
  background: var(--anchor-disc);
  box-shadow: 0 2px 3px -1px rgba(var(--ink-rgb), .2), inset 0 -2px 3px rgba(0, 0, 0, .08);
}
.anchor-ic {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.dot-face .anchor-ic {
  position: absolute;
  width: 62%;
  height: 62%;
  color: var(--anchor-ink);
}
.dot.is-bomb .anchor-ic { display: none; }

/* An anchor that reached the bottom drops out of the board and fades. */
.dot.is-sinking { transition: none; }
.dot.is-sinking .dot-face { animation: anchorSink .42s cubic-bezier(.5, 0, .8, .5) forwards; }
@keyframes anchorSink {
  0%   { transform: translateY(0) scale(1); opacity: 1; }
  25%  { transform: translateY(-8%) scale(1.06); opacity: 1; }
  100% { transform: translateY(90%) scale(.7); opacity: 0; }
}

/* linked into the current chain */
.dot.is-linked .dot-face {
  transform: scale(1.16);
  box-shadow:
    0 0 0 3px rgba(var(--c-rgb), .26),
    0 3px 6px -2px rgba(var(--ink-rgb), .3);
}

/* every dot a held wipe (square or 13+ run) will take */
.dot.is-pulse .dot-face { animation: dotPulse .8s ease-in-out infinite; }
@keyframes dotPulse {
  0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(var(--c-rgb), .5); }
  50%      { transform: scale(1.14); box-shadow: 0 0 0 7px rgba(var(--c-rgb), 0); }
}

/* fenced in by the held square — these are the ones about to arm */
.dot.is-trapped .dot-face {
  animation: trapFlash .7s ease-in-out infinite;
}
@keyframes trapFlash {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--ink-rgb), .45); }
  50%      { box-shadow: 0 0 0 5px rgba(var(--ink-rgb), 0); }
}

/* clearing */
.dot.is-clearing .dot-face {
  transform: scale(0);
  opacity: 0;
  transition-duration: .22s;
  transition-timing-function: cubic-bezier(.5, -.4, .8, .4);
}
.dot.is-clearing::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--dot-size);
  height: var(--dot-size);
  border-radius: 50%;
  border: 2.5px solid rgba(var(--c-rgb), .85);
  animation: ringOut .38s ease-out forwards;
  pointer-events: none;
}
@keyframes ringOut {
  from { transform: translate(-50%, -50%) scale(.8); opacity: .9; }
  to   { transform: translate(-50%, -50%) scale(2.1); opacity: 0; }
}

/* bombs */
.dot.is-bomb .dot-face {
  background:
    radial-gradient(circle at 34% 27%, rgba(255,255,255,.35), rgba(255,255,255,0) 55%),
    var(--ink);
  box-shadow: 0 0 0 3px rgba(var(--c-rgb), .5), 0 3px 8px -2px rgba(var(--ink-rgb), .5);
  animation: bombTick .5s ease-in-out infinite;
}
.dot.is-bomb .dot-glyph { opacity: 1; color: rgba(255,255,255,.92); }
@keyframes bombTick {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.1); }
}

/* Spawning. The drop uses the independent `translate` property so it composes
   with the inline `transform` that holds the dot's grid position — the dot is
   placed at its final cell immediately and only the offset is animated. A
   keyframe animation always plays on a freshly inserted node, where a
   transition would depend on a reflow being captured first. */
@keyframes dropIn {
  from { translate: 0 var(--drop, -100px); }
  to   { translate: 0 0; }
}
.dot.is-dropping { animation: dropIn var(--fall) var(--ease-fall); }

/* shuffle */
.dot.is-shuffling .dot-face {
  transform: scale(.25) rotate(-25deg);
  opacity: .3;
  transition-duration: .22s;
}

/* --- fx layer ----------------------------------------------------- */

.wave {
  position: absolute;
  border-radius: 50%;
  border: 3px solid rgba(var(--tint-rgb), .55);
  transform: translate(-50%, -50%) scale(0);
  animation: waveOut .62s cubic-bezier(.2, .7, .3, 1) forwards;
}
@keyframes waveOut {
  from { transform: translate(-50%, -50%) scale(0); opacity: .8; }
  to   { transform: translate(-50%, -50%) scale(1); opacity: 0; }
}

.blast {
  position: absolute;
  border-radius: 22%;
  background: radial-gradient(circle, rgba(255,255,255,.9), rgba(var(--ink-rgb), .28) 65%, transparent 72%);
  transform: translate(-50%, -50%) scale(.3);
  animation: blastOut .42s ease-out forwards;
}
@keyframes blastOut {
  from { transform: translate(-50%, -50%) scale(.3); opacity: 1; }
  to   { transform: translate(-50%, -50%) scale(1.05); opacity: 0; }
}

.bonus-bomb {
  position: absolute;
  border-radius: 50%;
  background:
    radial-gradient(circle at 34% 27%, rgba(255,255,255,.35), rgba(255,255,255,0) 55%),
    var(--ink);
  transform: translate(-50%, -50%);
  animation: bombFall .3s cubic-bezier(.55, 0, .9, .45) forwards;
}
@keyframes bombFall {
  from { translate: 0 var(--from, -200px); opacity: .6; }
  to   { translate: 0 0; opacity: 1; }
}

.float-pts {
  position: absolute;
  font-family: var(--display);
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--ink);
  transform: translate(-50%, -50%);
  animation: floatUp .9s ease-out forwards;
  pointer-events: none;
}
@keyframes floatUp {
  from { translate: 0 0; opacity: 1; }
  to   { translate: 0 -40px; opacity: 0; }
}

.app.shake { animation: shake .34s ease-in-out; }
@keyframes shake {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(-3px, 2px); }
  40% { transform: translate(3px, -2px); }
  60% { transform: translate(-2px, -1px); }
  80% { transform: translate(2px, 1px); }
}

/* ==================================================================
   Boosters
   ================================================================== */

.boosters {
  flex: 0 0 auto;
  display: flex;
  justify-content: center;
  gap: 8px;
  padding: 0 0 8px;
}

.booster {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 11px 7px 9px;
  border: 0;
  border-radius: 14px;
  background: var(--surface);
  box-shadow: var(--chip-shadow);
  font-size: .74rem;
  font-weight: 700;
  color: var(--ink);
  transition: background-color .18s ease, color .18s ease, opacity .2s ease, transform .15s var(--ease-pop);
}
.booster:active:not([disabled]) { transform: scale(.96); }
.booster svg {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .75;
}
.booster-name { white-space: nowrap; }
.booster-cost {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-variant-numeric: tabular-nums;
  color: rgba(var(--ink-rgb), .55);
}
.booster-cost .coin { width: 9px; height: 9px; }
.booster.is-armed { background: var(--ink); color: var(--paper); }
.booster.is-armed .booster-cost { color: rgba(250, 247, 242, .7); }
.booster[disabled] { opacity: .38; cursor: default; }

/* ==================================================================
   Hint & toast
   ================================================================== */

.hint {
  flex: 0 0 auto;
  margin: 0;
  padding: 2px 0 2px;
  text-align: center;
  font-size: .72rem;
  font-weight: 600;
  color: rgba(var(--ink-rgb), .42);
}

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom) + 96px);
  z-index: 20;
  padding: 8px 16px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--paper);
  width: max-content;
  max-width: min(88vw, 360px);
  font-size: .8rem;
  font-weight: 700;
  text-align: center;
  opacity: 0;
  transform: translate(-50%, 8px);
  transition: opacity .25s ease, transform .25s ease;
  pointer-events: none;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* ==================================================================
   Overlay
   ================================================================== */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(250, 247, 242, .82);
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);
  animation: fadeIn .22s ease;
}
.overlay[hidden] { display: none; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.sheet {
  width: 100%;
  max-width: 320px;
  padding: 28px 26px 24px;
  border-radius: 26px;
  background: var(--paper);
  box-shadow: 0 26px 60px -26px rgba(var(--ink-rgb), .55);
  text-align: center;
  animation: sheetIn .4s var(--ease-pop);
}
@keyframes sheetIn {
  from { transform: translateY(14px) scale(.94); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

.sheet-eyebrow {
  margin: 0;
  font-size: .62rem;
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: rgba(var(--ink-rgb), .42);
}

/* Body face, not the display face — Fredoka is reserved for the wordmark
   and the big numbers, so the score below stays the loudest thing here. */
.sheet-title {
  margin: 8px 0 0;
  font-size: 1.7rem;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -.015em;
}

.stars {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: 18px;
}
.stars svg { width: 32px; height: 32px; }
.stars .star { fill: rgba(var(--ink-rgb), .11); }
.stars .star.on {
  fill: var(--dot-yellow);
  animation: starPop .45s var(--ease-pop) backwards;
}
@keyframes starPop {
  from { transform: scale(0) rotate(-40deg); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

.sheet-score {
  margin: 18px 0 0;
  font-size: .8rem;
  font-weight: 700;
  color: rgba(var(--ink-rgb), .5);
}
.sheet-score b {
  display: block;
  font-family: var(--display);
  font-size: 2.6rem;
  font-weight: 600;
  line-height: 1.05;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.sheet-note {
  margin: 10px 0 0;
  font-size: .82rem;
  font-weight: 600;
  color: rgba(var(--ink-rgb), .55);
}

.sheet-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 22px;
}

.btn {
  padding: 13px 20px;
  border: 1.5px solid transparent;
  border-radius: 999px;
  background: transparent;
  font-size: .95rem;
  font-weight: 700;
  line-height: 1;
  transition: transform .15s var(--ease-pop), background-color .18s ease;
}
.btn:active { transform: scale(.97); }
.btn-primary {
  background: var(--ink);
  color: var(--paper);
}
.btn-quiet {
  border-color: rgba(var(--ink-rgb), .16);
  color: rgba(var(--ink-rgb), .75);
}

/* ==================================================================
   Short viewports — trim the chrome so the board always fits
   ================================================================== */

@media (max-height: 620px) {
  .stat-num { font-size: 1.65rem; }
  .levels { margin-top: 8px; }
  .status { margin-top: 8px; }
  .stage { padding: 8px 0; }
  .hint { display: none; }
  .booster { padding: 5px 9px 5px 7px; }
}

@media (max-height: 520px) {
  .wordmark { font-size: 1.1rem; }
  .chip { width: 32px; }
  .booster-name { display: none; }
}

/* ==================================================================
   Reduced motion — keep the state changes, drop the movement
   ================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .dot.is-pulse .dot-face {
    animation: none;
    box-shadow: 0 0 0 4px rgba(var(--c-rgb), .55);
  }
  .dot.is-bomb .dot-face { animation: none; }
  .dot.is-trapped .dot-face { animation: none; box-shadow: 0 0 0 4px rgba(var(--ink-rgb), .4); }
}
