:root {
  --paper: #e8dcbf;
  --ink: #14110d;
  --ink-soft: #14110dcc;
}

@font-face {
  font-family: "Parchment";
  src: url("ParchmentMf-Vqge.ttf") format("truetype");
  font-display: swap;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  min-height: 100dvh;   /* dvh tracks the true standalone-PWA viewport (iOS status-bar quirk) */
  width: 100%;
  overflow: hidden;
  background: #2a2620;
  font-family: "Georgia", "Times New Roman", serif;
  color: var(--ink);
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: none;
}

#stage {
  position: fixed;
  inset: 0;
  min-height: 100dvh;   /* backstop the CSS background against the iOS standalone viewport gap */
  display: flex;
  align-items: center;
  justify-content: center;
  background: #2a2620;
}

/* Shrink-wraps the canvas: the block-level <canvas> gives it the game's exact
   size, so absolutely-positioned children (HUD, screens) anchor to the play
   area instead of the full viewport. */
#frame {
  position: relative;
  line-height: 0;
  z-index: 1;          /* above the desktop ambient art (#ambient, z-index 0) */
}

#game {
  display: block;
  background: var(--paper);
  /* Sized by JS to match the viewport while keeping a portrait play area. */
}

/* ---------- Desktop ambient art (fills the screen around the column) ---------- */
/* Full-viewport background canvas, painted by IT.Ambient. Hidden on mobile so it
   costs nothing; shown only when #stage has the .desktop class (IT.Layout). */
#ambient {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: none;
  z-index: 0;
  pointer-events: none;
}
#stage.desktop #ambient { display: block; }

/* Desktop: a darker backdrop and a lifted, inked "page" so the centered column
   reads as a focused art piece sitting on the larger ink scene. */
#stage.desktop { background: #20201c; }
#stage.desktop #frame {
  border-radius: 2px;
  box-shadow: 0 10px 60px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(20, 17, 13, 0.5);
}

/* Desktop font scoping: the big in-frame text sizes off `vmin`/`vw` of the whole
   VIEWPORT, which is oversized inside the narrow centered column. Re-anchor them to
   --col (the live column width, set by Game.resize) using the same multipliers as
   the mobile vmin values, so they track the column instead of the wide screen.
   Mobile keeps the untouched vmin rules. */
#stage.desktop #score      { font-size: calc(var(--col) * 0.10); }
#stage.desktop #countdown  { font-size: min(calc(var(--col) * 0.40), 12rem); }
#stage.desktop h1          { font-size: min(calc(var(--col) * 0.24), 9.5rem); }
#stage.desktop h2          { font-size: min(calc(var(--col) * 0.08), 2.2rem); }
#stage.desktop .final-score span,
#stage.desktop .best span  { font-size: calc(var(--col) * 0.09); }

/* ---------- HUD ---------- */
#hud {
  position: absolute;
  top: 0; left: 0; right: 0;
  padding: env(safe-area-inset-top, 12px) 18px 0;
  padding-top: max(env(safe-area-inset-top), 14px);
  display: flex;
  align-items: center;
  /* Score sits on the left in flow; the pause button and wetness bar are
     absolutely positioned in the right corner (button on top, bar just under). */
  justify-content: flex-start;
  gap: 14px;
  pointer-events: none;
}

#score {
  font-size: 10vmin;
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  opacity: 0.85;
}

#wetness-wrap {
  /* Right corner, just under the pause button; right edges align (both right:18px). */
  position: absolute;
  right: 18px;
  top: calc(max(env(safe-area-inset-top), 14px) + 48px);
  width: 200px;
  max-width: 40%;
  height: 10px;
  border: 2px solid var(--ink);
  border-radius: 6px;
  overflow: hidden;
}

#wetness-bar {
  height: 100%;
  width: 100%;
  background: var(--ink);
  transform-origin: right center;
  transition: background-color 0.2s linear;
}

/* Pause button: re-enables pointer events (the HUD is click-through) and sits in
   the top-right corner, clear of the score/wetbar row. No z-index so the pause
   overlay (z-index 10) covers it while paused. */
.hud-btn {
  pointer-events: auto;
  position: absolute;
  top: max(env(safe-area-inset-top), 14px);
  right: 18px;
  width: 40px;
  height: 40px;
  padding: 0;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 3px;
  text-indent: 3px;               /* offset the letter-spacing so "II" looks centered */
  color: var(--ink);
  background: rgba(244, 241, 232, 0.6);
  border: 2px solid var(--ink);
  border-radius: 50%;
  line-height: 1;
}

/* ---------- Lore fragments (surface at score milestones) ---------- */
#lore {
  position: absolute;
  top: 40%;
  left: 10%;
  width: 80%;
  text-align: center;
  font-style: italic;
  font-size: 1.1rem;
  line-height: 1.4;
  color: var(--ink);
  opacity: 0;            /* JS-driven fade envelope, like #wetness-bar */
  pointer-events: none;
  z-index: 5;            /* below .screen (z-index 10) so game-over covers it */
}

/* Resume countdown: a big serif number centered over the LIVE frozen field
   (no veil — the player needs to see the game to reorient). The paper scrim
   keeps it legible over the busy backdrop/barriers, mirroring #overlay h1. */
#countdown {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: "Georgia", serif;
  font-size: min(40vmin, 12rem);
  font-weight: 700;
  line-height: 1;
  color: var(--ink);
  text-shadow: 0 2px 12px rgba(244, 241, 232, 0.9), 0 0 3px rgba(244, 241, 232, 0.9);
  pointer-events: none;
  z-index: 11;            /* above .screen (10); the pause panel is hidden by now anyway */
  animation: countdown-pulse 1s ease-out infinite;
}
/* A soft once-per-second beat so the number feels alive as it ticks down. */
@keyframes countdown-pulse {
  0%   { transform: translate(-50%, -50%) scale(1.18); opacity: 0.65; }
  35%  { transform: translate(-50%, -50%) scale(1);    opacity: 1; }
  100% { transform: translate(-50%, -50%) scale(1);    opacity: 1; }
}

/* ---------- Screens (start / game over) ---------- */
.screen {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(232, 220, 191, 0.82);
  backdrop-filter: blur(2px);
  z-index: 10;
}

/* The title screen uses a LIGHTER veil than game-over/pause so the Mandelbrot
   home art (drawn on the canvas beneath) reads through it. Scoped to #overlay so
   the other panels keep the full 0.82 veil for text legibility. */
#overlay {
  background: rgba(232, 220, 191, 0.55);
  backdrop-filter: blur(1px);
}
/* Soft paper scrim behind the title text so it stays legible over the busier
   background. The CTA is already a solid dark pill, so it needs none. */
#overlay h1,
#overlay .tag,
#overlay .how {
  text-shadow: 0 1px 6px rgba(244, 241, 232, 0.9), 0 0 2px rgba(244, 241, 232, 0.9);
}

.panel {
  text-align: center;
  padding: 28px 26px;
  max-width: 360px;
  width: 86%;
}

h1 {
  /* min() caps the title so it can't outgrow the panel: on portrait phones
     vmin tracks the screen width and stays small, but on landscape desktop
     vmin tracks the tall window height — the rem cap keeps it in-bounds. */
  font-family: "Parchment", "Georgia", serif;
  font-size: min(24vmin, 9.5rem);
  letter-spacing: normal;       /* the decorative script face sets its own spacing */
  line-height: 0.95;
  margin-bottom: 10px;
}

/* line-height is explicit because #frame sets line-height:0 (to shrink-wrap the
   canvas); without this the heading box collapses and overflows onto siblings. */
h2 { font-size: min(8vmin, 2.2rem); line-height: 1.1; margin-bottom: 14px; letter-spacing: -0.02em; }

.death-line {
  font-style: italic;
  opacity: 0.6;
  margin-bottom: 18px;
  line-height: 1.4;
  font-size: 1.02rem;
}

.tag {
  font-style: italic;
  opacity: 0.7;
  margin-bottom: 22px;
  line-height: 1.4;
  font-size: 1.85rem;
}

.how {
  list-style: none;
  text-align: left;
  margin: 0 auto 26px;
  display: inline-block;
  line-height: 2;
  font-size: 1.2rem;
  opacity: 0.85;
}
.how b { border-bottom: 2px solid var(--ink); }

/* ---------- First-play tutorial (#howto) ---------- */
/* A full-bleed parchment "page": OPAQUE paper fill (a translucent veil let the
   parked start-button blob bleed through behind the rows) and a panel that
   stretches to fill the whole play area, framed with a 2px ink border (same
   border vocabulary as #wetness-wrap / #settings-panel). */
#howto {
  background: #e8dcbf;
  align-items: stretch;
  justify-content: stretch;
  padding: max(env(safe-area-inset-top), 14px) 14px max(env(safe-area-inset-bottom), 14px);
}
/* Title pinned top, button pinned bottom, the instruction list grows to fill the
   space between (space-between + the list's own flex:1). */
.howto-panel {
  width: 100%;
  height: 100%;
  max-width: none;
  margin: 0;
  box-sizing: border-box;
  padding: 16px 18px;
  border: 2px solid var(--ink);
  border-radius: 10px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  text-align: center;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.howto-panel h2 { margin-bottom: 6px; }
.howto-list {
  flex: 1 1 auto;
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  line-height: 1.28;
  font-size: 0.95rem;
}
/* Each instruction is a centered vertical block: text on top, its sample below. */
.howto-list li {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
  margin: 0;
}
/* Mini-canvases drawn by js/main.js. The drop and fall tiles are sized THERE
   (inline style, from the live play-field width) so the sample ink matches its
   true in-game size on any screen; the widths below are only a pre-layout
   fallback. The pickup tile stays CSS-sized and viewport-aware. */
.howto-art { flex: 0 0 auto; }
.howto-art--drop   { width: 54px; height: min(60px, 14vw); }
.howto-art--pickup { width: min(56px, 13vw); height: min(56px, 13vw); }
.howto-art--fall   { width: 108px; height: min(54px, 12.5vw); }
/* Ink-economy row: the shared #coin-blob drop glyph centered as a small sample,
   bigger than its inline cluster size so it reads at tile scale (it keeps the
   gentle coin-spin; reduce-motion / OS prefs silence it via the .coin-glyph rules). */
.howto-art--ink { width: min(48px, 12vw); height: min(48px, 12vw); display: flex; align-items: center; justify-content: center; }
.howto-coin { width: 40px; height: 40px; }
/* The wetness sample mirrors the in-game #wetness-wrap / #wetness-bar look —
   at the SAME size as the HUD bar, like the drop samples. */
.howto-art[data-art="wet"] {
  width: 200px;
  max-width: 40vw;   /* like #wetness-wrap's 40% of the play field, not of the row */
  height: 10px;
  border: 2px solid var(--ink);
  border-radius: 6px;
  overflow: hidden;
}
/* Narrow phones: the JS-sized sample tiles (true in-game drop size) are taller
   than the old fixed-px ones, so claw back the difference from text/padding to
   keep all seven rows on one screen. */
@media (max-width: 480px) {
  .howto-panel { padding-top: 10px; padding-bottom: 10px; }
  .howto-list  { font-size: 0.88rem; }
  .howto-list li { gap: 8px; }
}
.howto-wet-bar {
  /* display:block is load-bearing: this is a <span>, and inline elements ignore
     width/height/transform (the in-game #wetness-bar is a div, so it never hit
     this). Without it the bar renders empty and the scaleX animation is a no-op. */
  display: block;
  width: 100%;
  height: 100%;
  background: var(--ink);
  transform-origin: right center;
}

/* ---------- Settings (Tier-2 #12) ---------- */
/* A corner gear, pinned top-right of the title, pause AND game-over screens (all
   .settings-gear), opening the full-screen #settings overlay. Shares the round
   paper-on-ink look of the in-game pause button (.hud-btn) so the UI reads as one
   family. (#settings-btn kept as an id alias for the title gear's fade rule.) */
.settings-gear {
  position: absolute;
  top: max(env(safe-area-inset-top), 14px);
  right: 14px;
  width: 40px;
  height: 40px;
  padding: 0;
  color: var(--ink);              /* the gear SVG fills via currentColor */
  background: transparent;        /* just the gear — no enclosing circle */
  border: none;
  overflow: visible;              /* let the roughened gear edge spill past the box, not clip */
  z-index: 13;                    /* above the meta cluster (12) / scrim / art */
}
.settings-gear svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* The #settings overlay reuses the .panel + .screen vocabulary (like #customize).
   A narrower column keeps the toggle rows and seals tidy. */
.settings-panel { max-width: 340px; }
.settings-rows { margin-bottom: 4px; }
.setting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 1.05rem;
  line-height: 1.4;       /* #overlay zeroes line-height; restore it so labels aren't clipped */
  padding: 7px 2px;       /* breathing room between the stacked rows */
  cursor: pointer;
}
#music-toggle, #sfx-toggle, #haptics-toggle, #reducemotion-toggle {
  width: 20px; height: 20px; cursor: pointer; accent-color: var(--ink);
}
#settings-close { display: block; margin: 8px auto 0; }

/* Remove Ads, drawn as a small ink wax-seal so it reads as in-world, not a store
   button. Shares the paper-on-ink family of #settings-btn / .hud-btn. */
.ink-seal {
  display: block;
  width: 100%;
  margin: 14px 0 2px;
  padding: 9px 12px;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--ink);
  background: rgba(244, 241, 232, 0.6);
  border: 2px solid var(--ink);
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px rgba(20, 17, 13, 0.25);  /* faint double-ring, like a struck seal */
  cursor: pointer;
}
.ink-seal.bought {
  cursor: default;
  opacity: 0.7;
  background: transparent;
  box-shadow: none;
}

/* Consent: a NON-blocking bottom banner (not a .screen modal) so the title stays
   playable while the choice is pending. Anchored to the play-area bottom, clear of
   the home-bar via the safe-area inset. z-index above the settings popover (12) so
   re-opening it from Privacy sits on top. */
#consent {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 13;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  background: rgba(232, 220, 191, 0.97);
  border-top: 2px solid var(--ink);
  box-shadow: 0 -6px 18px rgba(20, 17, 13, 0.18);
  text-align: center;
}
#consent.hidden { display: none; }
.consent-text {
  margin: 0 0 10px;
  font-size: 0.92rem;
  line-height: 1.4;
  color: var(--ink);
}
.consent-text a { color: var(--ink); }
.consent-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
}
/* Side-by-side, content-width seals — override .ink-seal's full-width block. */
.consent-actions .ink-seal { display: inline-block; width: auto; margin: 0; }
/* While the banner is open, lift the title CTA clear of it so "Hold to fall"
   stays fully visible and tappable (the banner sits at the very bottom). The
   secondary instruction lines hide for this transient moment so the lifted CTA
   doesn't collide with the centered tagline. */
#stage.consent-open #overlay .start-group {
  bottom: calc(max(env(safe-area-inset-bottom), 9vh) + 96px);
}
#stage.consent-open #overlay .start-group .how,
#stage.consent-open #overlay .start-group .best-line {
  display: none;
}

/* Start screen fills the full height: title pinned near the top, tagline centered
   on the true screen midpoint, instructions + CTA pinned to the bottom. Title and
   CTA are absolutely positioned so the tagline (the only in-flow child) centers on
   the screen regardless of their heights. Scoped to #overlay so the game-over and
   pause panels keep the default centered .panel layout. */
#overlay .panel {
  position: relative;             /* anchor for the pinned title + CTA */
  align-self: stretch;            /* fill height (overrides .screen's align-items:center) */
  width: 100%;
  max-width: min(94vw, 560px);    /* wider than the title/CTA so the tagline reaches toward the edges */
  display: flex;                  /* centers the tagline both axes */
  align-items: center;
  justify-content: center;
  padding: 0 18px;
}
#overlay h1 {
  position: absolute;
  top: max(env(safe-area-inset-top), 22px);
  left: 0; right: 0;
  height: 42%;                  /* frame-relative (not vh) so the title can't overshoot a
                                   letterboxed frame and collide with the tagline */
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
#overlay .tag { margin: 0; }      /* dead-center via the panel's flex centering */
/* Quiet best-score line in the bottom start-group, just above the instructions —
   reliably empty space (the title and centered tagline nearly touch on tall screens). */
#overlay .best-line {
  margin: 0 0 18px;
  text-align: center;
  font-size: 1.5rem;
  line-height: 1.4;         /* #overlay zeroes line-height; restore so the line isn't clipped to 0 height */
  letter-spacing: 0.06em;
  opacity: 0.75;
}
#overlay .best-line.hidden { display: none; }
#overlay .start-group {
  position: absolute;
  bottom: max(env(safe-area-inset-bottom), 9%);   /* frame-relative, matches the title's % sizing */
  left: 0; right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
#overlay .start-group .how { margin: 0 0 20px; }

button {
  font-family: inherit;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--paper);
  background: var(--ink);
  border: none;
  border-radius: 40px;
  padding: 14px 34px;
  cursor: pointer;
  transition: transform 0.08s ease;
}
button:active { transform: scale(0.96); }

/* ---------- Start button: larger CTA + hold-to-fall morph ---------- */
/* The title CTA is bigger than the default button, and morphs into the ink
   drop on hold (see js/main.js). Transitions cover every property the morph
   animates so a released-early press auto-reverses to the pill. */
#start-btn {
  position: relative;
  font-size: 1.5rem;
  padding: 18px 44px;
  transition: width 0.4s ease, height 0.4s ease, padding 0.4s ease,
              border-radius 0.4s ease, color 0.18s ease, transform 0.55s ease;
  will-change: width, height, transform;
}
/* The morph drives its own transform (travel) — don't let the press-scale fight it. */
#start-btn.morphing:active, #start-btn.flying:active { transform: none; }

/* Pill -> the actual ink drop. width/height are set inline from JS to the real
   ink diameter; the label fades out. The drop is NOT a plain circle — it's the
   exact silhouette render.js draws: a subtly wobbly blob body, a faint wet
   streak trailing above, and a glossy highlight. Body/streak are %-based so they
   track the box as it morphs pill -> drop. */
#start-btn.morphing {
  padding: 0;
  background: transparent;
  color: transparent;
  overflow: visible;            /* let the wet streak show above the box */
}
/* Body + glossy highlight. clip-path (--ink-blob) is generated in main.js from
   render.js _blobPath at phase 0 — the drop's exact wobbly outline. The
   highlight is a small light radial sitting inside the blob (paper @ ~0.5). */
#start-btn.morphing::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 35% 33%, rgba(232, 220, 191, 0.55) 0,
                    rgba(232, 220, 191, 0.5) 6%, rgba(232, 220, 191, 0) 9.5%),
    var(--ink);
  clip-path: var(--ink-blob, circle(50%));
}
/* Faint wet streak trailing above the body (echoes render.js's trailing shadow:
   half-width, ~1.4x tall, centered ~2.6 radii above the body). */
#start-btn.morphing::before {
  content: "";
  position: absolute;
  left: 25%;
  top: -150%;
  width: 50%;
  height: 140%;
  border-radius: 50%;
  background: rgba(20, 17, 13, 0.16);
}

/* Flight to the gameplay spawn point — JS sets the translate() inline. */
#start-btn.flying { transition: transform 0.55s ease; }

/* While the ink flies, the rest of the menu fades away. */
#overlay .panel.menu-fading h1,
#overlay .panel.menu-fading .tag,
#overlay .panel.menu-fading .how,
#overlay .panel.menu-fading .best-line,
#overlay .panel.menu-fading #settings-btn,
/* The meta cluster is now a sibling of .panel (a direct child of #overlay), so it
   fades via the general-sibling combinator rather than as a descendant. */
#overlay .panel.menu-fading ~ .meta-cluster {
  opacity: 0;
  transition: opacity 0.45s ease;
  pointer-events: none;
}

/* Secondary/outline button (e.g. "Quit to menu"). */
button.ghost {
  background: transparent;
  color: var(--ink);
  border: 2px solid var(--ink);
}

/* Pause panel stacks its heading + two buttons vertically with clear spacing. */
#pause .panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}
#pause .panel h2 { margin-bottom: 0; }
#pause .panel button { display: block; margin: 0; }

/* Game-over panel stacks its two actions vertically: primary "Fall again" + the
   secondary "Return to menu" ghost button below it. */
#gameover button { display: block; margin: 0 auto; }
/* Continue is the highlighted action when offered — a gentle breath draws the eye,
   and it sits above "Fall again" with a clear gap. */
#gameover #revive-btn {
  margin-bottom: 16px;
  animation: revive-pulse 1.5s ease-in-out infinite;
}
#gameover #revive-btn:active { animation: none; }
@keyframes revive-pulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.045); }
}
/* Little screen-with-a-play-triangle after the label: hints that Continue plays a
   rewarded ad. Sized to the label, currentColor (paper on the ink button). */
#gameover #revive-btn .revive-screen-ico {
  width: 1.15em;
  height: 1.15em;
  margin-left: 0.5em;
  vertical-align: -0.22em;
  overflow: visible;            /* let the roughened edges spill, not clip */
}
#gameover #menu-btn { margin-top: 10px; }

/* Share is an icon-only button pinned to the screen's bottom-left corner (the
   .screen overlays the shrink-wrapped #frame, so this is the play-area corner).
   Same paper-on-ink, no-enclosing-box treatment as #settings-btn. The global
   `#gameover button { margin: 0 auto }` is overridden — absolute + left only. */
#gameover #share-btn {
  position: absolute;
  left: max(env(safe-area-inset-left), 16px);
  bottom: max(env(safe-area-inset-bottom), 16px);
  margin: 0;
  width: 44px;
  height: 44px;
  padding: 0;
  color: var(--ink);              /* the SVG strokes via currentColor */
  background: transparent;
  border: none;
  overflow: visible;              /* let the roughened edges spill, not clip */
}
#gameover #share-btn svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}
/* The checkmark hides until a share succeeds (.shared, set in js/main.js). */
#gameover #share-btn .share-check { display: none; }
#gameover #share-btn.shared .share-glyph { display: none; }
#gameover #share-btn.shared .share-check { display: inline; }

/* Leaderboard icon button mirrors share in the bottom-RIGHT corner (same
   paper-on-ink, no-box treatment). Opens the #leaderboard overlay. */
#gameover #board-btn {
  position: absolute;
  right: max(env(safe-area-inset-right), 16px);
  bottom: max(env(safe-area-inset-bottom), 16px);
  margin: 0;
  width: 44px;
  height: 44px;
  padding: 0;
  color: var(--ink);
  background: transparent;
  border: none;
  overflow: visible;
}
#gameover #board-btn svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* ---- Leaderboard overlay ------------------------------------------------- */
/* The board panel is a touch wider than the others and caps the list height so a
   full roster scrolls inside the panel rather than pushing the buttons off-screen. */
.lb-panel { max-width: 380px; }
.lb-scope {
  margin: -6px 0 14px;
  font-style: italic;
  opacity: 0.6;
  font-size: 0.9rem;
  line-height: 1.3;
}
.lb-name-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 12px;
  font-size: 1rem;
  line-height: 1.4;          /* #frame zeroes line-height; restore so it isn't clipped */
}
.lb-name-row > span { opacity: 0.6; letter-spacing: 0.04em; }
#lb-name {
  flex: 1;
  min-width: 0;
  padding: 7px 10px;
  font: inherit;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-align: center;
  color: var(--ink);
  background: rgba(244, 241, 232, 0.6);
  border: 2px solid var(--ink);
  border-radius: 10px;
}
#lb-name::placeholder { color: var(--ink); opacity: 0.4; font-weight: 400; }

.lb-list {
  list-style: none;
  margin: 0 0 6px;
  padding: 0;
  text-align: left;
  max-height: 46vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.lb-list li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 5px 6px;
  line-height: 1.35;
  border-bottom: 1px solid rgba(20, 17, 13, 0.12);
}
.lb-list li:last-child { border-bottom: none; }
.lb-list .lb-pos {
  flex: 0 0 2.2em;
  text-align: right;
  font-weight: 700;
  opacity: 0.55;
  font-variant-numeric: tabular-nums;
}
.lb-list .lb-who {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lb-list .lb-pts {
  flex: 0 0 auto;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* The player's own row is struck darker so they can spot themselves at a glance. */
.lb-list li.you {
  background: rgba(20, 17, 13, 0.08);
  border-radius: 8px;
  border-bottom-color: transparent;
}
.lb-list li.you .lb-pos,
.lb-list li.you .lb-who { opacity: 1; }
.lb-list li.you .lb-who::after {
  content: " · you";
  font-style: italic;
  opacity: 0.6;
  font-weight: 400;
}

.lb-rank {
  margin: 8px 0 4px;
  font-style: italic;
  opacity: 0.7;
  font-size: 0.95rem;
}
#lb-close { display: block; margin: 14px auto 0; }

/* ---- Coins + Customize / "The Inkwell" (Tier-2 #11) --------------------- */
/* The coin glyph IS the Inkwell drop's silhouette (shared #coin-blob symbol),
   gently spinning so the currency reads as a living drop of ink. Sized to sit on
   the text baseline beside the number. */
.coin-glyph {
  display: inline-block;
  width: 1.05em; height: 1.05em;
  vertical-align: -0.2em;
  margin-right: 0.32em;
  color: var(--ink);
  overflow: visible;
  animation: coin-spin 6s linear infinite;
  transform-origin: 50% 50%;
}
@keyframes coin-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .coin-glyph { animation: none; } }

/* Top-left meta cluster (Customize + running coin total), shared by the title
   and the death screen. On both it's a direct child of the .screen (#overlay /
   #gameover), not the centered .panel, so it anchors to the screen's true
   top-left corner (opposite the gear, like the share/board corners). */
.meta-cluster {
  position: absolute;
  top: max(env(safe-area-inset-top), 14px);
  left: max(env(safe-area-inset-left), 14px);
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: 12;
}
/* Customize entry: a text label in a quiet pill. Sized to stand at least as tall
   as the gear (#settings-btn, 40px) so the label has breathing room top/bottom. */
.meta-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
  margin: 0;
  padding: 6px 18px;
  font: inherit;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--ink);
  background: rgba(244, 241, 232, 0.6);
  border: 2px solid var(--ink);
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px rgba(20, 17, 13, 0.25);  /* faint double-ring, like the seals */
  cursor: pointer;
}
.coin-readout {
  margin: 0;
  display: inline-flex;
  align-items: center;
  font-size: 1.35rem;       /* matches the taller Customize pill; glyph scales via em */
  letter-spacing: 0.04em;
  opacity: 0.85;
  font-variant-numeric: tabular-nums;
}

/* Game-over tally: the per-run "+N earned" celebration, centered under the score. */
.coin-tally {
  margin: 10px 0 0;
  text-align: center;
  font-size: 1.05rem;
  line-height: 1.4;
  letter-spacing: 0.04em;
  opacity: 0.8;
  font-variant-numeric: tabular-nums;
}
.coin-tally.hidden { display: none; }

.cz-panel { max-width: 380px; }
.cz-balance {
  margin: -6px 0 14px;
  font-size: 1.3rem;
  line-height: 1.4;
  letter-spacing: 0.05em;
  opacity: 0.85;
  font-variant-numeric: tabular-nums;
}
.cz-group-title {
  margin: 4px 0 8px;
  font-size: 0.95rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0.55;
  font-weight: 700;
}
.cz-grid {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.cz-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 8px 5px;
  border: 2px solid rgba(20, 17, 13, 0.5);
  border-radius: 12px;
  background: rgba(244, 241, 232, 0.45);
}
/* The equipped tile is struck darker with a double-ring, like a sealed choice. */
.cz-tile.selected {
  border-color: var(--ink);
  background: rgba(20, 17, 13, 0.08);
  box-shadow: inset 0 0 0 2px rgba(20, 17, 13, 0.22);
}
.cz-tile.cz-broke { animation: cz-shake 0.5s; }   /* can't afford -> a small refusal shake */
@keyframes cz-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}
.cz-swatch {
  width: 100%;
  height: 44px;
  border-radius: 8px;
  border: 1px solid rgba(20, 17, 13, 0.25);
  display: block;
}
.cz-name {
  font-size: 0.78rem;
  line-height: 1.25;
  text-align: center;
  letter-spacing: 0.02em;
}
.cz-state { font-size: 0.8rem; line-height: 1.3; min-height: 1.3em; }
.cz-state.cz-equipped { font-style: italic; opacity: 0.65; }
/* Compact equip/buy controls — override .ink-seal's full-width block in tiles. */
.cz-equip, .cz-buy {
  display: inline-block;
  width: auto;
  margin: 0;
  padding: 4px 10px;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--ink);
  background: rgba(244, 241, 232, 0.6);
  border: 2px solid var(--ink);
  border-radius: 999px;
  cursor: pointer;
}
.cz-equip { background: transparent; }
#cz-close { display: block; margin: 6px auto 0; }

/* ---------- Daily reward / streak (Tier-2 #8) ---------- */
/* A modal over the title showing an escalating streakCap-day coin track. */
.daily-panel { max-width: 420px; }
.daily-sub {
  margin: -4px 0 16px;
  font-size: 0.92rem;
  line-height: 1.45;
  opacity: 0.7;
}
.daily-track {
  list-style: none;
  margin: 0 0 14px;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 5px;
}
.daily-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 7px 2px;
  border: 2px solid rgba(20, 17, 13, 0.3);
  border-radius: 10px;
  background: rgba(244, 241, 232, 0.4);
  opacity: 0.5;                  /* future days read as dim / unreached */
}
.daily-cell.is-past { opacity: 1; }
/* The claimable day lifts gently to draw the tap; the sealed day is struck darker. */
.daily-cell.is-today {
  opacity: 1;
  border-color: var(--ink);
  background: rgba(20, 17, 13, 0.06);
  box-shadow: inset 0 0 0 2px rgba(20, 17, 13, 0.22);
  animation: daily-pulse 1.8s ease-in-out infinite;
}
.daily-cell.is-claimed {
  opacity: 1;
  border-color: var(--ink);
  background: rgba(20, 17, 13, 0.1);
}
@keyframes daily-pulse {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce) { .daily-cell.is-today { animation: none; } }
.daily-day {
  font-size: 0.62rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  opacity: 0.7;
}
/* A calm row — override .coin-glyph's spin + em sizing for the static track. */
.daily-coin { width: 18px; height: 18px; margin: 0; vertical-align: 0; animation: none; }
.daily-amt {
  font-size: 0.82rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.daily-streak-line {
  margin: 0 0 14px;
  font-size: 0.95rem;
  line-height: 1.4;
  letter-spacing: 0.03em;
  opacity: 0.85;
}
#daily-claim { display: block; margin: 0 auto; }
#daily-later { display: block; margin: 8px auto 0; }

/* ---------- Achievements / milestones (Tier-2 #10) ---------- */
/* A list screen over the title (mirrors #customize): unlocked rows are sealed
   darker with the coin glyph + "Earned"; locked rows are dim and advertise the
   Ink reward still to claim. */
.ach-panel { max-width: 400px; }
.ach-sub {
  margin: -4px 0 16px;
  font-size: 0.92rem;
  line-height: 1.45;
  opacity: 0.7;
}
.ach-list {
  list-style: none;
  margin: 0 0 14px;
  padding: 0;
  text-align: left;
  max-height: 52vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.ach-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  margin-bottom: 7px;
  border: 2px solid rgba(20, 17, 13, 0.3);
  border-radius: 12px;
  background: rgba(244, 241, 232, 0.4);
}
/* Earned milestones are struck darker with the double-ring seal, like an equipped
   cosmetic; locked ones stay dim until won. */
.ach-row.is-unlocked {
  border-color: var(--ink);
  background: rgba(20, 17, 13, 0.07);
  box-shadow: inset 0 0 0 2px rgba(20, 17, 13, 0.2);
}
.ach-row.is-locked { opacity: 0.55; }
.ach-row-body { flex: 1 1 auto; min-width: 0; }
.ach-row-name {
  margin: 0;
  font-size: 0.98rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.ach-row-desc {
  margin: 2px 0 0;
  font-size: 0.8rem;
  line-height: 1.35;
  opacity: 0.7;
}
.ach-row-tag {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* The tag's coin glyph sits calm beside the text (no spin), like the daily track. */
.ach-row-tag .coin-glyph { width: 16px; height: 16px; margin-right: 0.28em; vertical-align: 0; animation: none; }
.ach-row-tag.ach-earned { font-style: italic; opacity: 0.75; }
#ach-close { display: block; margin: 6px auto 0; }

/* Unlock toast: an ink-on-parchment banner that drops in from above any screen,
   holds, then slides back out. Pinned to #frame's top-center, above every screen
   (.screen z-index 10, .meta-cluster 12). */
.ach-toast {
  position: absolute;
  top: max(env(safe-area-inset-top), 12px);
  left: 50%;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 12px;
  width: max-content;
  max-width: 86%;
  padding: 10px 18px 10px 12px;
  background: #f1ebd9;
  border: 2px solid var(--ink);
  border-radius: 14px;
  box-shadow: inset 0 0 0 1px rgba(20, 17, 13, 0.25), 0 6px 18px rgba(20, 17, 13, 0.22);
  /* Off-screen above + transparent until .show; transition drives the slide. */
  transform: translate(-50%, -160%);
  opacity: 0;
  transition: transform 0.42s cubic-bezier(0.2, 0.9, 0.3, 1.2), opacity 0.42s ease;
  pointer-events: none;
}
.ach-toast.show { transform: translate(-50%, 0); opacity: 1; }
.ach-toast.hidden { display: none; }
.ach-toast-medal {
  flex: 0 0 auto;
  width: 40px; height: 40px;
  color: var(--ink);
  overflow: visible;
}
.ach-toast-medal svg { display: block; width: 100%; height: 100%; overflow: visible; }
.ach-toast-body { display: flex; flex-direction: column; gap: 1px; text-align: left; }
.ach-toast-eyebrow {
  margin: 0;
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.55;
  font-weight: 700;
}
.ach-toast-name {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.2;
}
.ach-toast-reward {
  margin: 1px 0 0;
  font-size: 0.82rem;
  font-weight: 700;
  opacity: 0.8;
  font-variant-numeric: tabular-nums;
}
.ach-toast-reward .coin-glyph { width: 15px; height: 15px; margin-right: 0.25em; vertical-align: -0.15em; animation: none; }
@media (prefers-reduced-motion: reduce) {
  .ach-toast { transition: opacity 0.3s ease; transform: translate(-50%, 0); }
}

/* ---------- Reduce motion (Settings toggle, Tier-2 #12) ---------- */
/* The manual toggle adds #stage.reduce-motion, mirroring the OS
   prefers-reduced-motion rules above: silence the decorative UI animations (coin
   spin, the countdown / revive / daily pulses, the cosmetic shake) and make the
   unlock toast appear without the slide. Gameplay is unaffected. */
#stage.reduce-motion .coin-glyph,
#stage.reduce-motion #countdown,
#stage.reduce-motion #gameover #revive-btn,
#stage.reduce-motion .daily-cell.is-today,
#stage.reduce-motion .cz-tile.cz-broke { animation: none; }
#stage.reduce-motion .ach-toast { transition: opacity 0.25s ease; transform: translate(-50%, 0); }

.hint {
  margin-top: 18px;
  font-size: 0.85rem;
  opacity: 0.5;
}
kbd {
  font-family: monospace;
  border: 1px solid var(--ink-soft);
  border-radius: 4px;
  padding: 1px 6px;
  font-size: 0.8rem;
}

.final {
  display: flex;
  justify-content: center;
  gap: 40px;
  margin-bottom: 26px;
}
.final-score span, .best span {
  display: block;
  font-size: 9vmin;
  font-weight: 700;
  /* Georgia uses old-style figures — 3/4/5/7/9 descend below the baseline. The
     extra ~0.15em of line-height contains those descenders so they can't paint
     down into the label gap (intermittent overlap depending on the digits). */
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.final small, .best small {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  font-size: 0.7rem;
  opacity: 0.55;
  margin-top: 6px;
}
.best span { opacity: 0.55; }

.hidden { display: none !important; }
