:root {
  /* Base sits above the halation threshold (~#121212): a near-black ground
     makes light text glow for astigmatic eyes and forces every accent to
     either vanish or vibrate. Text is held near 12.5:1 rather than the 16:1
     a near-white on near-black gives — still far past AAA, without the buzz. */
  --bg: #18161c;
  --bg-glow: #232029;
  --panel: #201d26;
  --panel-2: #2a2632;
  --border: #35303d;
  --tile: #2c2834;
  --tile-edge: #393341;
  --text: #d6d8de;
  --muted: #969aa6;
  --accent: #3f9c82;
  --accent-2: #2d7562;
  --on-accent: #04231c;
  --danger: #f0635a;
  /* near miss: wrong, but not the same wrong as naming another object */
  --warn: #e0a94a;
  --input-border: #363040;
}

html[data-theme="light"] {
  --bg: #eef1f6;
  --bg-glow: #ffffff;
  --panel: #ffffff;
  --panel-2: #e6eaf1;
  --border: #d8dee8;
  --tile: #2c2834;          /* covering tiles stay dark so reveals read clearly */
  --tile-edge: #393341;
  --text: #1a1f29;
  --muted: #5c6675;
  --accent: #2d7562;
  --accent-2: #24604f;
  --on-accent: #ffffff;
  --danger: #d6453c;
  --warn: #a9711a;
  --input-border: #c9d1dd;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

body {
  background: radial-gradient(1200px 800px at 50% -200px, var(--bg-glow) 0%, var(--bg) 60%);
  background-color: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  overscroll-behavior: none;
}

#app {
  min-height: 100dvh;
  max-width: 460px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  padding: calc(var(--safe-area-inset-top, env(safe-area-inset-top, 0px)) + 10px) 16px
           calc(var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px)) + 16px);
  /* phones in landscape / cut-outs: never under a side system bar */
  padding-left: max(16px, var(--safe-area-inset-left, env(safe-area-inset-left, 0px)));
  padding-right: max(16px, var(--safe-area-inset-right, env(safe-area-inset-right, 0px)));
}

.screen.hidden { display: none; }

/* Edge-to-edge phones (Android app): a solid strip under the status bar, so
   scrolled content never shows through the clock and icons. Zero-height on
   the web and wherever there is no top inset. */
body::before {
  content: ''; position: fixed; top: 0; left: 0; right: 0; z-index: 9;
  height: var(--safe-area-inset-top, env(safe-area-inset-top, 0px));
  background: var(--bg); pointer-events: none;
}
.icon-btn {
  background: none; border: none; color: var(--muted);
  font-size: 20px; width: 38px; height: 38px; border-radius: 10px;
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
}
.icon-btn:active { background: var(--panel-2); }

/* ================= HOME ================= */
.home-top { display: flex; justify-content: space-between; align-items: center; }
.logo-img { height: 30px; width: auto; display: block; }
/* The wordmark is fixed #F7F7F2 (dark-surface brand spec); flip it for the
   light theme rather than shipping a second asset. */
html[data-theme="light"] .logo-img { filter: invert(1); }

.home-hero { text-align: center; padding: 34px 0 26px; }
.home-tiles {
  display: grid; grid-template-columns: repeat(3, 26px); grid-auto-rows: 26px;
  gap: 4px; justify-content: center; margin-bottom: 22px;
}
.home-tiles i { background: var(--tile); border-radius: 5px; box-shadow: inset 0 0 0 1px var(--tile-edge); }
.home-tiles i.peek { background: linear-gradient(135deg, var(--accent-2), var(--accent)); box-shadow: none; }
.home-hero h1 { font-size: 30px; line-height: 1.15; font-weight: 800; letter-spacing: -0.01em; }
.home-sub { margin-top: 10px; color: var(--muted); font-size: 14px; white-space: pre-line; line-height: 1.45; }

.home-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 8px; }
.btn-primary, .btn-secondary {
  height: 58px; border: none; border-radius: 16px;
  font-size: 17px; font-weight: 800; cursor: pointer; letter-spacing: .01em;
}
.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:disabled { opacity: .55; }
.btn-secondary { background: var(--panel); color: var(--text); border: 1px solid var(--border); font-weight: 700; }
.btn-primary:active, .btn-secondary:active { transform: scale(.97); }
/* Endless as a full-weight twin of the Daily CTA (playtest 2026-09-13): the
   old gray panel read as tertiary. Inverted text/background — the second
   strongest surface there is, in both themes. */
.btn-secondary.btn-play { background: var(--text); color: var(--bg); border: none; }

/* Two-line buttons: label + small status line (Daily countdown, Resume). */
.btn-2line { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; line-height: 1.05; }
.btn-sub { font-size: 12px; font-weight: 600; opacity: .85; }

/* Interrupted-session row: resume button + a quiet discard. */
.resume-row { display: flex; gap: 8px; align-items: stretch; }
.resume-row .btn-secondary { flex: 1; }
.resume-row .icon-btn { width: 46px; flex: none; }

/* Lifetime record, promoted out of Stats (playtest 2026-09-13). */
.streak-chip { text-align: center; color: var(--muted); font-size: 13px; font-weight: 700; margin: -2px 0; }

/* There is no generic .hidden in this stylesheet — every component owns one. */
.resume-row.hidden, .streak-chip.hidden, .btn-sub.hidden { display: none; }

.play-style { display: flex; align-items: center; gap: 10px; margin-top: 2px; }
.play-style-label { color: var(--muted); font-size: 12.5px; white-space: nowrap; }
.play-style .segmented { flex: 1; padding: 0; }
.play-style .segmented button { height: 36px; font-size: 12.5px; }

.home-links { display: flex; justify-content: center; gap: 6px; margin-top: auto; padding-top: 26px; }
.home-links button {
  background: none; border: none; color: var(--muted); font-size: 14px;
  padding: 10px 12px; cursor: pointer; border-radius: 10px;
}
.home-links button:active { color: var(--text); }

/* ================= GENERIC SCREENS ================= */
.screen-top { display: flex; align-items: center; gap: 6px; padding-bottom: 12px; }
.screen-top h2 { font-size: 20px; flex: 1; }
.screen-top-spacer { width: 38px; }

.panel-list { display: flex; flex-direction: column; gap: 12px; }
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 16px; padding: 6px 16px; }
.panel.placeholder-note { padding: 16px; color: var(--muted); font-size: 14px; line-height: 1.5; }
.panel.placeholder-note b { color: var(--text); }
.section-label { font-size: 12px; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); margin: 8px 2px 0; }
.muted-note { color: var(--muted); font-size: 13px; text-align: center; padding: 8px 12px; }

/* ================= CATEGORIES ================= */
.category-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.category-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 16px;
  padding: 14px; cursor: pointer; color: var(--text); text-align: left;
}
.category-card:active { transform: scale(.97); border-color: var(--accent); }
.category-icon { font-size: 24px; }
.category-name { font-weight: 700; font-size: 15px; }
.category-meta { font-size: 11.5px; color: var(--muted); }
/* Each stat ("seen 1/76", "solved 1") is one unbreakable chunk; the narrow
   card may only wrap at the "·" between them, never inside a chunk. */
.category-meta span { white-space: nowrap; }
.category-bar { width: 100%; height: 4px; border-radius: 2px; background: var(--panel-2); margin-top: 6px; overflow: hidden; }
.category-bar i { display: block; height: 100%; background: var(--accent); }

/* ================= STATS ================= */
.stat-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1px; background: var(--border); border-radius: 16px; overflow: hidden; }
.stat-cell { background: var(--panel); padding: 14px 8px; text-align: center; }
.stat-cell b { display: block; font-size: 19px; font-variant-numeric: tabular-nums; }
.stat-cell span { font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }

.cat-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; }
.cat-name { width: 84px; font-size: 13px; font-weight: 600; }
.cat-bar { flex: 1; height: 6px; border-radius: 3px; background: var(--panel-2); overflow: hidden; }
.cat-bar i { display: block; height: 100%; background: var(--accent); }
.cat-val { font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }

/* ================= LEADERBOARD ================= */
.lb-tabs { display: flex; gap: 6px; margin-bottom: 10px; }
.lb-tabs button {
  flex: 1; height: 38px; border-radius: 10px; border: 1px solid var(--border);
  background: none; color: var(--muted); font-size: 13px; font-weight: 600; cursor: pointer;
}
.lb-tabs button.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.lb-caption { font-size: 11.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; padding: 8px 0 2px; }

.lb-row { display: flex; align-items: center; gap: 10px; padding: 10px 6px; border-bottom: 1px solid var(--border); border-radius: 6px; }
.lb-row:last-child { border-bottom: none; }
.lb-row.me { background: rgba(63, 156, 130, .1); border: 1px solid var(--accent); }
.lb-rank { width: 26px; color: var(--muted); font-weight: 700; font-size: 13px; }
.lb-answer { flex: 1; font-weight: 600; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-answer .you-tag { font-style: normal; font-size: 10px; color: var(--accent); border: 1px solid var(--accent); border-radius: 6px; padding: 1px 5px; margin-left: 4px; vertical-align: 1px; }
.lb-date { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.lb-score { font-variant-numeric: tabular-nums; color: var(--accent); }
.lb-report { background: none; border: none; color: var(--muted); font-size: 13px; padding: 4px 6px; margin-left: -4px; cursor: pointer; white-space: nowrap; opacity: .55; }
.lb-report[data-armed="1"], .lb-report[data-armed="sent"] { opacity: 1; color: var(--danger); font-size: 12px; font-weight: 700; }
.lb-divider { height: 1px; background: var(--border); margin: 6px 0; }
.retry-btn { width: 100%; height: 44px; margin-top: 10px; font-size: 14px; }

/* ================= PLAYER NAME ================= */
.player-name { color: var(--accent); font-size: 13px; font-weight: 600; }
.name-edit {
  background: none; border: 1px solid var(--border); color: var(--text);
  border-radius: 10px; padding: 8px 14px; font-size: 13px; cursor: pointer;
}
.name-editor { display: flex; gap: 8px; padding: 4px 0 10px; }
.name-editor.hidden { display: none; }
.name-editor input {
  flex: 1; min-width: 0; height: 42px; border-radius: 10px;
  border: 1px solid var(--input-border); background: var(--bg);
  color: var(--text); font-size: 15px; padding: 0 12px; outline: none;
  -webkit-user-select: text; user-select: text;
}
.name-editor .btn-secondary { height: 42px; padding: 0 14px; font-size: 13px; }
.name-note { font-size: 12px; color: var(--muted); min-height: 16px; padding-bottom: 8px; }
.name-note.error { color: var(--danger); }

/* First-launch welcome card (index.html #welcome-overlay) */
.welcome-card h2 { font-size: 24px; font-weight: 800; margin: 6px 0 8px; }
.welcome-card .welcome-intro { font-size: 14px; color: var(--muted); margin: 0 0 14px; line-height: 1.4; }
.welcome-card .name-editor { padding: 0 0 6px; }
.welcome-card .btn-primary { width: 100%; margin-top: 4px; }
.welcome-card .muted-note { padding: 12px 0 0; }

/* ================= SETTINGS ================= */
.setting-row { display: flex; justify-content: space-between; align-items: center; padding: 13px 0; font-size: 15px; }
.setting-row + .setting-row { border-top: 1px solid var(--border); }
a.setting-row { color: inherit; text-decoration: none; }
#delete-server-note { margin: 0 0 6px; }
#delete-server-note.error { color: var(--danger); }
.deletion-code { font: 600 15px/1.6 ui-monospace, "SFMono-Regular", Consolas, monospace; letter-spacing: .08em; user-select: all; }
.deletion-code-row { border-top: 1px solid var(--border); }
#deletion-code-new.hidden { display: none; }

.credits-panel .setting-row { cursor: pointer; }
.credits-list { max-height: 42vh; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; }
.credits-list.hidden { display: none; }
.credit-item { font-size: 12.5px; color: var(--muted); line-height: 1.45; border-top: 1px solid var(--border); padding-top: 9px; }
.credit-item:first-child { border-top: none; padding-top: 2px; }
.credit-item strong { color: var(--text); font-size: 13px; font-weight: 600; display: block; margin-bottom: 1px; }
.credit-item a { color: var(--accent); text-decoration: none; }
.credit-item a:hover { text-decoration: underline; }
.credit-tag { display: inline-block; font-size: 10.5px; padding: 1px 6px; border-radius: 8px; margin-left: 6px; vertical-align: 1px; background: var(--panel-2); color: var(--muted); }
.credit-tag.by { background: var(--accent); color: var(--on-accent); }

.toggle {
  width: 52px; height: 30px; border-radius: 15px; border: none;
  background: var(--panel-2); position: relative; cursor: pointer; transition: background .15s;
}
.toggle i {
  position: absolute; top: 3px; left: 3px; width: 24px; height: 24px;
  border-radius: 50%; background: var(--muted); transition: transform .15s, background .15s;
}
.toggle.on { background: var(--accent); }
.toggle.on i { transform: translateX(22px); background: var(--on-accent); }

.segmented { display: flex; gap: 6px; padding: 0 0 14px; }
.segmented.wrap { flex-wrap: wrap; }
.segmented.wrap button { flex: 1 1 30%; min-width: 96px; }
.segmented button {
  flex: 1; height: 40px; border-radius: 10px; border: 1px solid var(--border);
  background: none; color: var(--muted); font-size: 13px; font-weight: 600; cursor: pointer;
}
.segmented button.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.segmented button .beta-tag { font-size: 9px; font-weight: 700; letter-spacing: .06em; opacity: .7; vertical-align: super; margin-left: 1px; }

.danger-btn {
  width: 100%; height: 46px; margin: 10px 0; border-radius: 12px;
  border: 1px solid var(--danger); background: none; color: var(--danger);
  font-size: 14px; font-weight: 700; cursor: pointer;
}
.danger-btn:active { background: var(--danger); color: #fff; }

/* ================= GAME ================= */
.game-top { display: flex; align-items: center; gap: 8px; padding-bottom: 10px; }
.game-mode { flex: 1; font-size: 13px; font-weight: 700; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.game-hud { display: flex; gap: 10px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.game-hud b { color: var(--text); }
/* Current puzzle's category, top-right. Casual mercy: shown on calm/classic
   endless rounds; hardcore and the ranked daily keep it hidden (it is a
   solving hint) and category sessions already carry it in the mode label. */
.cat-chip {
  flex-shrink: 0; font-size: 11px; font-weight: 600; color: var(--muted);
  background: var(--panel-2); border: 1px solid var(--border);
  border-radius: 999px; padding: 3px 9px; white-space: nowrap;
}
/* The chip owns its .hidden, like every component in this stylesheet — and
   until now it did not, so the class startRound toggles landed on an element
   nothing hid. Hardcore and the ranked daily with its intro card announced the
   category they exist to withhold, and a category session showed a stale label
   in the very spot the origin flag takes over: startRound writes the category
   name before it knows whether this record has an origin, and only clears it
   on the branch that draws the flag. */
.cat-chip.hidden { display: none; }
/* Origin flag inside the chip (wariant A): a bundled image per country, not
   emoji — Windows has no flag glyphs and degrades them to letter codes that
   read as no flag at all. Sized to the chip's 11px text, one text-space gap
   from the label and between flags comes from the renderer. */
.origin-flag {
  width: 17px; height: 12.5px; object-fit: cover; border-radius: 2px;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .18);
  vertical-align: -2px;
}

.stage { display: flex; justify-content: center; }
.board-wrap { width: min(100%, 52dvh); display: flex; flex-direction: column; gap: 10px; }

.board {
  position: relative; width: 100%; aspect-ratio: 1 / 1;
  border-radius: 16px; overflow: hidden; background: #000;
  box-shadow: 0 10px 40px rgba(0, 0, 0, .55), 0 0 0 1px rgba(255, 255, 255, .04);
}
.board.correct { animation: pop .38s ease; box-shadow: 0 0 0 3px var(--accent), 0 10px 40px rgba(0, 0, 0, .55); }
@keyframes pop {
  0% { transform: scale(1); }
  35% { transform: scale(1.035); }
  100% { transform: scale(1); }
}

#puzzle-image { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.tiles { position: absolute; inset: 0; display: grid; }
.tile {
  background: var(--tile);
  box-shadow: inset 0 0 0 1px var(--tile-edge);
  /* Binary reveal: a covering tile is never translucent and never shrinks —
     no fade frames, no scale-down gaps for the photo to shine through. */
  transition: none;
}
.tile.revealed { opacity: 0; pointer-events: none; }

/* Announced bonus round: pill over the covered board, never in hit-testing. */
.bonus-badge {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
  pointer-events: none; white-space: nowrap;
  background: var(--accent); color: var(--on-accent);
  font-size: 12px; font-weight: 800; letter-spacing: .04em;
  padding: 6px 12px; border-radius: 999px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .35);
}
.bonus-badge.hidden { display: none; }

.progress { height: 4px; border-radius: 2px; background: var(--panel-2); overflow: hidden; }
.progress-fill { height: 100%; width: 0%; background: linear-gradient(90deg, var(--accent-2), var(--accent)); transition: width .12s linear; }

.answer { display: flex; gap: 8px; margin-top: 12px; }
#answer-input {
  flex: 1; min-width: 0; height: 52px; border-radius: 14px;
  border: 1px solid var(--input-border); background: var(--panel);
  color: var(--text); font-size: 18px; padding: 0 16px; outline: none;
  -webkit-user-select: text; user-select: text;
}
#answer-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(63, 156, 130, .15); }
#answer-input.shake { animation: shake .3s ease; border-color: var(--danger); }
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-7px); }
  50% { transform: translateX(6px); }
  75% { transform: translateX(-4px); }
}

.btn-go {
  height: 52px; padding: 0 20px; border: none; border-radius: 14px;
  background: var(--accent); color: var(--on-accent);
  font-size: 16px; font-weight: 700; cursor: pointer;
}
.btn-go:active,
.btn-go.pressed { transform: scale(.96); }

/* ---- soft keyboard ------------------------------------------------------
   --vvh is the usable height with the keyboard open (js/keyboard.js); the
   100dvh fallback covers the first paint and any browser without
   visualViewport. Pinned only on the game screen — the list screens must keep
   their own scrolling. */
#app:has(section[data-screen="game"]:not(.hidden)) {
  height: var(--vvh, 100dvh);
  min-height: 0;
  overflow: hidden;
}
#app:has(section[data-screen="game"]:not(.hidden)) .board-wrap {
  width: min(100%, calc(var(--vvh, 100dvh) * 0.52));
}

/* While typing, the board takes everything the answer row does not need
   rather than a fixed slice of the viewport. 156px = top bar (48) + answer
   row (46) + padding (18) + the feedback line (22) + gaps, measured on
   device; it holds from ~260px of usable height upwards. The top bar stays
   visible on purpose: the category chip must survive the keyboard — same
   spot as with it down — and the feedback line stays because game.js
   re-focuses the field after a wrong guess, so the keyboard is still up
   exactly when the message fires. Only the skip row folds away: Give up /
   End game earn nothing while typing. */
#app.kb-typing:has(section[data-screen="game"]:not(.hidden)) .board-wrap {
  width: min(100%, calc(var(--vvh, 100dvh) - 156px));
}
#app.kb-typing .skip-row { display: none; }
#app.kb-typing {
  padding-bottom: 8px;
}
#app.kb-typing #answer-input,
#app.kb-typing .btn-go { height: 46px; }

.feedback { min-height: 22px; text-align: center; font-size: 14px; color: var(--muted); }
.feedback.ok { color: var(--accent); font-weight: 600; }
/* A miss reads as a miss without being read: caps + weight carry it at a
   glance, which is what the line has to do while the tiles keep falling and
   the player is looking at the board, not at the text. text-transform is
   locale-aware and i18n.js sets <html lang>, so Turkish dotless i survives
   (i18n.js:205) — never uppercase these strings in JS. Size is untouched:
   the row is 22px and the board layout is measured against it. */
.feedback.wrong {
  color: var(--danger);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
}
/* "so close" is still a miss — same caps, same weight — but amber, because
   the player named a piece of the right thing and the colour should not
   accuse them of misreading the picture. */
.feedback.wrong.close { color: var(--warn); }

/* Level of the round in play. The multiplier runs x0.75..x1.5, so a player who
   can see "Expert" can tell a hard round from a bad guess. Sits beside the
   category chip and borrows its shape; only the tint changes with the level. */
.diff-chip {
  flex-shrink: 0; font-size: 11px; font-weight: 600;
  border-radius: 999px; padding: 3px 9px; white-space: nowrap;
  color: var(--muted); background: var(--panel-2); border: 1px solid var(--border);
}
.diff-chip[data-level="3"] { color: #c98a2e; border-color: #c98a2e55; }
.diff-chip[data-level="4"] { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 35%, transparent); }
/* The other half of the .hidden audit that found .cat-chip: the markup ships
   this chip hidden and startRound only ever takes the class off, so nothing
   renders wrong today — the game screen is itself hidden until the first round
   has already filled the chip in. The rule exists so the class means what it
   says, and so the empty pill cannot surface the day something puts it back. */
.diff-chip.hidden { display: none; }

.skip-row { text-align: center; }
.skip { background: none; border: none; color: var(--muted); font-size: 13px; text-decoration: underline; cursor: pointer; padding: 6px 10px; }

/* ================= RESULT OVERLAY ================= */
.overlay {
  position: fixed; inset: 0; z-index: 10;
  /* phones: keep sheets clear of the status and navigation bars */
  padding: max(16px, var(--safe-area-inset-top, env(safe-area-inset-top, 0px)))
           var(--safe-area-inset-right, env(safe-area-inset-right, 0px))
           max(16px, var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px)))
           var(--safe-area-inset-left, env(safe-area-inset-left, 0px));
  display: flex; align-items: center; justify-content: center;
  background: rgba(8, 10, 14, .72); backdrop-filter: blur(6px);
}
html[data-theme="light"] .overlay { background: rgba(240, 243, 248, .78); }
.overlay.hidden { display: none; }

.card {
  width: min(92vw, 380px); background: var(--panel);
  border: 1px solid var(--border); border-radius: 22px;
  padding: 26px 22px 20px; text-align: center;
  box-shadow: 0 24px 80px rgba(0, 0, 0, .6);
  animation: rise .22s cubic-bezier(.2, .9, .3, 1.2);
  /* A tall card scrolls inside itself instead of pushing its own confirm
     button below the fold — the calendar card is the one that found this. */
  max-height: calc(100dvh - 32px); overflow-y: auto; overscroll-behavior: contain;
}
@keyframes rise {
  from { transform: translateY(24px) scale(.96); opacity: 0; }
  to { transform: none; opacity: 1; }
}

.result-title { font-size: 15px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.result-thumb {
  width: 108px; height: 108px; object-fit: cover; border-radius: 16px;
  display: block; margin: 12px auto 0;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .4), 0 0 0 1px rgba(255, 255, 255, .07);
}
.result-thumb.hidden { display: none; }
.result-answer { font-size: 34px; font-weight: 800; margin: 6px 0 2px; text-transform: capitalize; }
/* The flag stands next to the answer itself — "Udon 🇯🇵" — so it is sized to
   the word, not to the 11px chip it comes from: 25px tall is about the cap
   height of this 34px line, which is what makes the pair read as one thing
   rather than as an icon dropped beside it. A long answer plus two flags wraps
   on its own; the line-height keeps a wrapped row from colliding. */
.result-answer .origin-flag { width: 34px; height: 25px; vertical-align: -4px; }
/* The whole group moves together or not at all. An <img> offers a wrap
   opportunity on both sides of itself no matter what space sits next to it —
   a non-breaking space between the flags was tried and measured still
   splitting yerba mate's four countries 3 + 1 — so the binding has to be
   nowrap on the wrapper, not the character between them. */
.result-answer .origin-flags { white-space: nowrap; }
.result-answer:has(.origin-flag) { line-height: 1.25; }
.result-meta { font-size: 12px; color: var(--muted); }
.result-score { font-size: 56px; font-weight: 800; color: var(--accent); margin: 10px 0 2px; font-variant-numeric: tabular-nums; }
.result-score.zero { color: var(--muted); }
.result-stats { font-size: 13px; color: var(--muted); white-space: pre-line; }
.result-score-block { margin: 0; }
.result-session { font-size: 12px; color: var(--muted); margin: 6px 0 16px; padding-top: 6px; border-top: 1px solid var(--border); white-space: pre-line; }
/* "A new puzzle lands tomorrow" — the line that closes the Daily. Sits under
   the session line and pulls up into its bottom margin so the card keeps its
   spacing whether or not the note is shown. */
.result-note { font-size: 12.5px; font-weight: 600; color: var(--accent); margin: -10px 0 14px; }
/* There is no global .hidden here — every element brings its own rule. */
.result-note.hidden { display: none; }

/* ================= REPORT SHEET ================= */
/* ⚑ link in the result card (below the session line) and the sheet it opens.
   The sheet overlays the result card — it sits later in the DOM on purpose. */
.report-link {
  display: block; margin: -10px 0 14px; padding: 0;
  background: none; border: none; cursor: pointer;
  font-size: 11.5px; color: var(--muted); text-decoration: underline dotted;
}
/* Hidden on cards with no just-played round behind them (the daily
   "already played" card): there would be nothing to report. */
.report-link.hidden { display: none; }
.report-card { text-align: left; }
.report-card .result-title { text-align: center; margin-bottom: 4px; }
.report-puzzle { text-align: center; font-size: 13px; color: var(--text); font-weight: 600; margin-bottom: 6px; }
.report-puzzle.hidden { display: none; }
.report-label { font-size: 12px; color: var(--muted); margin: 10px 0 8px; }
.report-reasons { display: grid; gap: 8px; }
.report-reason {
  text-align: left; padding: 11px 14px; cursor: pointer;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px;
  color: var(--text); font-size: 14px; font-weight: 600;
}
.report-reason[aria-checked="true"] { border-color: var(--accent); }
/* Bez kontekstu zagadki (otwarcie z Ustawień) pomiar trudności nie ma sensu —
   ui/report.js chowa powód razem z linią "Zagadka: …". */
.report-reason.hidden { display: none; }
#report-comment {
  width: 100%; margin-top: 12px; padding: 10px 12px; box-sizing: border-box;
  background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px;
  color: var(--text); font: inherit; font-size: 14px; resize: vertical;
}
#report-comment:focus { outline: none; border-color: var(--accent); }
.report-actions { display: flex; gap: 10px; margin-top: 14px; }
.report-actions .btn-secondary, .report-actions .btn-primary { flex: 1; }
/* Sent state: the form folds away and the thanks line is the whole card. */
.report-status { text-align: center; font-size: 16px; font-weight: 700; padding: 10px 2px 2px; }
.report-status.hidden { display: none; }
.report-card.sent .report-puzzle,
.report-card.sent .report-label,
.report-card.sent .report-reasons,
.report-card.sent .rj-wrap,
.report-card.sent #report-comment,
.report-card.sent .report-actions { display: none; }
.report-card.sent .report-status { padding: 14px 0 6px; }

/* Difficulty slider (.rj-*, makieta docs/mockups/zglos-blad-trudnosc-2026-09-13.html).
   Rozwija się tylko pod powodem "difficulty"; rodzimy input[type=range] daje
   strzałki i czytniki za darmo. Kciuk startuje na poziomie zagadki, a ten
   stopień trzyma kreskowy pierścień "obecny" — zgłoszenie zawsze czyta się
   jako "ta konkretna zagadka jest źle zaszuflankowana". */
.rj-wrap { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .24s cubic-bezier(.2, .9, .3, 1); }
.rj-wrap.open { grid-template-rows: 1fr; }
.rj-inner { overflow: hidden; min-height: 0; }
.rj-box { margin-top: 10px; padding: 0 2px; }
.rj-box .report-label { margin-bottom: 2px; }
/* Padding daje miejsce skrajnym etykietom ("Łatwe"/"Ekspert") i podpisowi
   "obecny": centrowane na środkach stopni, na stopniu 1 i 4 wystają poza
   tor, a .rj-inner wyżej (overflow:hidden dla animacji zwijania) ucinałby je
   na krawędzi karty (zgłoszone przez właściciela po wydaniu 2301). Tor i
   pozycje stopni liczą się z szerokości inputu, więc zwężenie jest spójne
   automatycznie — bez zmian w ui/report.js. */
.rj-scale { position: relative; padding: 0 28px; }
/* podpis "obecny" nad osią, nad stopniem zagadki */
.rj-caps { position: relative; height: 20px; }
.rj-cap {
  position: absolute; top: 4px; transform: translateX(-50%);
  font-size: 9.5px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted); border: 1px dashed var(--muted); border-radius: 7px; padding: 1px 7px;
  white-space: nowrap;
}
.rj-range {
  -webkit-appearance: none; appearance: none;
  display: block; width: 100%; height: 30px; margin: 0;
  background: transparent; cursor: pointer;
  /* wypełnienie toru kończy się na środku kciuka: --p = (wybrany-1)/3 */
  --thumb: 24px; --p: 0;
}
.rj-range:focus { outline: none; }
.rj-range::-webkit-slider-runnable-track {
  height: 6px; border-radius: 999px; border: 1px solid var(--border);
  background: linear-gradient(to right,
    var(--accent) 0,
    var(--accent) calc(12px + (100% - var(--thumb)) * var(--p)),
    var(--panel-2) calc(12px + (100% - var(--thumb)) * var(--p)));
}
.rj-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: var(--thumb); height: var(--thumb); border-radius: 50%;
  background: var(--panel); border: 3px solid var(--accent);
  margin-top: -10px; box-shadow: 0 2px 10px rgba(0, 0, 0, .5);
}
.rj-range::-moz-range-track { height: 6px; border-radius: 999px; border: 1px solid var(--border); background: var(--panel-2); }
.rj-range::-moz-range-progress { height: 6px; border-radius: 999px; background: var(--accent); }
.rj-range::-moz-range-thumb {
  width: calc(var(--thumb) - 6px); height: calc(var(--thumb) - 6px); border-radius: 50%;
  background: var(--panel); border: 3px solid var(--accent);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .5);
}
/* stopnie pod osią; pozycje (środki kciuków) ustawia ui/report.js inline */
.rj-stops { position: relative; height: 34px; margin-top: 2px; }
.rj-stop {
  position: absolute; top: 0; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}
.rj-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--panel-2); border: 1px solid var(--border);
  transition: background .15s, border-color .15s;
}
.rj-stop.on .rj-dot { background: var(--accent); border-color: var(--accent); }
.rj-stop.cur .rj-dot {
  background: var(--panel); border: 1px dashed var(--muted);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--muted) 22%, transparent);
}
.rj-lab { font-size: 10.5px; font-weight: 600; color: var(--muted); white-space: nowrap; }
/* wybrany poziom podświetla nazwę tintem diff-chip (1–2 akcent, 3 bursztyn, 4 czerwień) */
.rj-stop.sel .rj-lab { color: var(--accent); font-weight: 800; }
.rj-stop.sel[data-level="3"] .rj-lab { color: #c98a2e; }
.rj-stop.sel[data-level="4"] .rj-lab { color: var(--danger); }
.rj-summary { margin-top: 8px; font-size: 12.5px; color: var(--muted); }
.rj-hint { margin-top: 6px; font-size: 12px; font-weight: 600; color: var(--warn); display: none; }
.rj-hint.show { display: block; }

/* ================= HOW TO PLAY + BETA CARDS =================
   The pre-game how-to card gates every No-limit/Daily start until dismissed
   for good; the beta card greets a freshly picked beta language once per
   language; the extended help opens from Settings. The wordmark replaces a
   text "REVEAL" overline and inverts for the light theme like .logo-img. */
.ht-logo { height: 20px; width: auto; display: block; margin: 0 auto 10px; }
html[data-theme="light"] .ht-logo, html[data-theme="light"] .bt-logo { filter: invert(1); }
.howto-card h2, .beta-card h2, .ht-extended h2 { font-size: 24px; font-weight: 800; margin: 4px 0 0; }
/* Miniature of the covered board with one corner already cleared. */
.ht-board {
  width: 108px; height: 108px; margin: 16px auto 4px; border-radius: 16px;
  background: linear-gradient(135deg, #2d7562 0%, #3f9c82 45%, #1d7f9e 100%);
  overflow: hidden; position: relative;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .4), 0 0 0 1px rgba(255, 255, 255, .07);
}
.ht-board .g { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(6, 1fr); grid-template-rows: repeat(6, 1fr); }
.ht-board .g i { background: var(--tile); box-shadow: inset 0 0 0 1px var(--tile-edge); }
.ht-board .g i.off { opacity: 0; }
.ht-list { display: flex; flex-direction: column; gap: 13px; margin: 16px 0 6px; text-align: left; }
.ht-item { display: flex; gap: 11px; font-size: 14px; line-height: 1.5; }
.ht-item .ico { font-size: 17px; flex: none; line-height: 1.3; }
/* The dismiss-for-good checkbox: a div with role=checkbox, one tap toggles it
   and the start/ok button reads the state at confirm time. */
.ht-check {
  display: flex; gap: 10px; align-items: center; text-align: left;
  margin: 14px 2px; cursor: pointer; font-size: 13px; font-weight: 600; color: var(--muted);
}
.ht-check .box {
  width: 21px; height: 21px; border-radius: 6px; flex: none;
  border: 1.5px solid var(--input-border); background: var(--panel-2);
  display: grid; place-items: center; font-size: 13px; font-weight: 800; color: var(--on-accent);
}
.ht-check.checked .box { background: var(--accent); border-color: var(--accent); }
.ht-check.checked .box::after { content: '✓'; }
.ht-check.checked { color: var(--text); }
.howto-card .btn-primary, .beta-card .btn-primary { width: 100%; }
/* Beta card: badge beside the language name, thanks line carrying the inline
   wordmark ({logo} in beta.thanks, built by ui/howto.js). */
.beta-big {
  display: inline-block; font-size: 11px; font-weight: 800; letter-spacing: .08em;
  color: var(--warn); border: 1px solid var(--warn); border-radius: 8px;
  padding: 2px 8px; vertical-align: 4px; margin-left: 8px; white-space: nowrap;
}
.beta-body { font-size: 14px; line-height: 1.55; margin-top: 14px; text-align: left; }
.beta-thanks { font-size: 13.5px; font-weight: 700; color: var(--accent); margin-top: 12px; }
.bt-logo { height: 14px; width: auto; vertical-align: -2px; margin: 0 2px; }
/* Extended help: same card, longer text — the sections scroll, the Close
   button stays put. */
.ht-extended { display: flex; flex-direction: column; max-height: min(76dvh, 640px); }
.ht-scroll { overflow-y: auto; text-align: left; margin-top: 10px; }
.ht-sec { padding: 11px 0; border-top: 1px solid var(--border); }
.ht-sec:first-child { border-top: none; padding-top: 2px; }
.ht-sec h4 { font-size: 11px; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); margin-bottom: 5px; }
.ht-sec p { font-size: 13.5px; line-height: 1.55; }
.ht-extended .btn-secondary { width: 100%; margin-top: 14px; }



.btn-next {
  width: 100%; height: 56px; border: none; border-radius: 16px;
  background: var(--accent); color: var(--on-accent);
  font-size: 17px; font-weight: 800; cursor: pointer;
}
.btn-next:active { transform: scale(.97); }
.btn-end-session {
  width: 100%; height: 44px; margin-top: 8px; border-radius: 14px;
  background: none; border: 1px solid var(--border); color: var(--muted);
  font-size: 14px; font-weight: 700; cursor: pointer;
}
.btn-end-session:active { transform: scale(.98); }
.btn-end-session.hidden { display: none; }

/* ================= REDUCED MOTION ================= */
html.reduced-motion *,
html.reduced-motion .tile {
  animation: none !important;
}
html.reduced-motion .tile { transition-duration: 0ms !important; }
html.reduced-motion .progress-fill { transition: none; }

@media (prefers-reduced-motion: reduce) {
  .tile { transition-duration: 40ms; }
  .board.correct, #answer-input.shake, .card { animation-duration: .01s; }
  .rj-wrap { transition: none; }
}

/* ---- share (Phase B): ranked daily result card ---- */
.result-best {
  font-size: 12.5px; font-weight: 800; color: var(--accent);
  margin: 2px 0 0; letter-spacing: .04em;
}
.result-best.hidden { display: none; }
.btn-share {
  width: 100%; height: 44px; margin: 2px 0 10px; border-radius: 14px;
  background: none; border: 1.5px solid var(--accent); color: var(--accent);
  font-size: 14.5px; font-weight: 700; cursor: pointer;
}
.btn-share:active { transform: scale(.98); }
.btn-share:disabled { opacity: .6; }
.btn-share.hidden { display: none; }

/* ---- Daily intro card ----------------------------------------------------
   Stands in front of the ranked Daily with the category, the level and the
   times that put a solver in the top slice. Reuses .ht-check from the how-to
   card so the "don't show again" control looks and behaves identically. */
.di-card { text-align: center; }
.di-kicker {
  font-size: 11px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 10px;
}
.di-category {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px;
  font-size: 30px; font-weight: 800; line-height: 1.15; margin: 2px 0 6px;
}
.di-category .origin-flags { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.di-category .origin-flag { width: 34px; height: 25px; vertical-align: 0; }
.di-level { font-size: 13px; font-weight: 700; color: var(--muted); }
/* The last two elements the .hidden audit found without a rule. gateDailyIntro
   toggles the class on both whenever a day arrives without a category or
   without a difficulty, and nothing acted on it: the category div keeps its
   6px bottom margin while empty, so the kicker and the level drifted apart on
   exactly the days with least to say. */
.di-category.hidden, .di-level.hidden { display: none; }
.di-pace {
  display: flex; flex-direction: column; gap: 7px;
  margin: 18px 0 8px; padding: 14px 12px;
  border-radius: 14px; background: var(--panel-2);
}
/* Both halves of the pace block own their .hidden — gateDailyIntro toggles the
   class on them when a day has no pace data, and until now nothing acted on
   it. The block is a padded panel with its own background, so an empty one did
   not merely take up room: it drew a blank slab in the middle of the card. The
   note was worse — gateDailyIntro clears els.pace but never rewrites
   paceNote.textContent, so the caption from the last day that DID have data
   stayed put and captioned nothing ("na podstawie ostatnich 7 dni" under an
   empty box). Same defect as .cat-chip and .diff-chip: the class was there,
   the rule was not. */
.di-pace.hidden, .di-pace-note.hidden { display: none; }
.di-pace-row { display: flex; align-items: baseline; justify-content: center; gap: 8px; font-size: 14px; }
.di-pace-row b { font-size: 19px; font-weight: 800; font-variant-numeric: tabular-nums; min-width: 74px; text-align: right; }
.di-pace-row span { color: var(--muted); font-weight: 600; min-width: 92px; text-align: left; }
.di-pace-note { font-size: 11px; color: var(--muted); }
.di-card .btn-primary { width: 100%; }

/* ---- Daily Calendar block on the result card ----------------------------
   Two lines and nothing else: what today is, in bold, then why it is worth
   knowing. Everything that used to sit here — the shouted date caption, the
   folded explanation, the teaser for tomorrow — came off on owner feedback
   that the card still carried too much text.

   The head compacts only while this block is present, so every other card
   keeps its original proportions. */
.card.has-cal .result-thumb { width: 84px; height: 84px; }
.card.has-cal .result-answer { font-size: 26px; line-height: 1.25; margin: 4px 0 2px; }
.card.has-cal .result-score-block { margin: 8px 0 4px; padding: 8px 12px 9px; border-radius: 13px; background: var(--panel-2); }
.card.has-cal .result-score { font-size: 40px; line-height: 1; margin: 0 0 2px; }
.card.has-cal .result-stats { font-size: 12px; line-height: 1.4; }
.card.has-cal .result-session { margin: 8px 0 12px; }

.result-cal { margin: 0 0 4px; text-align: left; }
.rc-occasion { font-size: 14px; font-weight: 700; line-height: 1.35; margin-bottom: 6px; }
.rc-text { font-size: 13.5px; line-height: 1.55; color: var(--muted); }
.result-cal.hidden { display: none; }


/* ================= UPDATE BANNER ================= */
/* Sits above the result overlay (z-index 10) so it is still reachable when a
   round has just ended — that is the calmest moment to take a reload. Bottom
   centre keeps it clear of the HUD chips and the board. Like every component
   in this stylesheet it brings its own .hidden; the toast is markup that
   starts hidden, so forgetting that rule would show it to everyone forever. */
.update-toast {
  /* centred between the side insets, not on the whole screen (a side system bar is not free space) */
  position: fixed; left: calc(50% + (var(--safe-area-inset-left, env(safe-area-inset-left, 0px)) - var(--safe-area-inset-right, env(safe-area-inset-right, 0px))) / 2);
  bottom: calc(14px + var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px))); transform: translateX(-50%);
  z-index: 20; display: flex; align-items: center; gap: 8px;
  max-width: calc(100vw - 24px - var(--safe-area-inset-left, env(safe-area-inset-left, 0px)) - var(--safe-area-inset-right, env(safe-area-inset-right, 0px))); box-sizing: border-box;
  padding: 7px 8px 7px 14px; border-radius: 999px;
  background: var(--panel-2); border: 1px solid var(--border);
  color: var(--text); font-size: 13px; font-weight: 600;
  box-shadow: 0 8px 28px rgba(0, 0, 0, .5);
}
.update-toast.hidden { display: none; }
.update-toast-go {
  flex-shrink: 0; border: none; cursor: pointer;
  background: var(--accent); color: var(--on-accent);
  font-size: 13px; font-weight: 700; padding: 5px 13px; border-radius: 999px;
}
.update-toast-x {
  flex-shrink: 0; border: none; background: none; cursor: pointer;
  color: var(--muted); font-size: 17px; line-height: 1; padding: 2px 6px;
}

/* ================= ADS (plan F3): "save the streak" offer + HUD streak ================= */
.streak-save { z-index: 60; }
.streak-save-card { text-align: center; padding-top: 22px; }
.streak-save-flame { font-size: 64px; line-height: 1; animation: streak-flame 1.1s ease-in-out infinite alternate; }
.streak-save-count {
  font-size: 56px; font-weight: 900; line-height: 1.05; margin: 2px 0 8px;
  background: linear-gradient(180deg, #ffd166, #f0635a); -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: streak-pop .6s cubic-bezier(.2, 1.6, .4, 1) both;
}
.streak-save-card h2 { margin: 0 0 6px; }
.streak-save-body { color: var(--muted); margin: 0 0 16px; }
.streak-save-watch { width: 100%; margin-bottom: 8px; font-size: 16px; }
.streak-save-decline { width: 100%; }
.streak-save-timer { height: 4px; border-radius: 2px; background: var(--border); margin-top: 14px; overflow: hidden; }
.streak-save-timer i { display: block; height: 100%; background: var(--accent); transform-origin: left; animation: streak-timer linear forwards; }
.streak-save.still .streak-save-flame, .streak-save.still .streak-save-count { animation: none; }
@keyframes streak-flame { from { transform: scale(1) rotate(-4deg); } to { transform: scale(1.12) rotate(4deg); } }
@keyframes streak-pop { from { transform: scale(.4); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes streak-timer { from { transform: scaleX(1); } to { transform: scaleX(0); } }

.hud-stat.streak-warm b { color: #ffb347; }
.hud-stat.streak-fire { border-radius: 10px; box-shadow: 0 0 0 1px rgba(240, 99, 90, .45), 0 0 14px rgba(255, 140, 60, .45); }
.hud-stat.streak-fire b { color: #ff7a45; }
.hud-stat.streak-milestone { animation: streak-milestone .7s ease-out; }
.hud-stat.streak-saved { animation: streak-milestone .9s ease-out 2; }
@keyframes streak-milestone { 0% { transform: scale(1); } 35% { transform: scale(1.35); } 100% { transform: scale(1); } }
html.reduced-motion .hud-stat.streak-milestone, html.reduced-motion .hud-stat.streak-saved { animation: none; }
.streak-save.timer-stopped .streak-save-timer { visibility: hidden; }
#ad-privacy.hidden { display: none; }
