/* ============================================================
   Grid — white + aqua, round, minimal
   ============================================================ */

:root{
  --bg:            #ffffff;
  --bg-soft:       #f2fafb;
  --cell-empty:    #edf6f7;
  --ink:           #0b1a1d;
  --ink-2:         #35555a;
  --muted:         #7d989c;
  --line:          #e2eef0;

  --aqua:          #00c2cb;
  --aqua-deep:     #049aa4;
  --aqua-mid:      #45d6db;
  --aqua-soft:     #cdf1f3;
  --aqua-wash:     #e8fafb;

  /* accent used as TEXT on a page background. Split from --aqua-deep because
     that one also paints gradients, which need to stay dark behind white
     labels while text needs to get lighter in dark mode. Theme.apply() sets
     both. */
  --accent-ink:    #049aa4;

  /* raised surfaces: cards, sheets, modals, chips, the tab bar */
  --surface:       #ffffff;
  --surface-2:     #ffffff;
  --scrim:         rgba(255,255,255,.74);
  --scrim-solid:   rgba(255,255,255,.72);
  --badge-bg:      rgba(255,255,255,.94);
  --tabs-bg:       rgba(255,255,255,.86);
  --tray-grad:     linear-gradient(180deg,#fbfeff,#f3fbfc);
  --tray-sheen:    rgba(255,255,255,.9);
  --brand-start:   #0a5560;
  --bad:           #ffdede;
  --danger:        #e05252;
  /* The filled destructive button needs its own pair. --danger is tuned to be
     legible AS TEXT on the page, which makes it a poor fill: in dark mode it
     is a light pink, and white on it measured 2.19:1. */
  --danger-fill:   #c62f2f;
  --danger-on:     #ffffff;
  --toast-bg:      var(--ink);
  --toast-fg:      #ffffff;

  --r-sm: 10px;
  --r-md: 16px;
  --r-lg: 24px;
  --r-xl: 32px;

  --shadow-sm: 0 2px 8px rgba(10,60,66,.06);
  --shadow-md: 0 8px 26px rgba(10,60,66,.10);
  --shadow-lg: 0 20px 60px rgba(10,60,66,.16);

  --ease: cubic-bezier(.22,1,.36,1);
  --ease-back: cubic-bezier(.34,1.56,.64,1);

  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
}

*{ box-sizing:border-box; -webkit-tap-highlight-color:transparent; }

/* No visible scrollbars. This is a phone app: scrolling still works exactly
   as before, but the indicator itself is desktop chrome and reads as grit on
   iOS. Applied globally so a new scrollable region cannot reintroduce one. */
*{ scrollbar-width:none; -ms-overflow-style:none; }
*::-webkit-scrollbar{ width:0; height:0; display:none; }

html,body{ height:100%; }
body{
  margin:0;
  background:var(--bg);
  color:var(--ink);
  font-family:-apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",Inter,Roboto,system-ui,sans-serif;
  font-size:16px;
  line-height:1.4;
  -webkit-font-smoothing:antialiased;
  overscroll-behavior:none;
  user-select:none;
}

#app{
  max-width:460px;
  margin:0 auto;
  min-height:100%;
  padding:calc(var(--safe-t) + 10px) 20px calc(var(--safe-b) + 20px);
  display:flex;
  flex-direction:column;
}

.hidden{ display:none !important; }

/* ---------- top bar ---------- */
#topbar{
  display:flex; align-items:center; gap:12px;
  height:52px; flex:0 0 auto;
}
.brand{ display:flex; align-items:center; gap:9px; margin-right:auto; }
/* Four white rounded squares on the aqua gradient — four puzzles, one grid.
   Matches tools/make-icons.py, which draws the same mark for the app icons;
   before this the header showed a blank gradient tile that looked nothing
   like the icon on the home screen. */
/* Four white rounded squares on the aqua gradient — four puzzles, one grid.
   The numbers are lifted straight from tools/make-icons.py so the header
   mark and the home-screen icon are the same drawing at different sizes:
   the 2×2 block is 52% of the tile (padding 24%), the gap between squares
   is 8.5% of that block, each square's corner is 27% of its own side, and
   the tile's corner is 22.37%. Expressed in px because percentage padding
   resolves against the parent's width, not this element's. */
.brand-mark{
  width:22px; height:22px; border-radius:4.9px;
  background:linear-gradient(140deg,var(--aqua-mid),var(--aqua-deep));
  box-shadow:0 3px 10px rgba(0,194,203,.4);
  display:grid; grid-template-columns:1fr 1fr; grid-template-rows:1fr 1fr;
  gap:1px; padding:5.3px;
}
.brand-mark i{ background:#fff; border-radius:27%; }
.brand-name{
  font-weight:680; letter-spacing:-.035em; font-size:19px;
  background:linear-gradient(115deg,var(--brand-start) 0%,var(--aqua-deep) 45%,var(--aqua) 100%);
  -webkit-background-clip:text; background-clip:text;
  -webkit-text-fill-color:transparent; color:transparent;
}

/* Lives in the game screen, right-aligned above the stage dots. No pill:
   at a glance mid-run you want the number, and a filled capsule in the top
   bar competed with the board for attention. Same type treatment as the
   rest of the game chrome. */
.timer{
  font-variant-numeric:tabular-nums;
  font-weight:650; font-size:16px; letter-spacing:-.02em;
  color:var(--accent-ink);
  text-align:center;
  line-height:1;
}
/* Centred on its own row above the stage dots. It is back in normal flow,
   which costs about 22px — paid for by the trims to .stage-head, .stage-dots
   and .tray below, so the game screen is no taller than before. Sunday's 8×8
   is the case that matters: verify on 375×667 after touching any of them. */
.stage-timer{
  display:flex; justify-content:center; align-items:center;
  height:18px; margin-bottom:6px;
}

.avatar{
  width:34px; height:34px; border-radius:50%;
  border:1.5px solid var(--line); background:var(--bg-soft);
  color:var(--ink-2); font-weight:650; font-size:14px;
  display:grid; place-items:center; cursor:pointer; padding:0;
  transition:transform .18s var(--ease-back), box-shadow .2s;
  /* the mark inside is a square; without this its corners sit outside the
     circle (a 34px circle only inscribes a 24px square). */
  overflow:hidden;
}
.avatar > *, .avatar svg{ border-radius:inherit; display:block; }
.avatar:active{ transform:scale(.93); }
.avatar.is-in{
  background:linear-gradient(140deg,var(--aqua-mid),var(--aqua-deep));
  border-color:transparent; color:#fff;
  box-shadow:0 4px 14px rgba(0,194,203,.38);
}
.avatar-lg{ width:52px; height:52px; font-size:20px; }

/* ---------- screens ---------- */
.screen{ flex:1 1 auto; display:flex; flex-direction:column; min-height:0; }
#game{ justify-content:center; padding-bottom:12px; }

/* ---------- landing ---------- */
.hero{ margin:auto 0; padding-bottom:6vh; }
.hero-date{
  font-size:13px; font-weight:600; letter-spacing:.08em; text-transform:uppercase;
  color:var(--accent-ink); margin-bottom:14px;
}
.hero-title{
  font-size:clamp(30px,9vw,40px); line-height:1.08; letter-spacing:-.035em;
  font-weight:680; margin:0 0 26px;
}
.hero-steps{ display:flex; flex-direction:column; gap:10px; margin-bottom:34px; }
.step{ display:flex; align-items:center; gap:12px; color:var(--ink-2); font-size:15px; }
.step-n{
  width:24px; height:24px; border-radius:8px; flex:0 0 auto;
  background:var(--aqua-wash); color:var(--accent-ink);
  display:grid; place-items:center; font-size:12px; font-weight:700;
}
.hero-note{ text-align:center; color:var(--muted); font-size:13px; margin:14px 0 0; }

/* ---------- buttons ---------- */
.btn{
  font:inherit; font-weight:600; border:0; cursor:pointer;
  border-radius:var(--r-md); padding:12px 18px;
  transition:transform .16s var(--ease-back), background .18s, opacity .18s;
}
.btn:active{ transform:scale(.97); }
.btn-lg{ width:100%; padding:17px 20px; font-size:17px; border-radius:var(--r-lg); }
.btn-sm{ padding:7px 13px; font-size:13px; border-radius:12px; }
.btn-primary{
  background:linear-gradient(140deg,var(--aqua-mid),var(--aqua-deep));
  color:#fff; box-shadow:0 8px 22px rgba(0,194,203,.34);
}
.btn-ghost{ background:transparent; color:var(--muted); width:100%; }
.btn-ghost.btn-sm{ width:auto; }
.btn-ghost:hover{ color:var(--ink-2); }
.btn-soft{ background:var(--aqua-wash); color:var(--accent-ink); }
.btn[disabled]{ opacity:.4; pointer-events:none; }

/* ---------- stage head ---------- */
.stage-head{ position:relative; flex:0 0 auto; text-align:center; padding:0 0 9px; }
.stage-dots{ display:flex; gap:6px; justify-content:center; margin-bottom:9px; }
.stage-dots i{
  width:22px; height:4px; border-radius:2px; background:var(--line);
  transition:background .35s var(--ease), width .35s var(--ease);
}
.stage-dots i.on{ background:var(--aqua); width:30px; }
.stage-dots i.done{ background:var(--aqua-soft); }
.stage-title{ font-size:20px; font-weight:650; letter-spacing:-.02em; }
.stage-hint{ font-size:13.5px; color:var(--muted); margin-top:4px; min-height:19px; }

/* ---------- board ---------- */
.board-wrap{
  flex:0 0 auto; display:grid; place-items:center; position:relative;
  margin:0 auto;
}
.board{
  position:relative;
  display:grid;
  grid-template-columns:repeat(6,1fr);
  grid-template-rows:repeat(6,1fr);
  touch-action:none;
  border-radius:var(--r-lg);
}
.cell{ position:relative; }
.cell::after{
  content:''; position:absolute; inset:var(--cell-gap,3px);
  border-radius:var(--cell-r,10px);
  background:var(--c,var(--cell-empty));
  transition:background .18s var(--ease), transform .2s var(--ease-back), opacity .2s;
}
.cell.ok::after{ background:var(--aqua-soft); }
.cell.bad::after{ background:var(--bad); }
.cell.pop::after{ transform:scale(.9); }

/* wipe */
.cell.wipe::after{
  animation:wipeCell .5s var(--ease) var(--d,0ms) both;
}
/* The board is washed clean, not demolished. It used to flash every cell to
   a flat aqua and shrink it to scale(.3) — the grid visibly vanished and the
   next game built a new one, which is what made the seam between games feel
   like a scene change. Now each cell starts from its OWN final colour (the
   one prepareWipe painted), brightens as the diagonal wave passes over it,
   and settles back to empty at full size. The 6×6 never leaves the screen,
   so the next game simply fills the same grid. */
@keyframes wipeCell{
  0%  { transform:scale(1);    background:var(--c,var(--cell-empty)); }
  30% { transform:scale(1.07); background:var(--aqua-mid); }
  62% { transform:scale(1.02); background:var(--aqua-soft); }
  100%{ transform:scale(1);    background:var(--cell-empty); }
}

.board-badge{
  position:absolute; inset:0; display:grid; place-items:center;
  opacity:0; transform:scale(.85); pointer-events:none; z-index:5;
}
.board-badge span{
  font-size:19px; font-weight:700; letter-spacing:-.02em; color:var(--accent-ink);
  background:var(--badge-bg); padding:10px 22px; border-radius:999px;
  box-shadow:0 10px 30px rgba(10,60,66,.16);
}
.board-badge.show{ animation:badgePop .9s var(--ease) forwards; }
@keyframes badgePop{
  0%{opacity:0; transform:scale(.8)}
  22%{opacity:1; transform:scale(1)}
  70%{opacity:1; transform:scale(1)}
  100%{opacity:0; transform:scale(1.1)}
}

.board.shake{ animation:shake .4s; }
.cell.shake::after{ animation:shake .4s; }
@keyframes shake{
  0%,100%{ transform:translateX(0) }
  15%{ transform:translateX(-7px) }
  30%{ transform:translateX(6px) }
  45%{ transform:translateX(-4px) }
  60%{ transform:translateX(3px) }
  80%{ transform:translateX(-2px) }
}

/* ---------- tray ---------- */
/* the "bank" — where unplaced pieces and colors wait */
.tray.bank{
  border:1.5px solid var(--line);
  background:var(--tray-grad);
  border-radius:var(--r-lg);
  padding:15px 13px;
  box-shadow:inset 0 1px 0 var(--tray-sheen);
}
.tray{
  flex:0 0 auto;
  margin-top:13px;
  display:flex; align-items:center; justify-content:center; align-content:center;
  gap:14px; flex-wrap:wrap;
  touch-action:none;
}
/* Flood's growth meter. flex-basis 100% puts it on its own row above the
   swatches, which the tray's flex-wrap already handles. The fill is painted
   with the blob's live colour from JS, so it belongs to the board rather
   than the accent palette; the number beside it carries the same reading,
   so nothing here is colour-only. */
.growth{ flex:1 0 100%; display:flex; align-items:center; gap:10px; margin-bottom:2px; }
.growth-track{
  flex:1 1 auto; height:8px; border-radius:99px;
  background:var(--bg-soft); overflow:hidden;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.16);
}
.growth-fill{
  display:block; height:100%; width:0; border-radius:99px;
  transition:width .34s var(--ease-back), background .28s ease;
}
.growth-label{
  font-size:12.5px; font-weight:600; color:var(--ink-2);
  min-width:62px; text-align:right; font-variant-numeric:tabular-nums;
}

.tray-slot{ position:relative; transition:opacity .2s, transform .3s var(--ease-back); }
.tray-slot.taken{ opacity:0; pointer-events:none; transform:scale(.6); }

.controls{
  flex:0 0 auto; display:flex; gap:10px; justify-content:center; align-items:center;
  min-height:44px; padding-top:12px;
}
.moves{ display:flex; gap:5px; align-items:center; }
.moves i{
  width:9px; height:9px; border-radius:50%; background:var(--aqua);
  transition:background .3s, transform .3s var(--ease-back);
}
.moves i.spent{ background:var(--line); transform:scale(.75); }
.moves-label{ font-size:13px; color:var(--muted); margin-right:6px; }

/* ---------- pieces (game 1) ---------- */
#dragLayer{ position:fixed; inset:0; pointer-events:none; z-index:60; }
.piece{ position:absolute; left:0; top:0; }
.piece .blk{
  position:absolute;
  background:var(--pc,var(--aqua));
}
.piece.lift{
  filter:drop-shadow(0 14px 18px rgba(10,60,66,.28));
}
.piece.placed{ animation:snap .26s var(--ease-back); }
@keyframes snap{
  0%{ transform:scale(1.07) }
  60%{ transform:scale(.975) }
  100%{ transform:scale(1) }
}
.piece.ghost{ opacity:.28; }

/* ---------- path (game 2) ---------- */
.pathSvg{ position:absolute; inset:0; pointer-events:none; }
.node{
  position:absolute; display:grid; place-items:center;
  border-radius:50%; font-weight:700; font-size:15px;
  background:var(--surface); color:var(--accent-ink);
  border:2.5px solid var(--aqua); pointer-events:none;
  transition:background .2s, color .2s, transform .25s var(--ease-back);
}
.node.done{ background:var(--aqua-deep); color:#fff; border-color:var(--aqua-deep); transform:scale(1.04); }

/* ---------- swatches (game 3) ---------- */
.swatch{
  width:52px; height:52px; border-radius:50%;
  background:var(--sc); box-shadow:var(--shadow-sm);
  position:relative; transition:transform .18s var(--ease-back);
  touch-action:none;
}
.swatch:active{ transform:scale(.9); }
.swatch.dim{ transform:scale(.74); box-shadow:none; }
.cell.origin::after{
  box-shadow:inset 0 0 0 3px rgba(255,255,255,.9), inset 0 0 0 4.5px rgba(0,0,0,.12);
}
.board.hot{ box-shadow:0 0 0 3px var(--aqua-soft); }

/* ---------- recall (game 4) ---------- */
.cell.lit::after{
  background:var(--aqua) !important;
  transform:scale(1.03);
  box-shadow:0 6px 18px rgba(0,194,203,.42);
}
.cell.shaded::after{ background:var(--aqua-soft) !important; }

/* ---------- results ---------- */
.result-card{ margin:auto 0; padding-bottom:4vh; }
.result-kicker{
  font-size:12.5px; font-weight:650; letter-spacing:.09em; text-transform:uppercase;
  color:var(--accent-ink); text-align:center;
}
.result-time{
  font-size:clamp(48px,16vw,64px); font-weight:700; letter-spacing:-.045em;
  text-align:center; font-variant-numeric:tabular-nums; margin:2px 0 22px;
}
.splits{ display:flex; flex-direction:column; gap:2px; margin-bottom:26px; }
.split{
  display:flex; align-items:center; gap:12px;
  padding:11px 14px; border-radius:14px;
}
.split:nth-child(odd){ background:var(--bg-soft); }
.split-n{
  width:22px; height:22px; border-radius:7px; flex:0 0 auto;
  background:var(--aqua-wash); color:var(--accent-ink);
  display:grid; place-items:center; font-size:11.5px; font-weight:700;
}
.split-name{ flex:1 1 auto; font-size:14.5px; color:var(--ink-2); }
.split-time{ font-variant-numeric:tabular-nums; font-weight:650; font-size:14.5px; }

.lb{ margin-bottom:22px; }
.lb-head{
  display:flex; align-items:center; justify-content:space-between;
  font-size:12.5px; font-weight:650; letter-spacing:.09em; text-transform:uppercase;
  color:var(--muted); margin-bottom:8px; padding-left:2px;
}
.lb-list{ display:flex; flex-direction:column; gap:6px; }
.lb-row{
  display:flex; align-items:center; gap:11px;
  padding:11px 14px; border-radius:16px; background:var(--bg-soft);
}
.lb-row.me{ background:var(--aqua-wash); }
.lb-rank{ width:16px; font-size:13px; font-weight:700; color:var(--muted); }
.lb-row.me .lb-rank{ color:var(--accent-ink); }
.lb-name{ flex:1 1 auto; font-size:14.5px; font-weight:550; }
.lb-time{ font-variant-numeric:tabular-nums; font-weight:650; font-size:14.5px; }
.lb-time.pending{ color:var(--muted); font-weight:500; font-size:13px; }

.share-prompt{ text-align:center; margin-bottom:18px; }
.share-title{ font-weight:650; font-size:15px; }
.share-sub{ color:var(--muted); font-size:13.5px; margin-top:2px; }

/* ---------- sheet ---------- */
.backdrop{
  position:fixed; inset:0; background:rgba(8,32,35,.32);
  backdrop-filter:blur(3px); z-index:70;
  animation:fade .22s var(--ease);
}
@keyframes fade{ from{opacity:0} to{opacity:1} }

.sheet{
  position:fixed; left:0; right:0; bottom:0; z-index:80;
  background:var(--surface); border-radius:var(--r-xl) var(--r-xl) 0 0;
  padding:10px 22px calc(var(--safe-b) + 26px);
  max-width:460px; margin:0 auto;
  box-shadow:var(--shadow-lg);
  animation:sheetUp .34s var(--ease);
}
@keyframes sheetUp{ from{ transform:translateY(102%) } to{ transform:translateY(0) } }
/* the bar itself is drawn by .sheet .sheet-grab::before (see the sticky
   rule further down); this keeps the element full-width so it is grabbable */
.sheet-grab{ height:4px; margin:0 auto 18px; }
.sheet-title{ font-size:22px; font-weight:680; letter-spacing:-.025em; margin:0 0 4px; }
.sheet-sub{ color:var(--muted); font-size:14px; margin:0 0 20px; }
.sheet-fine{ color:var(--muted); font-size:12px; text-align:center; margin:14px 0 0; line-height:1.45; }

.field{ display:block; margin-bottom:14px; }
.field span{ display:block; font-size:12.5px; font-weight:600; color:var(--ink-2); margin-bottom:6px; }
/* type=email is the magic-link field; keep it on the same rule so the two
   never drift apart visually. The 16px is deliberate — anything smaller and
   iOS zooms the page on focus. */
input[type=text], input[type=email]{
  width:100%; font:inherit; font-size:16px; color:var(--ink);
  background:var(--bg-soft); border:1.5px solid transparent;
  border-radius:var(--r-md); padding:13px 15px; outline:none;
  transition:border-color .18s, background .18s;
}
input[type=text]:focus, input[type=email]:focus{ border-color:var(--aqua); background:var(--surface-2); }
.field-error{ color:var(--danger); font-size:13px; margin:-6px 0 12px; }

.profile-row{ display:flex; align-items:center; gap:14px; margin-bottom:24px; }
.profile-name{ font-size:19px; font-weight:670; letter-spacing:-.02em; }
.profile-meta{ font-size:13.5px; color:var(--muted); }

.friends{ margin-bottom:14px; }
.friends-head{
  font-size:12.5px; font-weight:650; letter-spacing:.09em; text-transform:uppercase;
  color:var(--muted); margin-bottom:9px;
}
.friend-add{ display:flex; gap:8px; margin-bottom:12px; }
.friend-add input{ flex:1 1 auto; }
.friend-list{ display:flex; flex-direction:column; gap:6px; max-height:180px; overflow:auto; }
.friend-row{
  display:flex; align-items:center; gap:11px;
  background:var(--bg-soft); border-radius:14px; padding:10px 13px;
}
.friend-dot{
  width:28px; height:28px; border-radius:50%; flex:0 0 auto;
  background:linear-gradient(140deg,var(--aqua-mid),var(--aqua-deep)); color:#fff;
  display:grid; place-items:center; font-size:12px; font-weight:650;
}
.friend-name{ flex:1 1 auto; font-size:14.5px; }
.friend-x{ background:none; border:0; color:var(--muted); font-size:18px; cursor:pointer; padding:0 4px; }
.empty-note{ color:var(--muted); font-size:13.5px; padding:6px 2px; }

/* ---------- finish modal ---------- */
.modal{
  position:fixed; inset:0; z-index:90; display:grid; place-items:center;
  background:var(--scrim-solid); backdrop-filter:blur(10px);
  animation:fade .25s var(--ease);
  padding:24px;
}
.modal-card{
  background:var(--surface); border-radius:var(--r-xl); box-shadow:var(--shadow-lg);
  padding:34px 26px 26px; width:100%; max-width:360px; text-align:center;
  max-height:86vh; overflow-y:auto;
  animation:cardIn .45s var(--ease-back);
}
@keyframes cardIn{ from{ transform:translateY(22px) scale(.94); opacity:0 } to{ transform:none; opacity:1 } }
.modal-badge{
  display:inline-block; font-size:12px; font-weight:700; letter-spacing:.1em; text-transform:uppercase;
  color:var(--accent-ink); background:var(--aqua-wash); padding:6px 13px; border-radius:999px;
}
.modal-time{
  font-size:60px; font-weight:700; letter-spacing:-.05em;
  font-variant-numeric:tabular-nums; margin:14px 0 2px;
}
.modal-sub{ color:var(--muted); font-size:14px; margin-bottom:24px; }

#confetti{
  position:fixed; inset:0; pointer-events:none; z-index:95; display:none;
}

/* ---------- toast ---------- */
.toast{
  position:fixed; left:50%; bottom:calc(var(--safe-b) + 28px);
  transform:translate(-50%,20px); opacity:0; pointer-events:none; z-index:100;
  background:var(--toast-bg); color:var(--toast-fg); font-size:14px; font-weight:550;
  padding:11px 18px; border-radius:999px; box-shadow:var(--shadow-md);
  transition:opacity .24s var(--ease), transform .24s var(--ease);
}
.toast.show{ opacity:1; transform:translate(-50%,0); }

@media (min-width:520px){
  .tray{ gap:18px; }
}


/* ============================================================
   Added: tabs, stats, boards, badges, settings, tutorial,
   colorblind glyphs, practice, level ramp.
   ============================================================ */

#app{ padding-bottom:calc(var(--safe-b) + 86px); }
body.playing #app{ padding-bottom:calc(var(--safe-b) + 20px); }

/* ---------- tab bar ---------- */
.tabs{
  position:fixed; left:0; right:0; bottom:0; z-index:40;
  max-width:460px; margin:0 auto;
  display:grid; grid-template-columns:repeat(3,1fr);
  background:var(--tabs-bg); backdrop-filter:blur(14px);
  border-top:1px solid var(--line);
  padding:8px 10px calc(var(--safe-b) + 8px);
}
.tabs button{
  background:none; border:0; font:inherit; cursor:pointer;
  font-size:11.5px; font-weight:600; color:var(--muted);
  display:flex; flex-direction:column; align-items:center; gap:3px;
  padding:6px 0; border-radius:14px; transition:color .2s;
}
.tabs button .ti{ font-size:17px; line-height:1; }
.tabs button.on{ color:var(--accent-ink); }

/* ---------- landing extras ---------- */
.hero-top{ display:flex; align-items:center; gap:10px; margin-bottom:14px; }
.hero-date{ margin-bottom:0; }
.streak-chip{
  font-size:11.5px; font-weight:700; letter-spacing:.02em;
  color:var(--accent-ink); background:var(--aqua-wash);
  padding:4px 10px; border-radius:999px; white-space:nowrap;
}
.level{ display:flex; align-items:center; gap:10px; margin:0 0 22px; }
.level-label{ font-size:13px; font-weight:600; color:var(--ink-2); }
.level-dots{ display:flex; gap:4px; margin-left:auto; }
.level-dots i{ width:7px; height:7px; border-radius:50%; background:var(--line); }
.level-dots i.on{ background:var(--aqua); }
.level.sunday .level-label{ color:var(--accent-ink); font-weight:700; }

.btn-block{ width:100%; }
.practice{ margin-top:30px; padding-top:22px; border-top:1px solid var(--line); }
.practice-head{
  font-size:12.5px; font-weight:650; letter-spacing:.09em; text-transform:uppercase;
  color:var(--muted); margin-bottom:10px;
}
.tag{
  font-size:9.5px; letter-spacing:.06em; background:var(--bg-soft);
  padding:3px 7px; border-radius:999px; margin-left:6px; color:var(--muted);
}
/* Both practice rows fit their width rather than scrolling. With the
   scrollbar hidden a scrolling row just looks like a clipped one, and both
   sets are fixed and small — seven days, four games — so there is nothing
   to page through. Chips share the space equally; min-width:0 lets them
   shrink under their text instead of forcing an overflow. */
.levels{ display:flex; gap:6px; margin-top:10px; justify-content:center; }
.level-chip{
  flex:1 1 0; min-width:0; border:1px solid var(--line); background:var(--surface-2); cursor:pointer;
  border-radius:14px; padding:8px 4px; font:inherit; color:var(--ink-2);
  display:flex; flex-direction:column; align-items:center; gap:1px;
  transition:border-color .18s, transform .16s var(--ease-back);
}
.level-chip:active{ transform:scale(.95); }
.level-chip b{ font-size:12.5px; font-weight:650; }
.level-chip span{ font-size:10.5px; color:var(--muted); }

/* The four games are a fixed set, so this row never scrolls: the chips
   share the width evenly and stay centred. `min-width:0` lets them shrink
   below their text width instead of forcing an overflow. */
.abandon{ margin-top:10px; }

/* ---------- shared page furniture ---------- */
.page-title{ font-size:26px; font-weight:680; letter-spacing:-.03em; margin:6px 0 18px; }
.section-label{
  font-size:12px; font-weight:650; letter-spacing:.09em; text-transform:uppercase;
  color:var(--muted); margin:22px 0 9px;
}
.muted-inline{ color:var(--muted); font-weight:600; letter-spacing:0; text-transform:none; }
.result-sub{ text-align:center; color:var(--muted); font-size:13px; margin:-14px 0 20px; }

/* ---------- stats ---------- */
.stat-row{ display:grid; grid-template-columns:repeat(3,1fr); gap:8px; }
.stat-cell{ background:var(--bg-soft); border-radius:18px; padding:14px 12px; text-align:center; }
.stat-val{ font-size:21px; font-weight:700; letter-spacing:-.03em; font-variant-numeric:tabular-nums; }
.stat-key{ font-size:11.5px; color:var(--muted); margin-top:2px; }

.histogram{
  display:grid; grid-template-columns:repeat(6,1fr); gap:8px;
  height:130px; align-items:end;
}
.hbar{ display:flex; flex-direction:column; justify-content:flex-end; height:100%; }
.hbar-fill{
  background:linear-gradient(180deg,var(--aqua-mid),var(--aqua));
  border-radius:10px 10px 6px 6px; position:relative; min-height:3px;
}
.hbar-n{
  position:absolute; top:-19px; left:0; right:0; text-align:center;
  font-size:11px; font-weight:650; color:var(--accent-ink);
}
.hbar-k{ text-align:center; font-size:10.5px; color:var(--muted); margin-top:6px; }
/* the "no runs yet" note is a child of the 6-column grid: let it span. */
.histogram .empty-note{ grid-column:1 / -1; align-self:center; text-align:center; }

/* ---------- badges ---------- */
.badge-grid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
.badge{
  background:var(--bg-soft); border-radius:18px; padding:13px;
  opacity:.5; transition:opacity .2s;
}
.badge.has{ opacity:1; background:var(--aqua-wash); }
.badge-dot{
  width:26px; height:26px; border-radius:9px; margin-bottom:8px;
  display:grid; place-items:center; font-size:12px; font-weight:700;
  background:var(--line); color:var(--muted);
}
.badge.has .badge-dot{
  background:linear-gradient(140deg,var(--aqua-mid),var(--aqua-deep)); color:#fff;
}
.badge-name{ font-size:13.5px; font-weight:650; letter-spacing:-.01em; }
.badge-desc{ font-size:11.5px; color:var(--muted); line-height:1.35; margin-top:2px; }

.new-badges{ display:flex; flex-direction:column; gap:6px; margin-bottom:18px; }
.new-badge{
  background:var(--aqua-wash); border-radius:14px; padding:9px 12px; text-align:left;
  animation:cardIn .5s var(--ease-back) both;
}
.nb-name{ display:block; font-size:13px; font-weight:700; color:var(--accent-ink); }
.nb-desc{ display:block; font-size:11.5px; color:var(--ink-2); }

/* ---------- boards ---------- */
.segmented{
  display:grid; grid-template-columns:repeat(3,1fr); gap:4px;
  background:var(--bg-soft); border-radius:16px; padding:4px; margin-bottom:16px;
}
.segmented button{
  font:inherit; font-size:13px; font-weight:600; border:0; cursor:pointer;
  background:transparent; color:var(--muted); padding:9px 4px; border-radius:12px;
  transition:background .2s, color .2s;
}
.segmented button.on{ background:var(--surface-2); color:var(--ink); box-shadow:var(--shadow-sm); }
.lb-av{ width:26px; height:26px; flex:0 0 auto; display:block; line-height:0; }
.lb-av svg{ border-radius:8px; display:block; }

/* ---------- share preview ---------- */
/* The preview now carries per-game splits rather than emoji bars, so it is
   read as text: no letter-spacing, tabular figures so the times line up, and
   the block centred while the lines themselves stay left-aligned. */
.grid-preview{
  display:table; margin:0 auto 22px;
  text-align:left; line-height:1.7; font-size:15px;
  font-variant-numeric:tabular-nums; color:var(--ink-2);
}
.grid-preview .gp-total{ color:var(--ink); font-weight:650; }
.grid-preview .gp-rule{ color:var(--line); letter-spacing:-1px; }

/* ---------- settings ---------- */
.settings{ margin-top:22px; padding-top:6px; border-top:1px solid var(--line); }
.swatch-row{ display:flex; gap:9px; flex-wrap:wrap; }
.tsw{
  width:34px; height:34px; border-radius:50%; border:0; cursor:pointer; padding:0;
  box-shadow:0 0 0 0 var(--aqua-soft); transition:box-shadow .2s, transform .18s var(--ease-back);
}
.tsw.on{ box-shadow:0 0 0 3px var(--bg), 0 0 0 5px var(--aqua); transform:scale(1.06); }

.chips{ display:flex; gap:7px; flex-wrap:wrap; }
.chip{
  font:inherit; font-size:13px; font-weight:600; cursor:pointer;
  border:1px solid var(--line); background:var(--surface-2); color:var(--ink-2);
  padding:8px 14px; border-radius:999px; transition:all .18s;
}
.chip.on{ background:var(--aqua-wash); border-color:transparent; color:var(--accent-ink); }

.toggle{
  display:flex; align-items:center; gap:14px; padding:14px 0;
  border-bottom:1px solid var(--line); cursor:pointer;
}
.toggle:last-child{ border-bottom:0; }
.toggle span{ flex:1 1 auto; font-size:14.5px; font-weight:550; }
.toggle em{ display:block; font-style:normal; font-size:12px; color:var(--muted); font-weight:400; margin-top:1px; }
.toggle input{ position:absolute; opacity:0; pointer-events:none; }
.toggle i{
  flex:0 0 auto; width:46px; height:28px; border-radius:999px; background:var(--line);
  position:relative; transition:background .22s var(--ease);
}
.toggle i::after{
  content:''; position:absolute; top:3px; left:3px; width:22px; height:22px;
  border-radius:50%; background:#fff; box-shadow:var(--shadow-sm);
  transition:transform .22s var(--ease-back);
}
.toggle input:checked + i{ background:var(--aqua); }
.toggle input:checked + i::after{ transform:translateX(18px); }

.profile-id{ flex:1 1 auto; }

/* ---------- colorblind ---------- */
body.cb .cell[data-glyph]::before{
  content:attr(data-glyph);
  position:absolute; inset:0; z-index:2;
  display:grid; place-items:center;
  font-size:calc(var(--cell-r,10px) * 1.5);
  color:rgba(255,255,255,.92);
  text-shadow:0 1px 2px rgba(0,0,0,.35);
  pointer-events:none;
}
body.cb .swatch[data-glyph]::after{
  content:attr(data-glyph);
  position:absolute; inset:0; display:grid; place-items:center;
  font-size:20px; color:rgba(255,255,255,.95);
  text-shadow:0 1px 2px rgba(0,0,0,.35);
}

/* ---------- tutorial ---------- */
.tut{
  position:fixed; inset:0; z-index:88; display:grid; place-items:center; padding:26px;
  background:var(--scrim); backdrop-filter:blur(10px);
  animation:fade .22s var(--ease);
}
.tut-card{
  background:var(--surface); border-radius:var(--r-xl); box-shadow:var(--shadow-lg);
  padding:28px 24px 22px; width:100%; max-width:340px;
  animation:cardIn .42s var(--ease-back);
}
.tut-kicker{
  font-size:11.5px; font-weight:700; letter-spacing:.1em; text-transform:uppercase;
  color:var(--accent-ink);
}
.tut-title{ font-size:22px; font-weight:680; letter-spacing:-.025em; margin:6px 0 8px; }
.tut-body{ font-size:14.5px; color:var(--ink-2); line-height:1.5; margin:0 0 22px; }

.more-badges{ font-size:12px; color:var(--muted); font-weight:600; padding-top:2px; }

#boards .lb-list{ margin-bottom:18px; }
#boardEmpty{ text-align:center; margin-bottom:16px; }

.tray.six{ gap:10px; }
.tray.six .swatch{ width:46px; height:46px; }

/* ---------- install prompt ---------- */
.install-btn{
  display:block; margin:10px auto 0; width:auto;
  color:var(--accent-ink); font-weight:600;
}

/* ============================================================
   Dark mode

   Neutrals only. The accent tokens (--aqua*, --accent-ink) are
   computed per palette in js/theme.js, because they have to be
   derived from whichever of the 8 accents is active.

   body.dark is set by Theme.apply() -- it resolves the "System"
   preference itself and re-applies on OS change, so there is no
   @media block here to disagree with an explicit Light choice.
   ============================================================ */
body.dark{
  --bg:            #0b1214;
  --bg-soft:       #141f22;
  --cell-empty:    #1c2b2f;
  --ink:           #e6f2f3;
  --ink-2:         #a9c5c9;
  --muted:         #6d888d;
  --line:          #24363b;

  --surface:       #131e21;
  --surface-2:     #1a272b;
  --scrim:         rgba(8,15,17,.74);
  --scrim-solid:   rgba(8,15,17,.72);
  --badge-bg:      rgba(19,30,33,.94);
  --tabs-bg:       rgba(11,18,20,.86);
  --tray-grad:     linear-gradient(180deg,#162326,#111c1f);
  --tray-sheen:    rgba(255,255,255,.05);
  --brand-start:   #9fe4e9;
  --bad:           #5a2328;
  --danger:        #ff8f8f;
  --danger-fill:   #ff8f8f;
  --danger-on:     #2a1114;
  --toast-bg:      #1f3034;
  --toast-fg:      #e6f2f3;

  --shadow-sm: 0 2px 8px rgba(0,0,0,.34);
  --shadow-md: 0 8px 26px rgba(0,0,0,.44);
  --shadow-lg: 0 20px 60px rgba(0,0,0,.58);

  color-scheme: dark;
}

/* the piece silhouette's bottom bevel reads as a black smear on dark */
body.dark .piece .blk{ box-shadow:none !important; }

/* white cell-ring on the flood origin is too hot against dark cells */
body.dark .cell.origin::after{
  box-shadow:inset 0 0 0 3px rgba(255,255,255,.75), inset 0 0 0 4.5px rgba(0,0,0,.3);
}

/* Locked badges dim themselves with opacity:.5, which works on white but
   composites to 1.06:1 against the dark ground -- the card disappears and
   the grid reads as floating text. In dark, keep the card solid and mute
   the contents instead. */
body.dark .badge{ opacity:1; background:var(--bg-soft); }
body.dark .badge.has{ background:var(--aqua-wash); }
body.dark .badge:not(.has) .badge-name{ color:var(--ink-2); }
body.dark .badge:not(.has) .badge-dot{ background:var(--line); color:var(--muted); }

/* ---------- sheet drag-to-dismiss ---------- */
/* the grab bar is the drag handle; give it a real touch target */
.sheet-grab{
  cursor:grab; touch-action:none;
  position:relative;
}
.sheet-grab::after{           /* invisible 44px target around the 4px bar */
  content:''; position:absolute; left:50%; top:50%;
  transform:translate(-50%,-50%);
  width:120px; height:44px;
}
.sheet.dragging{ animation:none; transition:none; cursor:grabbing; }
.sheet.settling{ animation:none; transition:transform .26s var(--ease); }

/* ---------- pause ---------- */
.pause-btn{
  flex:0 0 auto; width:30px; height:30px; padding:0;
  border:0; border-radius:50%; cursor:pointer;
  background:var(--aqua-wash); color:var(--accent-ink);
  font-size:10px; letter-spacing:1px; line-height:1;
  display:grid; place-items:center;
  transition:transform .18s var(--ease-back);
}
.pause-btn:active{ transform:scale(.9); }

/* the scrim blur alone still leaves a 6x6 grid readable — hide it properly,
   otherwise pausing is just free thinking time. */
body.paused .board,
body.paused .tray,
body.paused .controls,
body.paused .stage-hint{ filter:blur(20px); pointer-events:none; }

/* ---------- avatar picker ---------- */
.emoji-grid{
  display:grid; grid-template-columns:repeat(8,1fr); gap:6px;
  margin-top:12px;
}
.emoji-grid button{
  aspect-ratio:1; border:1.5px solid transparent; background:var(--bg-soft);
  border-radius:12px; cursor:pointer; font-size:19px; line-height:1;
  padding:0; display:grid; place-items:center;
  transition:transform .16s var(--ease-back), border-color .16s, background .16s;
}
.emoji-grid button:active{ transform:scale(.9); }
.emoji-grid button.on{ border-color:var(--aqua); background:var(--aqua-wash); }
.emoji-own{
  display:flex; align-items:center; gap:12px; margin-top:12px;
}
.emoji-own span{ font-size:12.5px; font-weight:600; color:var(--ink-2); flex:1 1 auto; }
.emoji-own input{
  width:64px; flex:0 0 auto; text-align:center; font-size:20px;
  padding:8px 6px; border-radius:12px; background:var(--bg-soft);
  border:1.5px solid transparent; outline:none; color:var(--ink);
}
.emoji-own input:focus{ border-color:var(--aqua); background:var(--surface-2); }

/* The sheet grew past the viewport once the mark picker was added, pushing
   the grab bar and profile row off-screen. Scroll it, and pin the grab bar
   so drag-to-dismiss stays reachable at any scroll position. */
.sheet{
  max-height:88vh;
  overflow-y:auto;
  overscroll-behavior:contain;
}
.sheet .sheet-grab{
  position:sticky; top:0; z-index:3;
  background:var(--surface);
  margin:-10px 0 14px; padding:14px 0 10px;
  background-clip:padding-box;
  width:100%; border-radius:0;
}
.sheet .sheet-grab::before{      /* the visible 38x4 bar */
  content:''; display:block; width:38px; height:4px; border-radius:2px;
  background:var(--line); margin:0 auto;
}

.practice-sub{ margin-top:20px; }
.practice-note{ font-size:12px; color:var(--muted); margin:10px 0 0; text-align:center; }
.level-chip.sunday{ border-color:var(--aqua-soft); }
.level-chip.sunday b{ color:var(--accent-ink); }

/* ---------- hints ---------- */
.btn-hint{
  background:var(--aqua-wash); color:var(--accent-ink);
  border:1.5px solid var(--aqua-soft);
}
.btn-hint[disabled]{ opacity:.5; }
.pop-in{ animation:cardIn .34s var(--ease-back) both; }

/* a hinted cell pulses rather than flashing once, so it survives a glance */
.cell.hintPulse::after{ animation:hintPulse 1.5s var(--ease) 2; }
@keyframes hintPulse{
  0%,100%{ box-shadow:0 0 0 0 var(--aqua); }
  50%    { box-shadow:0 0 0 3px var(--aqua); }
}
.swatch.hintPulse{ animation:hintPulse 1.5s var(--ease) 2; }

.hint-tag{
  display:inline-block; margin-left:6px; padding:1px 7px; border-radius:999px;
  background:var(--aqua-wash); color:var(--accent-ink);
  font-size:10.5px; font-weight:700; letter-spacing:.02em;
  vertical-align:middle;
}

/* ---------- connect: rewind to a checkpoint ---------- */
.cell.unwind::after{ animation:unwind .26s var(--ease) both; }
@keyframes unwind{
  0%  { transform:scale(1);   opacity:1 }
  100%{ transform:scale(.55); opacity:0 }
}
.node.ping{ animation:nodePing .5s var(--ease-back); }
@keyframes nodePing{
  0%  { transform:scale(1) }
  40% { transform:scale(1.28) }
  100%{ transform:scale(1.04) }
}
.result-hints{
  text-align:center; font-size:12.5px; color:var(--muted);
  margin:-12px 0 20px; padding:0 10px;
}

/* ---------- how to play ---------- */
.how-btn{ display:block; margin:16px auto 0; width:auto; color:var(--muted); }
.how-sheet{ z-index:82; }
.how-game{
  display:flex; gap:13px; align-items:flex-start;
  padding:14px 0; border-bottom:1px solid var(--line);
}
.how-game:last-child{ border-bottom:0; }
.how-n{
  width:26px; height:26px; border-radius:9px; flex:0 0 auto;
  background:var(--aqua-wash); color:var(--accent-ink);
  display:grid; place-items:center; font-size:12px; font-weight:700;
}
.how-body{ flex:1 1 auto; min-width:0; }
.how-title{ font-size:15px; font-weight:650; letter-spacing:-.01em; }
.how-text{ font-size:13.5px; color:var(--ink-2); line-height:1.5; margin:3px 0 0; }
.how-goal{ font-size:12px; color:var(--muted); margin:5px 0 0; }
.how-foot{ margin-top:6px; }
.how-note{ font-size:13px; color:var(--ink-2); line-height:1.5; margin:0 0 20px; }

/* ---------- danger zone ---------- */
.danger-zone{ margin-top:26px; padding-top:18px; border-top:1px solid var(--line); }
.danger-label{ color:var(--danger); margin-top:0; }
.btn-danger{
  background:transparent; color:var(--danger);
  border:1.5px solid var(--danger);
}
.btn-danger:active{ background:var(--bad); }
.delete-confirm{
  margin-top:12px; padding:14px; border-radius:var(--r-md);
  background:var(--bad);
  animation:cardIn .3s var(--ease-back) both;
}
.delete-confirm p{
  margin:0 0 14px; font-size:13.5px; line-height:1.5; color:var(--ink);
}
.delete-confirm .btn{ margin-bottom:8px; }
.delete-confirm .btn:last-child{ margin-bottom:0; }
/* the destructive option is the filled one only at the final step */
.delete-confirm .btn-danger{
  background:var(--danger-fill); color:var(--danger-on); border-color:transparent;
}

/* ---------- legal links ---------- */
.legal-links{
  text-align:center; font-size:12.5px; color:var(--muted);
  margin:18px 0 0; display:flex; gap:8px; justify-content:center; flex-wrap:wrap;
}
.legal-links a{ color:var(--accent-ink); text-decoration:none; font-weight:600; }
.legal-links a:hover{ text-decoration:underline; }
.sheet-fine a{ color:var(--accent-ink); }
