/* Honeyflow — Telegram Mini App styles. No external fonts or frameworks. */

:root {
  --bg: #FFF7E8;
  --surface: #FFFFFF;
  --surface-2: #FBF1DE;
  --ink: #2B2015;
  --muted: #8A745A;
  --line: #E9D8B8;
  --cell: #F1E2C6;
  --cell-edge: rgba(120, 88, 40, .10);
  --accent: #E8A11C;
  --accent-soft: #FFE6AC;
  --accent-ink: #4A3208;
  --good: #4E8C5B;
  --bad: #C4563F;
  --shadow: 0 8px 26px rgba(93, 62, 14, .09);
  --tile-1: #E8A11C;
  --tile-2: #D9694A;
  --tile-3: #6E9E7A;
  --tile-4: #7B7FC4;
  --tile-5: #4E9BB5;
  --tile-6: #C08A55;
  --board-gap: 4px;
  --board-pad: 9px;
  --radius: 16px;
  --tabbar-h: 62px;
}

body[data-scheme="dark"] {
  --bg: #16120D;
  --surface: #241C14;
  --surface-2: #2C2318;
  --ink: #FFF1D8;
  --muted: #A58C6D;
  --line: #3B3022;
  --cell: #2F261B;
  --cell-edge: rgba(255, 226, 170, .06);
  --accent: #FFC247;
  --accent-soft: #4A3515;
  --accent-ink: #231705;
  --good: #7FC28C;
  --bad: #E5806A;
  --shadow: 0 8px 26px rgba(0, 0, 0, .35);
  --tile-1: #FFC247;
  --tile-2: #F0805C;
  --tile-3: #86BC94;
  --tile-4: #9A9EE3;
  --tile-5: #63B6D1;
  --tile-6: #DCA46B;
}

/* Purchased themes */
body[data-skin="wildflower"] {
  --bg: #F6F8E9; --surface: #FFFFFF; --surface-2: #EBF2D8;
  --ink: #28331F; --muted: #6F8060; --line: #D9E5C3; --cell: #E4EDD0;
  --accent: #7DA84A; --accent-soft: #E3F0C4; --accent-ink: #1C2A10;
  --tile-1: #E5B33E; --tile-2: #D96F6F; --tile-3: #6E9E7A;
  --tile-4: #8E86C0; --tile-5: #5BA2B5; --tile-6: #B58A5C;
}
body[data-skin="wildflower"][data-scheme="dark"] {
  --bg: #141A10; --surface: #202A18; --surface-2: #28331F;
  --ink: #F0F6E2; --muted: #9DB18A; --line: #34422A; --cell: #29351F;
  --accent: #A7CE6A; --accent-soft: #354621;
  --tile-1: #F2CE61; --tile-2: #EE8C86; --tile-3: #8BC49A;
  --tile-4: #A9A2DC; --tile-5: #79C0D2; --tile-6: #CDA073;
}
body[data-skin="midnight"], body[data-skin="midnight"][data-scheme="light"] {
  --bg: #101426; --surface: #1B2140; --surface-2: #232B52;
  --ink: #EEF0FF; --muted: #9AA3CC; --line: #2E3765; --cell: #232B52;
  --accent: #F3C34E; --accent-soft: #3A3461; --accent-ink: #1A1530;
  --good: #74D8A0; --bad: #F1877C;
  --shadow: 0 8px 30px rgba(0, 0, 0, .4);
  --tile-1: #F3C34E; --tile-2: #F08A8A; --tile-3: #6CD2A4;
  --tile-4: #9B8CFF; --tile-5: #5CB8E8; --tile-6: #DE9C6B;
}
body[data-skin="sakura"] {
  --bg: #FFF3F3; --surface: #FFFFFF; --surface-2: #FDE8EC;
  --ink: #3A242B; --muted: #A57E89; --line: #F2D8DF; --cell: #F8E3E8;
  --accent: #D97394; --accent-soft: #FBD9E3; --accent-ink: #40131F;
  --tile-1: #EFAF6B; --tile-2: #D97394; --tile-3: #8FB49A;
  --tile-4: #A992C9; --tile-5: #78AFC6; --tile-6: #C89186;
}
body[data-skin="sakura"][data-scheme="dark"] {
  --bg: #1D1219; --surface: #2B1B25; --surface-2: #35212E;
  --ink: #FFEAF0; --muted: #C29AA8; --line: #452B39; --cell: #35212E;
  --accent: #F09AB6; --accent-soft: #4A2737;
  --tile-1: #F5C184; --tile-2: #F08AAA; --tile-3: #9EC9A9;
  --tile-4: #BAA8DE; --tile-5: #8CC3D9; --tile-6: #DCA797;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { height: 100%; margin: 0; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  font-size: 15px;
  line-height: 1.45;
  overscroll-behavior: none;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}
button { font: inherit; color: inherit; cursor: pointer; border: 0; background: none; }
/* The `hidden` attribute must beat any `display` a component sets. Without
   this, `.setting-row { display: flex }` keeps a hidden row on screen — the
   reminder switch showed up in builds where it could not work. */
[hidden] { display: none !important; }
.hidden { display: none !important; }

#app {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  min-height: 100dvh;
  padding-top: env(safe-area-inset-top);
}

/* ── top bar ──────────────────────────────────────────────────────────── */
.topbar {
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 16px 6px;
  gap: 8px;
}
.brand { display: flex; align-items: center; gap: 8px; min-width: 0; }
.brand img { border-radius: 10px; flex: none; }
.brand-text { display: flex; flex-direction: column; min-width: 0; }
.brand-text strong { font-size: 17px; letter-spacing: .16em; line-height: 1.1; }
.brand-text small { color: var(--muted); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.top-actions { display: flex; gap: 5px; }
.icon-btn {
  position: relative;
  width: 36px; height: 36px; border-radius: 12px;
  display: grid; place-items: center;
  background: var(--surface); color: var(--muted);
  box-shadow: 0 1px 0 var(--line);
}
/* "There is a reward to collect" indicator on the quests button. */
.quest-dot {
  position: absolute; top: 4px; right: 4px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--bad); box-shadow: 0 0 0 2px var(--surface);
  pointer-events: none;
}
.icon-btn:active { transform: scale(.94); }
body #btn-sound.muted #sound-wave { opacity: .18; }

/* ── honey (soft currency) ────────────────────────────────────────────── */
/* A hexagon reads as a honeycomb cell at 13 px, where a circle would read as
   a coin from some other game. */
.coin-dot {
  width: 13px; height: 13px; flex: none;
  background: var(--accent);
  clip-path: polygon(50% 0, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%);
}
.coin-chip {
  display: inline-flex; align-items: center; gap: 4px;
  height: 36px; padding: 0 8px; border-radius: 12px;
  background: var(--surface); color: var(--ink);
  box-shadow: 0 1px 0 var(--line);
}
.coin-chip b { font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
.coin-chip:active { transform: scale(.94); }
/* A payout pulses the chip instead of stacking a second toast on top of the
   sheet that already announced it. */
.coin-chip.bump { animation: coinBump .44s cubic-bezier(.2,.9,.3,1); }
@keyframes coinBump {
  0% { transform: scale(1); }
  35% { transform: scale(1.12); }
  100% { transform: scale(1); }
}
/* Unclaimed daily honey: same dot as the quests button, same meaning. */
.coin-chip .coin-dot { box-shadow: 0 0 0 0 transparent; }
.coin-chip.claimable { position: relative; }
.coin-chip.claimable::after {
  content: ''; position: absolute; top: 3px; right: 3px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--bad); box-shadow: 0 0 0 2px var(--surface);
}

/* ── views ────────────────────────────────────────────────────────────── */
#main { flex: 1 1 auto; min-height: 0; position: relative; }
.view {
  display: none;
  height: 100%;
  overflow-y: auto;
  padding: 8px 16px 18px;
  -webkit-overflow-scrolling: touch;
}
.view.active { display: block; }
#view-game.active { display: flex; flex-direction: column; overflow: hidden; }

.view-title { font-size: 24px; margin: 10px 0 2px; letter-spacing: -.02em; }
.view-sub { color: var(--muted); font-size: 13px; margin: 0 0 16px; }
.section-title { font-size: 14px; margin: 22px 0 8px; color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: .08em; }
.fine-print { font-size: 11.5px; color: var(--muted); line-height: 1.5; margin: 14px 4px; }
.fine-print.center { text-align: center; margin-top: 22px; }

/* ── game screen ──────────────────────────────────────────────────────── */
.mode-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex: none; }
.segmented {
  display: inline-flex; background: var(--surface-2); border-radius: 12px; padding: 3px; gap: 2px;
  box-shadow: inset 0 0 0 1px var(--line);
}
.segmented.wide { display: flex; width: 100%; margin-bottom: 14px; }
.segmented.wide .seg { flex: 1; }
.segmented.small .seg { padding: 5px 12px; font-size: 12px; }
.seg {
  padding: 7px 14px; border-radius: 9px; font-size: 12.5px; font-weight: 600;
  color: var(--muted); white-space: nowrap; transition: background .16s, color .16s;
}
.seg.active { background: var(--surface); color: var(--ink); box-shadow: 0 1px 4px rgba(0,0,0,.08); }
.day-label { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

.score-row { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; flex: none; margin: 10px 2px 2px; }
.score-main { display: flex; flex-direction: column; }
.score-main .label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .1em; }
.score-main strong { font-size: 40px; line-height: 1; font-weight: 750; font-variant-numeric: tabular-nums; letter-spacing: -.03em; }
.score-side { display: flex; gap: 6px; padding-bottom: 4px; }
.chip {
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
  padding: 5px 10px; display: flex; flex-direction: column; align-items: center; min-width: 62px;
}
.chip span { font-size: 9.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
.chip b { font-size: 15px; font-variant-numeric: tabular-nums; }

.combo-banner {
  flex: none; height: 20px; text-align: center; font-size: 12px; font-weight: 700;
  color: var(--accent); letter-spacing: .03em; opacity: 0; transform: translateY(-4px);
  transition: opacity .18s, transform .18s;
}
.combo-banner.show { opacity: 1; transform: none; }

/* Board sizing: app.js measures the free space and sets an exact square, so
   the 8x8 cells stay square and the drag/snap math stays correct. overflow:
   hidden is a hard guarantee that a too-large board can never paint over the
   three pieces in the tray below (short screens, Telegram viewport changes). */
.board-wrap {
  flex: 1 1 auto; min-height: 0;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  padding: 2px 0;
  container-type: size;
}
.board {
  width: min(100%, 430px);
  aspect-ratio: 1 / 1;
  /* No max-height here: it clamps only height and turns square cells into
     rectangles. fitBoard() sets both dimensions from the measured space. */
  flex: none;
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-template-rows: repeat(8, 1fr);
  gap: var(--board-gap);
  padding: var(--board-pad);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  box-shadow: var(--shadow);
  touch-action: none;
}
/* First paint before JS runs (and WebViews without JS sizing): container units
   cap the width by the available height, keeping the board square. */
@supports (width: 1cqh) {
  .board { width: min(100%, 430px, 100cqh); }
}
.cell {
  position: relative;
  min-width: 0; min-height: 0;
  border-radius: 9px;
  background: var(--cell);
  box-shadow: inset 0 0 0 1px var(--cell-edge);
  transition: background .12s ease, transform .12s ease;
}
.cell.filled {
  background: var(--c);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.28), inset 0 -2px 0 rgba(0,0,0,.10);
  animation: cellPop .16s ease-out;
}
.cell.filled::after {
  content: ""; position: absolute; inset: 14% 14% auto auto;
  width: 22%; height: 22%; border-radius: 50%;
  background: rgba(255,255,255,.24);
}
.c1 { --c: var(--tile-1); } .c2 { --c: var(--tile-2); } .c3 { --c: var(--tile-3); }
.c4 { --c: var(--tile-4); } .c5 { --c: var(--tile-5); } .c6 { --c: var(--tile-6); }
.cell.preview-ok { background: var(--accent-soft); box-shadow: inset 0 0 0 2px var(--accent); }
.cell.preview-bad { box-shadow: inset 0 0 0 2px var(--bad); }
.cell.flash { animation: cellFlash .38s ease-out; }
/* A cleared line is shown leaving: the cell is already empty underneath, and a
   tile layer on top shrinks away in the colour it had before the move. Drawing
   it on the cell itself left a white hole in the board for 240 ms, which read
   as broken layout. The state is already updated — this is decoration, so it
   can never delay or block a placement. */
.cell.clearing {
  animation: none;                       /* cancel the cellPop from .filled */
  transition: none;                      /* the empty cell is there at once  */
  background: var(--cell);
  box-shadow: inset 0 0 0 1px var(--cell-edge);
}
.cell.clearing::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; z-index: 2;
  background:
    radial-gradient(circle at 75% 25%, rgba(255,255,255,.24) 0 11%, rgba(255,255,255,0) 12%),
    var(--c);
  box-shadow: inset 0 2px 0 rgba(255,255,255,.28), inset 0 -2px 0 rgba(0,0,0,.10);
  animation: cellClear .24s ease-in forwards;
  pointer-events: none;
}
.cell.clearing::after { display: none; }  /* the dot lives in ::before here */
/* Paid hint: the ring breathes so it is noticed without a modal. */
.cell.hint-cell { animation: hintPulse 1.1s ease-in-out infinite; }
@keyframes cellPop { from { transform: scale(.72); opacity: .5; } to { transform: none; opacity: 1; } }
@keyframes cellFlash { 0% { background: #FFF8DC; transform: scale(1.06); } 100% { background: var(--cell); transform: none; } }
@keyframes cellClear {
  0% { transform: scale(1); opacity: 1; }
  55% { transform: scale(1.07); opacity: 1; }
  100% { transform: scale(.38); opacity: 0; }
}
@keyframes hintPulse {
  0%, 100% { box-shadow: inset 0 0 0 2px var(--accent); }
  50% { box-shadow: inset 0 0 0 2px var(--accent), 0 0 0 3px var(--accent-soft); }
}
#score-value.bump { animation: scoreBump .32s ease-out; }
@keyframes scoreBump {
  0% { transform: scale(1); }
  40% { transform: scale(1.13); }
  100% { transform: scale(1); }
}

.tray-hint { flex: none; text-align: center; font-size: 11px; color: var(--muted); margin: 8px 0 6px; }
.tray { flex: none; display: flex; gap: 8px; justify-content: center; align-items: center; min-height: 84px; }
.tray-slot {
  --mini: clamp(17px, 6vw, 26px);
  flex: 1 1 0; max-width: 132px; min-height: 80px;
  display: grid; place-items: center;
  border-radius: 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: 0 2px 0 var(--line);
  touch-action: none;
  transition: transform .14s, border-color .14s, background .14s;
}
.tray-slot.selected { border-color: var(--accent); background: var(--accent-soft); transform: translateY(-3px); }
.tray-slot.used { opacity: .38; }
.tray-slot.shake { animation: shake .3s; }
.tray-slot.hint-slot { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft), 0 2px 0 var(--line); }
/* A shuffled or refilled tray arrives rather than teleporting in. */
.tray-slot.fresh { animation: slotIn .26s ease-out; }
@keyframes slotIn { from { opacity: 0; transform: translateY(7px) scale(.9); } to { opacity: 1; transform: none; } }

/* ── boosts (free play only) ──────────────────────────────────────────── */
.boost-bar { flex: none; display: flex; gap: 10px; justify-content: center; margin-top: 10px; }
.boost {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 54px; height: 44px; border-radius: 13px;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line);
  box-shadow: 0 2px 0 var(--line);
  transition: transform .12s, opacity .12s;
}
.boost:active:not(:disabled) { transform: scale(.94); }
.boost:disabled { opacity: .4; }
/* Out of stock is dimmed but still tappable: the tap explains where to buy. */
.boost.empty { opacity: .55; }
.boost-count {
  position: absolute; top: -6px; right: -6px;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 9px; background: var(--accent); color: var(--accent-ink);
  font-size: 10px; font-weight: 750; line-height: 18px; text-align: center;
  font-variant-numeric: tabular-nums;
}
/* An empty stock shows no badge at all: "0" reads as "broken", nothing reads
   as "none left" together with the dimmed button. */
.boost-count:empty { display: none; }
.mini { display: grid; gap: 3px; }
.mini-cell, .mini-gap { width: var(--mini, 22px); height: var(--mini, 22px); border-radius: 6px; }
.mini-cell { background: var(--c); box-shadow: inset 0 2px 0 rgba(255,255,255,.26), inset 0 -2px 0 rgba(0,0,0,.09); }
.mini-gap { background: transparent; }
@keyframes shake { 0%,100%{transform:none} 25%{transform:translateX(-4px)} 75%{transform:translateX(4px)} }

.game-actions { flex: none; display: flex; gap: 8px; margin-top: 12px; }
.btn {
  border-radius: 13px; padding: 11px 14px; font-size: 13px; font-weight: 650;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  transition: transform .12s, filter .12s;
  white-space: nowrap;
}
.btn:active { transform: scale(.97); }
.btn.primary { background: var(--accent); color: var(--accent-ink); flex: 1; }
.btn.ghost { background: var(--surface); border: 1px solid var(--line); flex: 1; }
.btn.wide { width: 100%; margin-top: 14px; }
.btn.small { padding: 8px 12px; font-size: 12px; background: var(--accent); color: var(--accent-ink); }
.btn[disabled] { opacity: .45; pointer-events: none; }
.badge {
  background: var(--surface-2); border-radius: 8px; padding: 1px 6px;
  font-size: 11px; font-variant-numeric: tabular-nums;
}
.offline-banner {
  flex: none; margin-top: 10px; padding: 8px 12px; border-radius: 11px;
  background: var(--accent-soft); color: var(--ink); font-size: 11.5px; text-align: center;
}

/* drag ghost */
#drag-layer { position: fixed; inset: 0; pointer-events: none; z-index: 60; }
.drag-ghost {
  position: absolute; display: grid; gap: var(--board-gap);
  filter: drop-shadow(0 10px 12px rgba(0,0,0,.24));
  opacity: .94; transform: translateZ(0);
}
.drag-ghost .mini-cell, .drag-ghost .mini-gap {
  width: var(--ghost-cell); height: var(--ghost-cell); border-radius: 9px;
}
.drag-ghost .mini-cell { opacity: .95; }

/* ── cards & lists ────────────────────────────────────────────────────── */
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
}
.list { padding: 4px 0; }
.row { display: flex; align-items: center; gap: 11px; padding: 10px 14px; border-bottom: 1px solid var(--line); }
.row:last-child { border-bottom: 0; }
.row.me { background: var(--accent-soft); }
.rank { width: 26px; text-align: center; font-weight: 750; font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }
.rank.top1 { color: #D9A017; } .rank.top2 { color: #9AA3AD; } .rank.top3 { color: #B97A4B; }
.avatar-sm {
  width: 34px; height: 34px; border-radius: 11px; flex: none;
  display: grid; place-items: center; font-weight: 700; font-size: 14px;
  background: var(--accent-soft); color: var(--accent-ink);
}
.row-name { flex: 1; min-width: 0; }
.row-name b { display: block; font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row-name small { color: var(--muted); font-size: 11px; }
.row-score { font-weight: 750; font-variant-numeric: tabular-nums; font-size: 15px; }
.empty { padding: 26px 16px; text-align: center; color: var(--muted); font-size: 13px; }
.my-rank {
  display: flex; justify-content: space-between; align-items: center;
  background: var(--accent); color: var(--accent-ink);
  border-radius: var(--radius); padding: 12px 16px; margin-bottom: 14px; font-size: 13px;
}
.my-rank b { font-size: 20px; }

/* leagues — the division strip, on the board and on the profile */
.league-bar {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px; margin-bottom: 12px;
}
.league-card { margin-top: 10px; }
.league-top {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-bottom: 9px;
}
.league-name {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 13.5px; font-weight: 700;
}
.league-dot {
  width: 10px; height: 10px; border-radius: 50%; flex: none;
  background: var(--lc, var(--accent));
}
.league-next {
  font-size: 11px; color: var(--muted); text-align: right;
  font-variant-numeric: tabular-nums;
}
.league-track {
  height: 6px; border-radius: 3px; background: var(--line);
  overflow: hidden;
}
.league-track i {
  display: block; height: 100%; border-radius: inherit;
  background: var(--lc, var(--accent));
  transition: width .4s ease;
}

/* season pass — the strip on the Path screen and its reward sheet */
.season-strip {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px; margin-bottom: 14px;
}
.season-top {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-bottom: 9px;
}
.season-name { font-size: 13.5px; font-weight: 700; }
.season-tier { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.season-track { height: 6px; border-radius: 3px; background: var(--line); overflow: hidden; }
.season-track i {
  display: block; height: 100%; border-radius: inherit;
  background: var(--accent); transition: width .4s ease;
}
.season-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-top: 10px;
}
.season-next { font-size: 11px; color: var(--muted); }
.season-list { display: grid; gap: 8px; margin: 12px 0; }
.season-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: var(--radius);
  background: var(--cell); border: 1px solid var(--line);
}
.season-row.ready { border-color: var(--accent); background: var(--accent-soft); }
.season-row.done { opacity: .55; }
.season-n {
  width: 22px; height: 22px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-size: 11px; font-weight: 700;
  background: var(--surface); border: 1px solid var(--line);
}
.season-row.ready .season-n { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.season-r { flex: 1; min-width: 0; font-size: 12.5px; }
.season-at, .season-got { font-size: 11px; color: var(--muted); white-space: nowrap; }

/* shop */
.shop-list { display: grid; gap: 10px; }
.shop-item {
  display: flex; align-items: center; gap: 12px; padding: 13px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.shop-icon {
  width: 46px; height: 46px; border-radius: 14px; flex: none;
  display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent);
}
.shop-icon svg { width: 26px; height: 26px; }
.shop-body { flex: 1; min-width: 0; }
.shop-body b { display: block; font-size: 14px; }
.shop-body small { display: block; color: var(--muted); font-size: 11.5px; line-height: 1.35; }
.shop-body em { display: block; font-style: normal; color: var(--accent); font-size: 11px; margin-top: 3px; }
/* Where an item actually works. Buying a booster that is dead in the mode the
   app opens in was the complaint behind audit B-07. */
.shop-body em.mode-note { color: var(--muted); }
.shop-body em.mode-note::before { content: "· "; }
.shop-buy { flex: none; display: flex; flex-direction: column; align-items: center; gap: 1px; }
.price { font-size: 12px; font-weight: 750; color: var(--accent); }
.owned-tag { font-size: 11px; color: var(--good); font-weight: 650; }

/* profile */
.profile-head { display: flex; align-items: center; gap: 14px; margin: 10px 0 18px; }
.avatar {
  width: 62px; height: 62px; border-radius: 20px; flex: none;
  display: grid; place-items: center; font-size: 26px; font-weight: 750;
  background: var(--accent); color: var(--accent-ink);
}
.profile-head .view-title { margin: 0; }
.profile-head .view-sub { margin: 2px 0 0; }
.stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.stat { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 13px; }
.stat b { display: block; font-size: 24px; font-variant-numeric: tabular-nums; }
.stat span { font-size: 11px; color: var(--muted); }
.ref-card { display: flex; align-items: center; gap: 10px; padding: 12px 14px; }
.ref-card code { flex: 1; min-width: 0; font-size: 11.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
.skin-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.skin {
  border: 1px solid var(--line); border-radius: var(--radius); padding: 10px;
  background: var(--surface); text-align: left;
}
.skin.active { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.skin .swatches { display: flex; gap: 4px; margin-bottom: 8px; }
.skin .swatches i { width: 20px; height: 20px; border-radius: 6px; }
.skin b { font-size: 12.5px; }
.skin small { display: block; font-size: 10.5px; color: var(--muted); }
.settings { padding: 4px 14px; }
.setting-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--line); font-size: 13px; }
.setting-link { display: block; width: 100%; text-align: left; padding: 12px 0; border-bottom: 1px solid var(--line); font-size: 13px; color: var(--accent); }
.setting-link:last-child { border-bottom: 0; }
.switch { width: 44px; height: 26px; border-radius: 13px; background: var(--line); padding: 3px; transition: background .16s; }
.switch i { display: block; width: 20px; height: 20px; border-radius: 50%; background: #fff; transition: transform .16s; box-shadow: 0 1px 3px rgba(0,0,0,.2); }
.switch.on { background: var(--accent); }
.switch.on i { transform: translateX(18px); }

/* Volume sliders. The native range input is kept (it has keyboard and screen
   reader behaviour for free) and only re-skinned; the thumb is 22 px so it is
   still draggable with a thumb, not just a stylus. */
.slider {
  -webkit-appearance: none; appearance: none;
  width: 132px; height: 22px; background: transparent; margin: 0;
  --fill: var(--accent);
}
.slider::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px;
  background: linear-gradient(to right, var(--fill) var(--pct, 35%), var(--line) var(--pct, 35%));
}
.slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 22px; height: 22px; border-radius: 50%; margin-top: -9px;
  background: #fff; border: 2px solid var(--accent); box-shadow: 0 1px 4px rgba(93,62,14,.22);
}
.slider::-moz-range-track { height: 4px; border-radius: 2px; background: var(--line); }
.slider::-moz-range-progress { height: 4px; border-radius: 2px; background: var(--accent); }
.slider::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%; background: #fff;
  border: 2px solid var(--accent); box-shadow: 0 1px 4px rgba(93,62,14,.22);
}
.slider:disabled { opacity: .4; }
.slider:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 4px; }

/* ── tab bar ──────────────────────────────────────────────────────────── */
.tabbar {
  flex: 0 0 auto;
  display: flex; align-items: stretch;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--surface);
  border-top: 1px solid var(--line);
}
.tab {
  position: relative;
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; color: var(--muted); font-size: 10px; font-weight: 600;
}
.tab.active { color: var(--accent); }
.tab:active { opacity: .7; }
/* "There is a new level waiting" — the one thing that brings a lapsed player
   back into the campaign. Cleared the moment the map is opened. */
.tab-dot {
  position: absolute; top: 2px; right: 50%; margin-right: -16px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 2px var(--surface);
  pointer-events: none;
}
.tab-dot[hidden] { display: none; }

/* ── overlays ─────────────────────────────────────────────────────────── */
.overlay {
  position: fixed; inset: 0; z-index: 80;
  display: flex; align-items: center; justify-content: center;
  padding: 20px; background: rgba(24, 16, 6, .55);
  backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
  animation: fadeIn .16s ease-out;
}
.sheet {
  width: min(100%, 400px); max-height: 86dvh; overflow-y: auto;
  background: var(--surface); color: var(--ink);
  border-radius: 24px; padding: 24px 22px 20px;
  box-shadow: 0 24px 60px rgba(0,0,0,.28);
  animation: sheetIn .22s cubic-bezier(.2,.9,.3,1);
}
.sheet h2 { margin: 0 0 8px; font-size: 22px; letter-spacing: -.02em; }
.sheet p { color: var(--muted); font-size: 13px; margin: 0 0 16px; }
.sheet .big-score { font-size: 54px; font-weight: 780; line-height: 1; letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.sheet .sub-score { font-size: 12px; color: var(--muted); margin-bottom: 18px; }
.sheet .actions { display: flex; gap: 8px; margin-top: 18px; flex-wrap: wrap; }
.sheet .actions .btn { flex: 1; min-width: 110px; }
/* Two long labels side by side do not fit 390 px: stack them instead of
   clipping the primary one (audit B-01 sheet). */
.sheet .actions.col { flex-direction: column; }
.sheet .actions.col .btn { flex: none; width: 100%; }

/* Seven-day login calendar. One row, no scroll: seven cells of ~44 px fit a
   390 px phone with the sheet's padding to spare. */
.coin-row { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 5px; margin: 0 0 14px; }
.coin-day {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 8px 2px 7px; border-radius: 12px;
  background: var(--surface-2); border: 1px solid transparent;
  color: var(--muted);
}
.coin-day-n { font-size: 9px; letter-spacing: .05em; text-transform: uppercase; }
.coin-day b { font-size: 12px; color: var(--ink); font-variant-numeric: tabular-nums; }
.coin-day .coin-dot { width: 10px; height: 10px; }
.coin-day.today { background: var(--accent-soft); border-color: var(--accent); }
.coin-day.today b { color: var(--accent); }
.coin-day.done { opacity: .5; }
.coin-day.done .coin-dot { background: var(--line); }
.coin-total {
  display: flex; align-items: center; gap: 7px;
  padding: 10px 13px; margin: 0 0 14px; border-radius: 14px;
  background: var(--surface-2);
}
.coin-total .coin-dot { width: 16px; height: 16px; }
.coin-total b { font-size: 20px; font-variant-numeric: tabular-nums; }
.coin-total span { font-size: 12px; color: var(--muted); }
/* The reward line inside the win sheet. Hidden until the server confirms the
   grant, so a level never promises honey it did not pay. */
.win-reward {
  margin: -6px 0 4px; text-align: center;
  font-size: 13px; font-weight: 650; color: var(--accent);
}
/* A coin price the balance cannot cover: still tappable (it explains itself),
   just visibly out of reach. */
.btn.small.is-short { background: var(--surface-2); color: var(--muted); }
.sheet ol { padding-left: 20px; margin: 0 0 6px; font-size: 13px; line-height: 1.6; }
.sheet ol li { margin-bottom: 8px; }
.tutorial-art { height: 118px; border-radius: 16px; background: var(--surface-2); display: grid; place-items: center; margin-bottom: 18px; }
.tutorial-art .demo-grid { display: grid; grid-template-columns: repeat(5, 20px); grid-template-rows: repeat(4, 20px); gap: 3px; }
.tutorial-art .demo-grid i { border-radius: 5px; background: var(--cell); }
.tutorial-art .demo-grid i.on { background: var(--accent); }
.dots { display: flex; gap: 6px; justify-content: center; margin: 18px 0 6px; }
.dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--line); }
.dots i.on { background: var(--accent); width: 18px; border-radius: 4px; }
.link-row { display: flex; justify-content: center; margin-top: 12px; }
.link-row button { font-size: 12px; color: var(--muted); text-decoration: underline; }
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
@keyframes sheetIn { from { opacity: 0; transform: translateY(16px) scale(.97) } to { opacity: 1; transform: none } }

/* toast */
#toast-root { position: fixed; left: 0; right: 0; bottom: calc(var(--tabbar-h) + 18px + env(safe-area-inset-bottom)); z-index: 100; pointer-events: none; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.toast {
  background: var(--ink); color: var(--bg); font-size: 12.5px; font-weight: 600;
  padding: 9px 16px; border-radius: 20px; box-shadow: 0 6px 18px rgba(0,0,0,.22);
  animation: toastIn .2s ease-out;
}
@keyframes toastIn { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }

/* ── Classic mode UI (build spec §4, 6, 9, 11) ─────────────────────────────
   Everything here is pure CSS: no framework, no build step, no runtime deps.
   The game view keeps the board as the only flex-growing element, so adding a
   hint bar or a combo strip can shrink the 8x8 grid but never push it over the
   tray (see fitBoard() in app.js). */
.flow-strip {
  flex: none; display: flex; align-items: center;
  gap: 8px; height: 22px;
}
.flow-strip .combo-banner { flex: 1; height: auto; }
.forgive-dots { flex: none; display: flex; gap: 4px; align-items: center; }
.forgive-dots i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--line); transition: background .18s, transform .18s;
}
.forgive-dots i.on { background: var(--accent); transform: scale(1.25); }

.cell.guide-target {
  box-shadow: inset 0 0 0 2px var(--accent);
  animation: guidePulse 1.3s ease-in-out infinite;
}
@keyframes guidePulse {
  0%, 100% { background: var(--cell); }
  50% { background: var(--accent-soft); }
}
.tray-slot.guide-slot {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
  animation: guidePulseSlot 1.3s ease-in-out infinite;
}
@keyframes guidePulseSlot {
  0%, 100% { box-shadow: 0 0 0 2px var(--accent-soft); }
  50% { box-shadow: 0 0 0 4px var(--accent-soft); }
}
/* Honest game-over feedback: pieces that truly have no legal spot. */
.tray-slot.unplaceable { opacity: .48; border-color: var(--bad); }

.guide-hint {
  flex: none; display: flex; align-items: center; gap: 8px;
  margin: 4px 0 2px; padding: 7px 10px;
  border-radius: 12px; background: var(--accent-soft);
  font-size: 11.5px; line-height: 1.35; color: var(--ink);
}
.guide-hint span { flex: 1; min-width: 0; }
.guide-hint button {
  flex: none; font-size: 11.5px; font-weight: 650;
  color: var(--accent-ink); text-decoration: underline;
}

/* Daily quest cards (shared markup in the leaders view and in the sheet). */
.quest-list, .quest-grid { display: grid; gap: 10px; }
.quest {
  padding: 12px 14px; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--shadow);
}
.quest.done { border-color: var(--good); }
.quest-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; font-size: 13px;
}
.quest-top b { font-weight: 650; }
.quest-top span { color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.quest-bar {
  height: 6px; margin: 9px 0 7px; border-radius: 4px;
  background: var(--surface-2); overflow: hidden;
}
.quest-bar i { display: block; height: 100%; background: var(--accent); border-radius: 4px; transition: width .25s; }
.quest.done .quest-bar i { background: var(--good); }
.quest small { display: block; color: var(--muted); font-size: 11.5px; }
.quest em { display: block; font-style: normal; color: var(--accent); font-size: 11.5px; margin-top: 4px; }
.quest.done em { color: var(--good); }
.quest .btn.small { margin-top: 10px; }

/* Server-driven offer card. Hidden unless /api/offers says otherwise. */
.offer-card {
  display: flex; flex-direction: column; gap: 5px;
  margin: 2px 0 14px; padding: 14px 16px;
  border-radius: var(--radius); border: 1px solid var(--accent);
  background: var(--surface); box-shadow: var(--shadow);
}
.offer-badge {
  align-self: flex-start; padding: 3px 9px; border-radius: 999px;
  background: var(--accent); color: var(--accent-ink);
  font-size: 10px; font-weight: 750; letter-spacing: .07em; text-transform: uppercase;
}
.offer-card b { font-size: 15px; }
.offer-card small { color: var(--muted); font-size: 11.5px; line-height: 1.4; }
.offer-actions { display: flex; align-items: center; gap: 12px; margin-top: 8px; }
.offer-actions .btn { flex: none; }
.link-btn { font-size: 12px; color: var(--muted); text-decoration: underline; }
.go-offer {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  margin-top: 8px; padding: 12px 14px; border-radius: 14px;
  background: var(--accent-soft);
}
.go-offer b { font-size: 13px; }
.go-offer small { color: var(--muted); font-size: 11.5px; }
.go-offer .btn { margin-top: 8px; }

/* ── Journey campaign ──────────────────────────────────────────────────────
   A campaign level replaces the score row with a goal: the target is "collect
   12 amber and 8 blue", not a number of points. Everything below is plain CSS
   driven by the skin variables, so the three bought themes and the Telegram
   dark scheme need no extra rules. Nothing here creates a new stacking context
   around the board, so fitBoard() keeps measuring the same free space. */
.level-hud { display: none; }
.level-hud.show {
  display: flex; flex-direction: column; gap: 6px;
  flex: none; margin: 8px 2px 0;
}
/* While a level is live, the score row, the mode switch and the action bar are
   all meaningless. Hiding them also hands ~90px back to the 8x8 board, which
   matters on a 640px-tall WebView. */
body.mode-journey .score-row,
body.mode-journey .mode-row,
body.mode-journey .game-actions { display: none; }

.level-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.level-head b { font-size: 15px; font-weight: 700; letter-spacing: -.01em; }
.level-head .link-btn { flex: none; font-size: 11.5px; }

.level-goal { display: flex; flex-wrap: wrap; gap: 6px; }
.goal-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 9px; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--line);
  font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums;
}
.goal-chip b { color: var(--ink); font-size: 13px; font-weight: 750; }
.goal-chip.score-chip { font-size: 13px; }
.goal-chip.done { border-color: var(--good); }
.goal-chip.done b { color: var(--good); }
/* "Any colour" has no palette entry, so its dot is the whole palette. */
.goal-dot {
  width: 18px; height: 18px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; line-height: 1; font-style: normal;
  /* The glyph, not the hue, is what identifies the colour: six shades of honey
     are indistinguishable to a colour-blind player (audit U-06). */
  color: #1e1b16; text-shadow: 0 0 2px rgba(255, 255, 255, .9);
  background: conic-gradient(var(--tile-1), var(--tile-2), var(--tile-3),
              var(--tile-4), var(--tile-5), var(--tile-6), var(--tile-1));
}
.goal-dot.c1, .goal-dot.c2, .goal-dot.c3,
.goal-dot.c4, .goal-dot.c5, .goal-dot.c6 { background: var(--c); }

.level-meta {
  display: flex; justify-content: space-between; gap: 10px;
  font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums;
}

/* Star marks are shared by the level grid, the map and the win sheet. */
.star-row { display: flex; gap: 7px; justify-content: center; margin: 6px 0 2px; }
.star-row i { font-style: normal; font-size: 27px; line-height: 1; color: var(--line); }
.star-row i.on { color: var(--accent); text-shadow: 0 2px 10px rgba(232, 161, 28, .35); }

/* ── journey map ─────────────────────────────────────────────────────────── */
.journey-head { margin-bottom: 16px; }
.journey-progress {
  display: flex; align-items: center; gap: 10px; margin-top: 10px;
  font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums;
}
.journey-bar {
  flex: 1; min-width: 60px; height: 8px; border-radius: 5px;
  background: var(--surface-2); overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--line);
}
.journey-bar i {
  display: block; height: 100%; width: 0; border-radius: 5px;
  background: var(--accent); transition: width .3s ease;
}
#journey-count, #journey-stars { white-space: nowrap; }
#journey-stars { color: var(--accent); font-weight: 700; }

.journey-acts { display: grid; gap: 14px; }
.journey-act {
  padding: 12px 12px 14px; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--shadow);
}
.act-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
/* The act title never wraps: "The Warehouses" on two lines reads as a bug. */
.act-head b { flex: none; font-size: 13.5px; letter-spacing: -.01em; white-space: nowrap; }
.act-head small { flex: 1; min-width: 0; color: var(--muted); font-size: 10.5px; text-align: right; line-height: 1.3; }

/* 5 columns x 4 rows keeps each level a >=48px tap target on a 320px screen
   while showing a whole 20-level act without scrolling. */
.level-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 7px; }
/* Phone-first layout: on a tablet a stretched 5-column grid becomes a
   spreadsheet of 136px tiles. Cap the column instead of the font size. */
@media (min-width: 480px) {
  .journey-head, .journey-acts, #journey-note {
    max-width: 404px; margin-left: auto; margin-right: auto;
  }
}
.level-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; aspect-ratio: 1 / 1; padding: 0;
  border-radius: 12px; background: var(--surface-2); border: 1px solid var(--line);
  transition: transform .12s, background .12s, border-color .12s;
}
.level-btn:active { transform: scale(.94); }
.lv-num { font-size: 15px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
.lv-stars { display: flex; align-items: center; gap: 1px; height: 9px; }
.lv-stars i { font-style: normal; font-size: 8px; line-height: 1; color: var(--line); }
.lv-stars i.on { color: var(--accent); }
.level-btn.done { background: var(--accent-soft); border-color: var(--accent); }
/* An uncleared level advertises what its first clear pays. Same slot as the
   star row, so the tile never changes height between states. */
.lv-reward { height: 9px; font-size: 8.5px; font-weight: 700; color: var(--accent); line-height: 9px; }
/* Locked stays clickable on purpose: the tap explains itself with a toast. */
.level-btn.locked { opacity: .42; }
.level-btn.locked .lv-stars { visibility: hidden; }
.level-btn.killed { opacity: .3; }
.level-btn.killed .lv-num { text-decoration: line-through; }

/* ── achievements (developer reference §09) ───────────────────────────────
   Badges only. Tier colour is the whole visual language: bronze / silver /
   gold, dimmed when not earned. No art assets, so nothing to preload. */
.ach-card {
  position: relative; display: flex; flex-direction: column; gap: 9px;
  padding: 13px 14px; margin-top: 12px; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.ach-card:active { transform: scale(.995); }
.ach-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.ach-head b { font-size: 14px; font-weight: 700; }
.ach-head-right { display: inline-flex; align-items: center; gap: 6px; }
.ach-count { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.ach-preview { display: flex; flex-wrap: wrap; gap: 6px; }
.ach-next { margin: 0; font-size: 11.5px; color: var(--muted); line-height: 1.35; }
.ach-next:empty { display: none; }

.ach-badge {
  width: 32px; height: 32px; flex: none; border-radius: 10px;
  display: grid; place-items: center;
  font-size: 13px; font-weight: 780; letter-spacing: -.02em;
  background: var(--surface-2); color: var(--muted);
  border: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.ach-badge.t1 { background: #C08A4A; border-color: #C08A4A; color: #FFF6E8; }
.ach-badge.t2 { background: #9AA3AD; border-color: #9AA3AD; color: #F7FAFC; }
.ach-badge.t3 { background: #D9A017; border-color: #D9A017; color: #2A1B02; }
.ach-badge.locked { background: var(--surface-2); border-color: var(--line); color: var(--muted); opacity: .55; }

/* Inline, not absolutely positioned: an overlapping dot read as a rendering
   bug next to the counter. */
.dot {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--accent); align-self: center;
}
.dot[hidden] { display: none; }

/* The sheet reuses .sheet; only the list is new. */
.ach-group { margin: 0 0 16px; }
.ach-group:last-of-type { margin-bottom: 4px; }
.ach-group-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin: 0 0 6px;
}
.ach-group-head b { font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.ach-group-head span { font-size: 11.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
.ach-item { display: flex; align-items: flex-start; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line); }
.ach-item:last-child { border-bottom: 0; }
.ach-item .ach-badge { width: 38px; height: 38px; border-radius: 12px; font-size: 14px; }
.ach-item-body { flex: 1; min-width: 0; }
.ach-item-body b { display: block; font-size: 13px; font-weight: 600; }
.ach-item-body small { display: block; font-size: 11px; color: var(--muted); line-height: 1.35; margin-top: 1px; }
.ach-track { height: 4px; border-radius: 3px; background: var(--surface-2); overflow: hidden; margin-top: 6px; }
.ach-track i { display: block; height: 100%; border-radius: 3px; background: var(--accent); transition: width .25s ease; }
.ach-item.done .ach-item-body small { color: var(--accent); }
.ach-tick { font-style: normal; font-weight: 700; }
.ach-item.done .ach-track { display: none; }
.ach-note { margin: 14px 0 0; font-size: 11px; color: var(--muted); line-height: 1.45; }

@media (max-height: 700px) {
  .score-main strong { font-size: 32px; }
  .tray { min-height: 70px; }
  .tray-slot { min-height: 68px; }
  .view { padding-top: 4px; }
  .flow-strip { height: 18px; }
  .guide-hint { padding: 5px 9px; margin: 2px 0 0; font-size: 11px; }
  .level-hud.show { gap: 4px; margin-top: 4px; }
  .level-head b { font-size: 14px; }
  .goal-chip { padding: 3px 8px; font-size: 11.5px; }
  .level-meta { font-size: 10.5px; }
  .journey-act { padding: 10px 10px 12px; }
  .level-grid { gap: 6px; }
}
/* Very short/landscape viewports: fixed chrome eats all the height. Give the
   board a usable minimum and let the game view scroll instead of shrinking
   the 8x8 grid to a few pixels. */
@media (max-height: 520px) {
  #view-game.active { overflow-y: auto; }
  .board-wrap { min-height: 300px; }
}
@media (max-width: 360px) {
  .seg { padding: 6px 10px; font-size: 11.5px; }
  .btn { padding: 10px 8px; font-size: 12px; }
  .level-grid { gap: 5px; }
  .lv-num { font-size: 13px; }
  .act-head small { display: none; }
  .star-row i { font-size: 23px; }
}
/* WebView performance: isolate the 64-cell grid and list rows so a single
   class change cannot trigger layout of the whole Mini App on low-end phones. */
.board, .cell { contain: layout style; }
.row, .shop-item, .skin, .journey-act, .level-btn { contain: content; }
.drag-ghost { will-change: left, top; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
