/* ================================================================
   REVERSI COLLECTION
   Premium Board Rendering · 8 Themes · Browser Compat
   Custom Grid System · Proper Depth & Lighting
================================================================ */

/* ══════════════════════════════════════════════════════════════
   CSS CUSTOM GRID SYSTEM
   Usage: .rs-grid, .rs-col-*, .rs-gap-*, .rs-align-*
══════════════════════════════════════════════════════════════ */
.rs-grid          { display: grid; width: 100%; }
.rs-grid-2        { grid-template-columns: repeat(2, 1fr); }
.rs-grid-3        { grid-template-columns: repeat(3, 1fr); }
.rs-grid-4        { grid-template-columns: repeat(4, 1fr); }
.rs-grid-auto     { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
.rs-col-1         { grid-column: span 1; }
.rs-col-2         { grid-column: span 2; }
.rs-col-3         { grid-column: span 3; }
.rs-col-4         { grid-column: span 4; }
.rs-gap-xs        { gap: 4px; }
.rs-gap-sm        { gap: 6px; }
.rs-gap-md        { gap: 8px; }
.rs-gap-lg        { gap: 14px; }
.rs-align-center  { align-items: center; }
.rs-align-start   { align-items: start; }
.rs-justify-center{ justify-items: center; }

/* ══════════════════════════════════════════════════════════════
   BASE THEME TOKENS  (Obsidian default)
══════════════════════════════════════════════════════════════ */
:root {
  --bg:             #0d0f0e;
  --surface:        #141916;
  --panel:          #111410;
  --brass:          #c9a84c;
  --brass-light:    #e8c870;
  --brass-dark:     #8a6a20;
  --text-primary:   #e2ddd0;
  --text-muted:     #7a8070;
  --border:         rgba(201,168,76,.2);
  --shadow-deep:    0 20px 60px rgba(0,0,0,.8);
  --radius:         4px;
  --font-ui:        Arial, "Helvetica Neue", Helvetica, sans-serif;

  /* Board premium vars */
  --board-felt1:    #1a2e20;
  --board-felt2:    #142018;
  --board-felt3:    #0d1810;
  --board-line:     rgba(255,255,255,.06);
  --board-grid-hi:  rgba(255,255,255,.04);
  --board-dot:      rgba(140,200,100,.5);
  --board-frame:    rgba(201,168,76,.3);
  --board-vignette: rgba(0,0,0,.55);
  --hint-fill:      rgba(160,220,140,.18);
  --hint-stroke:    rgba(160,220,140,.55);
  --hover-cell:     rgba(160,220,140,.1);

  /* Piece premium vars */
  --piece-b0: #3e3e50;
  --piece-b1: #1c1c26;
  --piece-b2: #060608;
  --piece-w0: #ffffff;
  --piece-w1: #f2eedd;
  --piece-w2: #ccc8b4;

  --score-active-glow: rgba(201,168,76,.2);
}

/* ── Theme: Crimson Palace ─────────────────────────────────── */
body[data-theme="crimson"] {
  --bg:             #0e0a0a;
  --surface:        #1a1010;
  --panel:          #110808;
  --brass:          #e05a5a;
  --brass-light:    #ff8888;
  --brass-dark:     #8a2020;
  --text-primary:   #f0d8d8;
  --text-muted:     #806060;
  --border:         rgba(220,80,80,.2);

  --board-felt1:    #2a1414;
  --board-felt2:    #1e0e0e;
  --board-felt3:    #140808;
  --board-line:     rgba(255,180,180,.06);
  --board-grid-hi:  rgba(255,100,100,.04);
  --board-dot:      rgba(220,100,100,.5);
  --board-frame:    rgba(220,80,80,.3);
  --board-vignette: rgba(20,0,0,.6);
  --hint-fill:      rgba(240,140,140,.18);
  --hint-stroke:    rgba(240,140,140,.55);
  --hover-cell:     rgba(240,140,140,.1);

  --piece-b0: #2a1010;
  --piece-b1: #180808;
  --piece-b2: #060202;
  --piece-w0: #fff0f0;
  --piece-w1: #f0d0d0;
  --piece-w2: #d8a0a0;
  --score-active-glow: rgba(220,80,80,.2);
}

/* ── Theme: Arctic Frost ───────────────────────────────────── */
body[data-theme="arctic"] {
  --bg:             #080c12;
  --surface:        #0d1520;
  --panel:          #080e18;
  --brass:          #5ab4e8;
  --brass-light:    #88d0ff;
  --brass-dark:     #2a6090;
  --text-primary:   #c8ddf0;
  --text-muted:     #4a6080;
  --border:         rgba(80,160,220,.2);

  --board-felt1:    #0e1e32;
  --board-felt2:    #091626;
  --board-felt3:    #050e18;
  --board-line:     rgba(120,200,255,.07);
  --board-grid-hi:  rgba(80,160,255,.04);
  --board-dot:      rgba(80,180,240,.6);
  --board-frame:    rgba(80,160,220,.3);
  --board-vignette: rgba(0,5,20,.6);
  --hint-fill:      rgba(120,200,255,.18);
  --hint-stroke:    rgba(120,200,255,.55);
  --hover-cell:     rgba(120,200,255,.1);

  --piece-b0: #1a3050;
  --piece-b1: #0e1e38;
  --piece-b2: #04080e;
  --piece-w0: #e8f4ff;
  --piece-w1: #c0d8f0;
  --piece-w2: #90b4d4;
  --score-active-glow: rgba(80,160,220,.2);
}

/* ── Theme: Amber Dusk ─────────────────────────────────────── */
body[data-theme="amber"] {
  --bg:             #0f0d08;
  --surface:        #1a1508;
  --panel:          #100c05;
  --brass:          #e8a830;
  --brass-light:    #ffc840;
  --brass-dark:     #906020;
  --text-primary:   #f0e0c0;
  --text-muted:     #806040;
  --border:         rgba(220,160,40,.2);

  --board-felt1:    #2a1c06;
  --board-felt2:    #1e1404;
  --board-felt3:    #120c02;
  --board-line:     rgba(255,200,80,.06);
  --board-grid-hi:  rgba(220,160,40,.04);
  --board-dot:      rgba(220,160,60,.55);
  --board-frame:    rgba(220,160,40,.3);
  --board-vignette: rgba(20,10,0,.6);
  --hint-fill:      rgba(240,180,80,.18);
  --hint-stroke:    rgba(240,180,80,.55);
  --hover-cell:     rgba(240,180,80,.1);

  --piece-b0: #1e1000;
  --piece-b1: #100800;
  --piece-b2: #060400;
  --piece-w0: #fff8e0;
  --piece-w1: #f0dea0;
  --piece-w2: #d4b860;
  --score-active-glow: rgba(220,160,40,.2);
}

/* ── Theme: Violet Reign ───────────────────────────────────── */
body[data-theme="violet"] {
  --bg:             #0a080f;
  --surface:        #141020;
  --panel:          #0d0a18;
  --brass:          #a878e8;
  --brass-light:    #c8a0ff;
  --brass-dark:     #603890;
  --text-primary:   #d8c8f0;
  --text-muted:     #605878;
  --border:         rgba(160,110,220,.2);

  --board-felt1:    #1a1030;
  --board-felt2:    #120a24;
  --board-felt3:    #0a0618;
  --board-line:     rgba(200,150,255,.07);
  --board-grid-hi:  rgba(160,100,255,.04);
  --board-dot:      rgba(180,120,255,.55);
  --board-frame:    rgba(160,110,220,.3);
  --board-vignette: rgba(10,0,20,.6);
  --hint-fill:      rgba(200,150,255,.18);
  --hint-stroke:    rgba(200,150,255,.55);
  --hover-cell:     rgba(200,150,255,.1);

  --piece-b0: #281040;
  --piece-b1: #180828;
  --piece-b2: #060410;
  --piece-w0: #f0e8ff;
  --piece-w1: #d8c0f0;
  --piece-w2: #b090d4;
  --score-active-glow: rgba(160,110,220,.2);
}

/* ── Theme: Neon Cyberpunk ─────────────────────────────────── */
body[data-theme="neon"] {
  --bg:             #060810;
  --surface:        #0a0d1a;
  --panel:          #060812;
  --brass:          #00ffe0;
  --brass-light:    #80fff0;
  --brass-dark:     #008870;
  --text-primary:   #c0f0e8;
  --text-muted:     #2a6060;
  --border:         rgba(0,255,200,.18);

  --board-felt1:    #080c18;
  --board-felt2:    #050810;
  --board-felt3:    #030408;
  --board-line:     rgba(0,255,200,.12);
  --board-grid-hi:  rgba(0,200,255,.06);
  --board-dot:      rgba(0,255,180,.8);
  --board-frame:    rgba(0,255,200,.4);
  --board-vignette: rgba(0,0,20,.7);
  --hint-fill:      rgba(0,255,200,.15);
  --hint-stroke:    rgba(0,255,200,.7);
  --hover-cell:     rgba(0,255,200,.08);

  --piece-b0: #1a2a3a;
  --piece-b1: #0a1020;
  --piece-b2: #040608;
  --piece-w0: #e0fffa;
  --piece-w1: #00ffe0;
  --piece-w2: #008878;
  --score-active-glow: rgba(0,255,200,.25);
}

/* ── Theme: Sakura ─────────────────────────────────────────── */
body[data-theme="sakura"] {
  --bg:             #1a0e10;
  --surface:        #251418;
  --panel:          #180c10;
  --brass:          #f4a0c0;
  --brass-light:    #ffd0e0;
  --brass-dark:     #a04060;
  --text-primary:   #f8e0e8;
  --text-muted:     #806070;
  --border:         rgba(240,140,170,.2);

  --board-felt1:    #2e1a20;
  --board-felt2:    #201218;
  --board-felt3:    #140a0e;
  --board-line:     rgba(255,180,200,.07);
  --board-grid-hi:  rgba(240,140,170,.04);
  --board-dot:      rgba(240,160,180,.65);
  --board-frame:    rgba(240,140,170,.35);
  --board-vignette: rgba(20,5,10,.65);
  --hint-fill:      rgba(255,180,200,.18);
  --hint-stroke:    rgba(255,180,200,.6);
  --hover-cell:     rgba(255,180,200,.1);

  --piece-b0: #4a1a28;
  --piece-b1: #2e0e18;
  --piece-b2: #0e0408;
  --piece-w0: #fff0f4;
  --piece-w1: #f8d0dc;
  --piece-w2: #e0a0b8;
  --score-active-glow: rgba(240,140,170,.22);
}

/* ── Theme: Gold Marble ────────────────────────────────────── */
body[data-theme="marble"] {
  --bg:             #f5f0e8;
  --surface:        #ede4d4;
  --panel:          #f0e8d8;
  --brass:          #8a6820;
  --brass-light:    #c09840;
  --brass-dark:     #5a4010;
  --text-primary:   #2a1e0a;
  --text-muted:     #8a7850;
  --border:         rgba(138,104,32,.22);

  --board-felt1:    #e8dcc8;
  --board-felt2:    #ddd0b8;
  --board-felt3:    #cfc0a0;
  --board-line:     rgba(90,60,10,.1);
  --board-grid-hi:  rgba(90,60,10,.04);
  --board-dot:      rgba(120,80,20,.5);
  --board-frame:    rgba(120,80,20,.4);
  --board-vignette: rgba(80,50,10,.25);
  --hint-fill:      rgba(180,140,40,.2);
  --hint-stroke:    rgba(150,100,20,.6);
  --hover-cell:     rgba(180,140,40,.12);

  --piece-b0: #2a2030;
  --piece-b1: #1a1020;
  --piece-b2: #080408;
  --piece-w0: #fafaf8;
  --piece-w1: #ede8e0;
  --piece-w2: #c8c0b0;
  --score-active-glow: rgba(138,104,32,.2);
}

/* ══════════════════════════════════════════════════════════════
   RESET + BASE
══════════════════════════════════════════════════════════════ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* All type in this stylesheet is sized in rem, so bumping the root here
   scales every label/button/title uniformly in one place — matches
   draughts-ember-and-slate's html{font-size:144%}. */
html { font-size: 144%; }

html, body {
  width: 100%; height: 100%;
  /* iOS Safari bug (confirmed fixed the same way in draughts-ember-and-
     slate, their round 46): plain `%`/`vh` height resolves against the
     browser's "large" viewport — as if the address-bar chrome docked at
     the bottom were already hidden, even while it's still on screen eating
     into the real visible area. `100dvh` tracks the ACTUAL visible height
     live. Layered as a second declaration after the 100% one (not a
     replacement) so browsers that don't understand `dvh` just ignore this
     line and keep the 100% fallback — no @supports needed. A
     `position:fixed` element establishes its OWN containing block directly
     off the viewport, independent of html/body, so this alone does not
     propagate to #lobby-screen/#compat-screen/#orientation-screen/
     .overlay — each of those gets the same `height:100dvh` added directly
     (see their own rules). */
  height: 100dvh;
  background: var(--bg);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-weight: 600;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background .4s, color .4s;
  /* Blocks pinch-zoom on touch devices — the game must always render at
     100% scale (direct request). Per the CSS Touch Action spec, the
     effective touch-action on a nested element is the INTERSECTION of its
     own value and every ancestor's, so setting this here is enough to stop
     pinch-zoom starting anywhere in the app without needing to repeat it on
     every scrollable popup — listing pan-x/pan-y (not "manipulation",
     which still permits pinch-zoom per spec, only drops the double-tap
     delay) keeps ordinary scrolling (e.g. the coins store grid, Rules
     popup) working everywhere underneath. */
  touch-action: pan-x pan-y;
}
html { -webkit-tap-highlight-color: transparent; }
*, *::before, *::after { -webkit-user-select: none; user-select: none; }
input, textarea { -webkit-user-select: text; user-select: text; }
/* No drag-out of any image in the app (logo, splash background, language
   flag, credits logo) — direct request. The real, cross-browser-reliable
   block is the global 'dragstart' preventDefault in script.js (catches
   every element, not just <img>); -webkit-user-drag here is Safari/
   WebKit-only belt-and-suspenders, and each <img> tag also carries
   draggable="false" directly in the markup so the block still holds even
   if JS hasn't run yet (e.g. the instant it's painted) or fails to load. */
img { -webkit-user-drag: none; }

body::before {
  content: '';
  position: fixed; inset: 0;
  background:
    radial-gradient(ellipse 80% 60% at 50% 0%, rgba(42,74,50,.25) 0%, transparent 70%),
    radial-gradient(ellipse 60% 40% at 80% 100%, rgba(201,168,76,.06) 0%, transparent 60%);
  pointer-events: none;
  transition: background .4s;
}

/* Theme-switch performance fix — same mechanism as draughts-ember-and-slate
   (their round 55): switching body[data-theme] recomputes a couple dozen
   themed CSS custom properties, and a lot of elements that read them
   (lobby mode/diff/theme buttons, icon buttons, primary buttons, toggle
   buttons, close icons, exit-confirm icons) carry `transition: all .2s`
   for ordinary hover/selection feedback — that same transition also
   applies to the simultaneous theme-driven color change, so the browser
   spends a chunk of a second continuously repainting instead of doing one
   instant flip. A fast desktop GPU absorbs that; a weak mobile chip drops
   frames visibly. ThemeSystem.apply() (script.js) toggles this class onto
   <html> for exactly one frame-pair around the data-theme change (forced
   reflow + double rAF — the standard "let the transitionless frame commit
   before transitions come back" pattern) instead of deleting any
   transition declaration outright — hover/selection feedback is untouched
   the rest of the time, only the instant during a theme switch goes
   transition-free. *::before/*::after are included because pseudo-elements
   (e.g. .primary-btn::before, .lobby-play-btn::before) carry their own
   separate transitions that this class must also suppress. */
.theme-switching, .theme-switching *, .theme-switching *::before, .theme-switching *::after {
  transition: none !important;
}

/* ══════════════════════════════════════════════════════════════
   SPLASH SCREEN — first screen shown; 1:1 with draughts-ember-and-slate
   (background fade-in 1.5s, logo fade-in+rise 1.5s, "Tap to Continue"
   prompt appears after 1.5s and pulses, tap fades the whole screen out
   over .4s and hands off to Lobby.show() — see Splash module in
   script.js). .splash-inner is capped to the same 460px column as
   .lobby-inner, not 100vw/vh — the splash background/logo frame the
   game's own content box, not the raw physical screen.
══════════════════════════════════════════════════════════════ */
#splash-screen {
  position: fixed; inset: 0; height: 100dvh;
  background: var(--bg);
  /* draughts-ember-and-slate uses z-index:1000 for the same screen, but
     that project has no equivalent of the coins HUD (addons/coins,
     position:fixed, z-index:8000, rendered into <body> independently of
     the lobby's own .hidden state as soon as coins.js loads) — at 1000 the
     HUD bled through on top of the splash here. Raised above it, still
     below #orientation-screen(9500)/#compat-screen(9999) so either can
     still interrupt the splash. */
  z-index: 8600;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  opacity: 1; transition: opacity .4s;
}
#splash-screen.hidden { opacity: 0; pointer-events: none; }
.splash-inner {
  position: relative; width: 100%; max-width: 460px; height: 100%;
  overflow: hidden;
}
.splash-bg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0; transition: opacity 1.5s ease;
}
.splash-bg.show { opacity: 1; }
.splash-logo {
  position: absolute; left: 50%;
  /* -25% over calc(max(28px, env(safe-area-inset-top) + 16px) + 7%) —
     scaling the whole formula by .75 raises the logo uniformly instead of
     hand-recomputing each of its three components separately. */
  top: calc((max(28px, env(safe-area-inset-top) + 16px) + 7%) * .75);
  width: 96.7385%;
  /* Same crop-via-CSS fix as .lobby-logo (see its comment) — Logo.png is a
     square 1440×1440 canvas with the artwork in a ~39%-tall horizontal
     band, so a plain width+auto-height box would reserve a near-square
     area here too (towering over the splash, likely colliding with the
     tap prompt below it). */
  aspect-ratio: 1381 / 562; object-fit: cover; object-position: center 46.7%;
  transform: translateX(-50%) translateY(10px) scale(.98);
  filter: drop-shadow(0 8px 24px rgba(0,0,0,.6));
  opacity: 0; transition: opacity 1.5s ease, transform 1.5s ease;
}
.splash-logo.show { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
.splash-tap {
  position: absolute; left: 50%; bottom: 8%; width: 80%;
  transform: translateX(-50%);
  text-align: center;
  font-family: var(--font-ui); font-weight: 700; font-size: 1rem;
  letter-spacing: .15em; text-transform: uppercase;
  color: #fff; -webkit-text-stroke: 2px #000; paint-order: stroke fill; /* +1px over 1px */
  text-shadow: 0 2px 14px rgba(0,0,0,.85);
  opacity: 0; transition: opacity .5s ease;
}
.splash-tap.show { opacity: 1; animation: splash-tap-pulse 1.4s ease-in-out infinite; }
@keyframes splash-tap-pulse {
  0%, 100% { opacity: 1; transform: translateX(-50%) scale(1); }
  50%      { opacity: .55; transform: translateX(-50%) scale(1.06); }
}

/* ══════════════════════════════════════════════════════════════
   ORIENTATION LOCK — mobile/touch landscape only, pure CSS show
══════════════════════════════════════════════════════════════ */
#orientation-screen {
  display: none;
  position: fixed; inset: 0;
  height: 100dvh; /* position:fixed has its own containing block — see the html,body comment */
  background: #000;
  z-index: 9500;
  align-items: center; justify-content: center;
}
#orientation-screen img {
  max-width: 90vw; max-height: 90vh;
}
@media (orientation: landscape) and (hover: none) and (pointer: coarse) {
  #orientation-screen { display: flex; }
}

/* ══════════════════════════════════════════════════════════════
   RTL (Arabic) / CJK (Japanese) — text only, never the layout
══════════════════════════════════════════════════════════════ */
[data-lang="ar"] [data-i18n] {
  direction: rtl; unicode-bidi: plaintext; letter-spacing: normal !important;
}
/* ══════════════════════════════════════════════════════════════
   BROWSER COMPATIBILITY SCREEN
══════════════════════════════════════════════════════════════ */
#compat-screen {
  position: fixed; inset: 0;
  height: 100dvh; /* position:fixed has its own containing block — see the html,body comment */
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  flex-direction: column;
  text-align: center;
  padding: 40px 24px;
}

#compat-screen.hidden { display: none; }

.compat-inner {
  max-width: 420px;
  width: 100%;
}

.compat-icon {
  width: 64px; height: 64px;
  margin: 0 auto 20px;
  border: 2px solid var(--brass);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--brass);
  font-size: 1.6rem;
  box-shadow: 0 0 30px rgba(201,168,76,.2);
}

.compat-title {
  font-family: var(--font-ui);
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--brass);
  margin-bottom: 8px;
  text-shadow: 0 0 30px rgba(201,168,76,.4);
}

.compat-subtitle {
  font-size: .85rem;
  font-style: italic;
  color: var(--text-muted);
  letter-spacing: .15em;
  margin-bottom: 24px;
}

.compat-message {
  font-size: .95rem;
  color: var(--text-primary);
  line-height: 1.7;
  margin-bottom: 24px;
}

.compat-supported-label {
  font-family: var(--font-ui); font-size: .55rem; letter-spacing: .12em;
  color: var(--text-muted); text-transform: uppercase; margin-bottom: 10px;
}

.compat-browsers {
  display: flex;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 28px;
}

.compat-browser-chip {
  padding: 8px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--text-muted);
  font-family: var(--font-ui);
  font-size: .6rem;
  letter-spacing: .15em;
  text-transform: uppercase;
}

.compat-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 20px;
}

.compat-divider-line { flex: 1; height: 1px; background: var(--border); }
.compat-divider-gem  { width: 5px; height: 5px; background: var(--brass-dark); transform: rotate(45deg); }

.compat-detail {
  font-size: .78rem;
  color: var(--text-muted);
  line-height: 1.6;
  text-align: left;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 16px;
}

.compat-detail-title {
  font-family: var(--font-ui);
  font-size: .55rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 6px;
}

/* ══════════════════════════════════════════════════════════════
   APP LAYOUT
══════════════════════════════════════════════════════════════ */
#app {
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: stretch;
  justify-content: space-between;
  gap: 0;
  width: 100%;
  max-width: 520px;
  margin: 0 auto;
  /* Same top clearance as .lobby-screen — the coins HUD (addons/coins,
     position:fixed, top-left) and the header's icon row (right-aligned)
     occupy separate horizontal zones, so they share this Y band by
     design instead of the header needing extra clearance under the HUD. */
  padding: max(25px, env(safe-area-inset-top)) .434em .5208em;
  position: relative;
  font-size: clamp(9px, min(4.35vw, 3.3vh), 23.04px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
#app > * { flex-shrink: 0; }

/* ══════════════════════════════════════════════════════════════
   HEADER — icons only (no title, see .lobby-title in the lobby);
   the game name lives in the lobby brand, not repeated in-game.
══════════════════════════════════════════════════════════════ */
#header {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 0 .1736em .26em;
  border-bottom: 1px solid var(--border);
  margin-bottom: .3em;
}

.header-controls { display: flex; gap: 1.6493em; align-items: center; flex-shrink: 0; }

.icon-btn {
  width: 3.9063em; height: 3.9063em;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-muted);
  transition: all .2s;
  flex-shrink: 0;
}
.icon-btn:hover { border-color: var(--brass); color: var(--brass); background: rgba(201,168,76,.06); }
.icon-btn svg { width: 1.7905em; height: 1.7905em; }

/* ══════════════════════════════════════════════════════════════
   SCORE PANEL
══════════════════════════════════════════════════════════════ */
#score-panel {
  width: 100%;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: .3472em;
  margin-bottom: .6076em;
  align-items: center;
}

.score-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: .3472em .5208em;
  display: flex; align-items: center; justify-content: center; gap: .434em;
  transition: border-color .3s, box-shadow .3s;
  position: relative; overflow: hidden;
}

.score-card::before {
  content: ''; position: absolute; inset: 0; opacity: 0; transition: opacity .3s;
}
.score-card.black-card::before { background: linear-gradient(135deg, rgba(255,255,255,.03) 0%, transparent 60%); }
.score-card.white-card::before { background: linear-gradient(135deg, rgba(255,255,255,.04) 0%, transparent 60%); }

.score-card.active {
  border-color: var(--brass);
  box-shadow: 0 0 16px var(--score-active-glow), inset 0 0 20px rgba(201,168,76,.04);
}
.score-card.active::before { opacity: 1; }

.score-disc {
  width: 1.375em; height: 1.375em; border-radius: 50%; flex-shrink: 0;
}
.score-disc.black {
  background: radial-gradient(circle at 35% 30%, var(--piece-b0), var(--piece-b2));
  box-shadow: 0 3px 8px rgba(0,0,0,.8), inset 0 1px 2px rgba(255,255,255,.1);
}
.score-disc.white {
  background: radial-gradient(circle at 35% 30%, var(--piece-w0), var(--piece-w2));
  box-shadow: 0 3px 8px rgba(0,0,0,.6), inset 0 1px 3px rgba(255,255,255,.8);
}

.score-info { flex: 0 1 auto; min-width: 0; text-align: center; }
.score-num {
  font-family: var(--font-ui); font-size: 1.4em; font-weight: 700;
  color: var(--text-primary); line-height: 1.1;
}
.score-sep {
  font-family: var(--font-ui); font-size: .7em;
  color: var(--text-muted); text-align: center;
}

/* ══════════════════════════════════════════════════════════════
   BOARD  — Premium Rendering Container
══════════════════════════════════════════════════════════════ */
#board-wrap {
  position: relative;
  width: 100%; aspect-ratio: 1;
  /* Round 56 bug: a manager-reported, unreproducible-by-QA faint vertical
     scrollbar on the PC game scene — swept every desktop width/height/lang
     combo and found the real, deterministic cause: at window inner height
     <=~700px the sum of #app's content exceeds #app's own height by a few
     px (growing fast as height drops further), independent of width,
     language or zoom. #app's overflow-y:auto (see its own comment) was
     *designed* to backstop exactly this ("short-wide desktop window" case,
     round 1) but the fixed part of the math — 460px here plus
     #status-bar's fixed min-height and #app's fixed top padding, none of
     which shrink with #app's own vh-based local font-size clamp — never
     actually shrinks on a short window, while everything else (header,
     score panel, gaps) does. A corporate 1366x768-class laptop, once you
     subtract the browser's own chrome, lands exactly in this band — a
     completely ordinary machine QA/dev's own (typically taller/maximized)
     windows never happened to hit. Capping width by vh too makes the board
     itself absorb the shortfall instead of staying rigid: unchanged at
     window heights this project already tests at (>=~754px, where
     61vh>=460px already), shrinking gracefully below that. */
  max-width: min(460px, 61vh);
}

/* Corner ornaments */
.board-corner {
  position: absolute; width: 18px; height: 18px;
  border-color: var(--brass-dark); border-style: solid;
  opacity: .7; z-index: 2;
}
.board-corner.tl { top: -2px; left: -2px;   border-width: 2px 0 0 2px; }
.board-corner.tr { top: -2px; right: -2px;   border-width: 2px 2px 0 0; }
.board-corner.bl { bottom: -2px; left: -2px; border-width: 0 0 2px 2px; }
.board-corner.br { bottom: -2px; right: -2px;border-width: 0 2px 2px 0; }

/* Premium board outer glow */
#board-wrap::before {
  content: '';
  position: absolute; inset: -4px;
  border-radius: 4px;
  background: transparent;
  box-shadow:
    0 0 40px rgba(0,0,0,.8),
    0 0 0 1px rgba(201,168,76,.08),
    inset 0 0 60px rgba(0,0,0,.4);
  pointer-events: none;
  z-index: 1;
}

#board-canvas {
  display: block; width: 100%; height: 100%;
  cursor: pointer; border-radius: 2px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.04);
}

/* ══════════════════════════════════════════════════════════════
   STATUS BAR
══════════════════════════════════════════════════════════════ */
#status-bar {
  width: 100%;
  display: flex; align-items: center; justify-content: center;
  gap: .3472em;
  margin-top: .6076em;
  /* Fixed px (not em) on purpose — matches draughts-ember-and-slate: the
     floating Restart button is a fixed-size .icon-btn (~52px, since
     <button> doesn't inherit #app's fluid clamp font-size — it resolves
     against the browser's UA-default button font-size instead), so this
     box needs a matching fixed height, not one that drifts with #app's
     own clamp, for align-items:center to actually line up the status
     text's vertical center with the button's vertical center. */
  min-height: 57px;
}
/* PC-only nudge — #status-bar sits in #app's normal flow (margin-top from
   the board above) while #btn-restart is position:absolute off on its
   own, so matching heights alone doesn't guarantee matching centers; after
   Restart's own +5px raise above, measurement showed the status text
   sitting 2.508px above the button's new center on desktop (direct bug
   report — "text looks higher than the button, PC only"). Mobile wasn't
   affected (both share one transform there, see the media block below, so
   they moved together and stayed aligned) — this is scoped to PC only so
   it doesn't fight that. */
@media (min-width: 481px) {
  #status-bar { transform: translateY(2.508px); }
}

#status-text {
  font-family: var(--font-ui);
  font-size: 1.14em; /* +20% over .95em */ font-style: italic;
  color: var(--text-muted); letter-spacing: .05em;
  text-align: center; transition: color .3s;
}
#status-text.highlight { color: var(--brass); }

/* ══════════════════════════════════════════════════════════════
   OVERLAYS — shared base
══════════════════════════════════════════════════════════════ */
.overlay {
  position: fixed; inset: 0;
  height: 100dvh; /* position:fixed has its own containing block — see the html,body comment */
  background: rgba(8,10,9,.88);
  display: flex; align-items: center; justify-content: center;
  /* Above the coins HUD (addons/coins/coins.css, z-index:8000) — that HUD
     is position:fixed and otherwise sits above the whole lobby/game scene
     on purpose, but it must never float on top of an open modal (Settings,
     Pause, Rules, Game Over, ...). Still below orientation-screen (9500)
     and compat-screen (9999), which are meant to sit above everything,
     including these popups. */
  z-index: 8300;
  /* -webkit- prefix required for Safari/iOS to apply this at all (unprefixed
     backdrop-filter alone silently does nothing there pre-Safari 18) — and
     without it, WebKit has also been seen to flash/re-layer the blur right
     as the opacity transition below finishes (reported as the popup
     "flickering once after it opens", intermittent since it depends on
     exactly when WebKit decides to (re)composite the filter). will-change
     hints the browser to keep this pre-composited for the transition's
     whole duration instead of only settling into its final compositing
     strategy once the animation stops. */
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  will-change: backdrop-filter;
  opacity: 0; pointer-events: none;
  transition: opacity .4s;
  overflow-y: auto;
  padding: 16px 0;
}
.overlay.show { opacity: 1; pointer-events: all; }

.dialog {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 40px 44px;
  text-align: center;
  max-width: 432px; width: 92%;
  /* dvh as a second declaration (same pattern as html,body/#lobby-screen/
     etc., see their comments) — plain vh resolves against the mobile
     browser's "large" viewport (as if the address-bar chrome were already
     hidden); right as the opening transition finishes, if that chrome's
     real state gets reconciled around the same moment, max-height
     suddenly recomputes to a different px value and the dialog visibly
     snaps to a new size for one frame — read as "it flickers/re-opens
     once." dvh tracks the actual visible height the whole time instead,
     so there's nothing to snap to once the transition ends. */
  max-height: 90vh;
  max-height: 90dvh;
  overflow-y: auto;
  position: relative;
  box-shadow: var(--shadow-deep);
  transform: translateY(10px);
  transition: transform .4s;
  flex-shrink: 0;
  margin: auto;
}
.overlay.show .dialog { transform: translateY(0); }

/* dialog corner ornaments */
.dialog::before, .dialog::after,
.dialog .d-corner-br, .dialog .d-corner-bl {
  content: ''; position: absolute;
  width: 14px; height: 14px;
  border-color: var(--brass-dark); border-style: solid;
}
.dialog::before  { top:8px; left:8px;   border-width:1px 0 0 1px; }
.dialog::after   { top:8px; right:8px;  border-width:1px 1px 0 0; }
.dialog .d-corner-br { bottom:8px; right:8px; border-width:0 1px 1px 0; }
.dialog .d-corner-bl { bottom:8px; left:8px;  border-width:0 0 1px 1px; }

.dialog-title {
  font-family: var(--font-ui); font-size: 1.3rem; font-weight: 700;
  color: var(--brass); letter-spacing: .08em;
  margin-bottom: 6px;
  text-shadow: 0 0 20px rgba(201,168,76,.5);
  /* Safety net, not a primary fix: some languages render a single-word
     compound title ("Spielanleitung") with no space to wrap at — without
     this it silently overflows past the dialog's edge instead of wrapping
     to a second line, on any popup, in any language, at any size this
     class is ever used at. */
  overflow-wrap: break-word;
}
/* ── Game Over result discs ── */
#overlay-gameover .dialog-title { margin-bottom: 30px; } /* was paired with .dialog-subtitle's own margin, now removed (see below) — compensate here so the title isn't cramped against .result-discs */
.result-discs {
  display: flex; justify-content: center; gap: 87.36px; /* +20% over 72.8px (which itself was +30% over the original 56px) */
  margin-bottom: 24px; align-items: center;
}
.result-item {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  position: relative; padding-bottom: 1.3em;
}
.result-disc-large { width: 44px; height: 44px; border-radius: 50%; }
.result-disc-large.black {
  background: radial-gradient(circle at 35% 30%, var(--piece-b0), var(--piece-b2));
  box-shadow: 0 6px 20px rgba(0,0,0,.9), inset 0 2px 4px rgba(255,255,255,.12);
}
.result-disc-large.white {
  background: radial-gradient(circle at 35% 30%, var(--piece-w0), var(--piece-w2));
  box-shadow: 0 6px 20px rgba(0,0,0,.5), inset 0 2px 4px rgba(255,255,255,.9);
}
.result-score-num {
  font-family: var(--font-ui); font-size: 1.6rem; font-weight: 700;
  color: var(--text-primary);
}
.winner-badge {
  font-size: .65rem; letter-spacing: .2em;
  color: var(--brass); text-transform: uppercase;
  font-family: var(--font-ui);
  /* Absolutely positioned (anchored to .result-item, which has its own
     position:relative + reserved padding-bottom above) instead of a normal
     flow child: the winner's badge text is much wider than the loser's
     (empty) one, and if it took part in flow sizing it would make the two
     .result-item columns different WIDTHS as well as heights — pushing the
     black/white discs out of horizontal symmetry around center (on top of
     the vertical mismatch this already fixed once, see below). Taking it
     out of flow entirely means both columns' width/height are driven only
     by the disc+score-number, which are the same for both colors, so the
     discs land symmetrically without needing to hand-tune the gap around
     whichever side happens to have the (wider) winner text. */
  position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  white-space: nowrap;
}

/* ── Buttons ── */
.primary-btn {
  width: 100%; padding: 11px;
  background: transparent;
  border: 1px solid var(--brass);
  border-radius: var(--radius);
  color: var(--brass);
  font-family: var(--font-ui); font-size: .75rem;
  letter-spacing: .2em; text-transform: uppercase;
  cursor: pointer; margin-top: 6px;
  transition: all .2s; position: relative; overflow: hidden;
}
.primary-btn::before {
  content: ''; position: absolute; inset: 0;
  background: var(--brass); opacity: 0; transition: opacity .2s;
}
.primary-btn:hover { color: var(--bg); }
.primary-btn:hover::before { opacity: 1; }
.primary-btn span { position: relative; z-index: 1; }

.go-lobby-btn { border-color: var(--border) !important; color: var(--text-muted) !important; }
.go-lobby-btn::before { background: rgba(201,168,76,.08) !important; opacity: 0 !important; }
.go-lobby-btn:hover { color: var(--text-primary) !important; border-color: var(--brass) !important; }
.go-lobby-btn:hover::before { opacity: 1 !important; }

.quit-danger-btn  { border-color: rgba(180,60,60,.4) !important; color: rgba(180,80,80,.7) !important; }
.quit-danger-btn::before { background: rgba(180,60,60,.08) !important; opacity: 0 !important; }
.quit-danger-btn:hover { color: #c05050 !important; border-color: rgba(180,60,60,.7) !important; }
.quit-danger-btn:hover::before { opacity: 1 !important; }

.quit-dialog-title { color: #b85050 !important; text-shadow: 0 0 20px rgba(180,60,60,.4) !important; }

/* ── Pref toggle (Sound/Music/Language) — reused identically in the
   Settings and Pause popups ── */
.pref-toggle-row { display: flex; gap: 12px; justify-content: center; margin: 4px 0 24px; flex-wrap: wrap; }
.pref-toggle-btn {
  flex: 1 1 0; min-width: 101px; max-width: 144px;
  padding: 14px 7px;
  background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text-muted);
  cursor: pointer; transition: all .2s;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
}
.pref-toggle-btn:hover { border-color: var(--brass); color: var(--brass); }
.pref-toggle-btn svg { width: 29px; height: 29px; flex-shrink: 0; }
.pref-toggle-btn .pref-icon-off { display: block; }
.pref-toggle-btn .pref-icon-on  { display: none; }
.pref-toggle-btn.on .pref-icon-off { display: none; }
.pref-toggle-btn.on .pref-icon-on  { display: block; }
.pref-toggle-btn.on { border-color: var(--brass); color: var(--brass); background: rgba(201,168,76,.08); box-shadow: 0 0 10px rgba(201,168,76,.15); }

.lang-toggle-btn img { width: 34px; height: 23px; border-radius: 2px; object-fit: cover; box-shadow: 0 1px 4px rgba(0,0,0,.5); }

.settings-dialog .dialog-title { margin-bottom: 26px; }
/* Was 4px — fine back when the toggle row was the last thing in the
   dialog, but the "How to Play" button now follows it and needs real
   breathing room, not a near-zero gap. */
.settings-dialog .pref-toggle-row { margin-bottom: 22px; }
#settings-rules-btn { margin-top: 16px; }

/* Pause popup: no subtitle/decorative icon above the toggles (the title
   leads straight into them), and the toggles are icon-only there (no
   Sound/Music text label, unlike Settings) — capped narrower so they
   don't stretch into wide empty boxes. */
#overlay-pause .dialog-title { margin-bottom: 20px; }
#overlay-pause .pref-toggle-btn { max-width: 92px; }
/* Resume / How to Play / Exit — every gap between these three widened well
   beyond the shared .primary-btn default (6px), not just the one before
   Exit like before Rules existed here. Uniform now rather than singling
   Exit out again: with three buttons instead of two, an uneven pattern
   would read as arbitrary rather than as "Exit is the outlier on purpose". */
#overlay-pause #pause-rules-btn,
#overlay-pause #pause-exit-btn { margin-top: 16px; }

/* ── Settings / Credits popups ─────────────────────────────── */
.credits-dialog { max-width: 528px; padding: 48px 38px 38px; }
.credits-dialog { padding-top: 55px; }
.credits-logo-frame {
  /* Purely a centering wrapper — no panel background/border/radius, as in
     draughts-ember-and-slate; the logo sits directly on the dialog surface. */
  display: flex; align-items: center; justify-content: center; padding: 34px;
}
/* logo.png is a plain black silhouette on a transparent background — invert
   it to white on the game's dark themes, but leave it black on "marble"
   (the one light-background theme) so it stays visible against a light
   panel. If a future theme adds another light background, add it here too. */
.credits-logo { max-width: 100%; height: auto; display: block; filter: invert(1); }
body[data-theme="marble"] .credits-logo { filter: none; }

/* ── Rules / How to Play popup ─────────────────────────────── */
/* Single column on every screen (desktop included — a 2-column grid was
   tried at one point to make the no-scroll math easier on short desktop
   windows, but the single-column look is the one actually wanted; kept
   the no-scroll guarantee anyway). No scroll, ever, on any screen or in
   any language — instead of a handful of hand-picked breakpoints, which
   turned into whack-a-mole across the 10 languages (French/German/
   Ukrainian run meaningfully longer than English) and odd width×height
   combinations, a single continuously fluid local font-size on
   .rules-list, the same min(vw,vh) clamp pattern already used by
   .lobby-inner/#app elsewhere in this file. Everything below it (icon,
   row title, row text) is sized in em against that one local scale, so it
   keeps shrinking smoothly as EITHER dimension gets tighter, rather than
   jumping between a few fixed sizes and leaving gaps between them. */
.rules-dialog {
  max-width: 560px;
  /* .dialog's base padding (40px 44px, all sides) is a flat px amount —
     fine for short, fixed-size popups, but on a very short viewport this
     alone (80px top+bottom) can be a meaningful chunk of the 90vh budget.
     Scale just top/bottom fluidly with viewport height instead. */
  padding-top: clamp(14px, 5vh, 40px);
  padding-bottom: clamp(10px, 4vh, 40px);
}
.rules-dialog .dialog-title { margin-bottom: clamp(6px, 2vh, 16px); }
.rules-list {
  display: flex; flex-direction: column; text-align: left;
  font-size: clamp(9px, min(3.4vw, 2.6vh), 14px);
  row-gap: .85em;
}
/* PC only — more breathing room between the 4 rule blocks. Scoped above
   the mobile breakpoint rather than raising the shared .85em, which is
   also the minimum gap space-evenly works from in the mobile full-screen
   layout below (a bigger fixed minimum there would fight that layout's
   own distribution instead of just adding requested breathing room). */
@media (min-width: 481px) {
  .rules-list { row-gap: 1.3em; }
}
.rules-row { display: flex; align-items: flex-start; gap: .75em; }
.rules-icon {
  flex-shrink: 0; width: 2.35em; height: 2.35em; border-radius: 50%;
  background: var(--panel); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  color: var(--brass);
}
.rules-icon svg { width: 1.1em; height: 1.1em; }
.rules-icon-star { font-size: 1.05em; line-height: 1; }
.rules-disc { display: inline-block; width: .78em; height: .78em; border-radius: 50%; margin-left: -.2em; }
.rules-disc:first-child { margin-left: 0; }
.rules-disc.black {
  background: radial-gradient(circle at 35% 30%, var(--piece-b0), var(--piece-b2));
  /* At this small size the black piece's own gradient barely reads against
     the (also very dark) .rules-icon badge behind it — unlike the large
     44px discs in the game-over popup, where it sits on the lighter
     --surface dialog background instead. A visible ring keeps it legible
     regardless of theme or badge shade. */
  box-shadow: 0 2px 4px rgba(0,0,0,.6), 0 0 0 1px rgba(201,168,76,.35);
}
.rules-disc.white {
  background: radial-gradient(circle at 35% 30%, var(--piece-w0), var(--piece-w2));
  box-shadow: 0 2px 4px rgba(0,0,0,.4);
}
.rules-copy { flex: 1; min-width: 0; }
.rules-row-title {
  font-family: var(--font-ui); font-size: 1.05em; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--brass);
  margin-bottom: .2em;
}
.rules-row-text {
  font-family: var(--font-ui); font-size: 1.05em; line-height: 1.3;
  color: var(--text-muted);
}
/* ── Exit-confirm dialog: check/cross icon buttons ─────────── */
.exit-confirm-dialog .dialog-title { margin-bottom: 34px; }
.exit-confirm-btn-row {
  display: flex; justify-content: center; align-items: center;
  gap: clamp(26px, 19vw, 101px); flex-wrap: wrap;
}
.exit-confirm-icon-btn {
  width: 70px; height: 70px; border-radius: 50%;
  border: 1px solid var(--border); background: transparent;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: all .2s; flex-shrink: 0;
}
.exit-confirm-icon-btn svg { width: 29px; height: 29px; }
.exit-confirm-no { color: var(--text-muted); }
.exit-confirm-no:hover { border-color: rgba(200,60,60,.6); color: #c05050; background: rgba(180,60,60,.07); }
.exit-confirm-yes { border-color: rgba(100,180,100,.5); color: #90d890; }
.exit-confirm-yes:hover { border-color: #90d890; background: rgba(100,180,100,.1); box-shadow: 0 0 14px rgba(100,180,100,.25); }

/* ══════════════════════════════════════════════════════════════
   LOBBY SCREEN
══════════════════════════════════════════════════════════════ */
#lobby-screen {
  position: fixed; inset: 0;
  height: 100dvh; /* position:fixed has its own containing block — see the html,body comment */
  background: var(--bg);
  display: flex; align-items: stretch; justify-content: center;
  z-index: 200; opacity: 1; transition: opacity .5s;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-top: max(25px, env(safe-area-inset-top));
}
#lobby-screen.hidden { opacity: 0; pointer-events: none; }
/* iOS-only, same fix as draughts-ember-and-slate: halve the safe-area
   reservation specifically on iOS — their notch/Dynamic-Island inset is
   generous enough that the full value here left more empty top clearance
   than the lobby actually needs, on top of already tight mobile budgets. */
html.is-ios #lobby-screen { padding-top: max(25px, calc(env(safe-area-inset-top) * .5)); }

.lobby-inner {
  width: 100%; max-width: 460px;
  font-size: clamp(11px, 2.85vh, 23.04px);
  /* Top padding clears the topbar buttons: .78em offset + 3.74em button
     height (desktop, the larger of the two breakpoints) + a small buffer.
     Bottom padding -~33% over clamp(12px,1.2em,28px) — tightened together
     with .lobby-brand's margin above, to help the lobby fit without a
     scrollbar now that the ornament divider is gone. */
  padding: clamp(50px, 4.85em, 112px) 20px clamp(8px, .8em, 19px);
  display: flex; flex-direction: column; align-items: center;
  justify-content: space-between;
  position: relative; z-index: 1;
  margin: 0 auto;
}
/* iOS-only, same fix as draughts-ember-and-slate: a flat ~12% shrink of
   the whole lobby's local em-scale (-12% over clamp(11px,2.85vh,23.04px))
   — dvh tracks the address-bar chrome live, but only once it's settled;
   right at first paint, before any scroll/resize event, some Safari
   versions haven't necessarily reconciled it yet, so the lobby briefly
   lays out against a taller-than-actually-visible height. A small
   across-the-board margin here absorbs that without waiting on a resize. */
html.is-ios .lobby-inner { font-size: clamp(9.68px, 2.508vh, 20.28px); }
.lobby-inner > * { flex-shrink: 0; }

/* Fullscreen / Settings / Credits topbar — right side; the coins HUD
   (addons/coins, position:fixed) is on the left to match, so the two
   never compete for the same corner on narrow screens where .lobby-inner
   fills nearly the full width. */
.lobby-topbar {
  position: absolute; top: .78em; right: .78em; z-index: 6;
  display: flex; gap: .78em;
}
.lobby-icon-btn {
  width: 3.74em; height: 3.74em; border-radius: 50%;
  background: var(--panel); border: 1px solid var(--border);
  color: var(--text-muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: all .2s; flex-shrink: 0;
}
.lobby-icon-btn svg { width: 1.72em; height: 1.72em; }
.lobby-icon-btn:hover {
  border-color: var(--brass); color: var(--brass); background: rgba(201,168,76,.06);
  transform: translateY(-2px); box-shadow: 0 4px 16px rgba(201,168,76,.2);
}

/* Brand — logo image (script/images/graphics/Logo.png), replacing the old
   text title/subtitle; same width/positioning as draughts-ember-and-slate's
   .lobby-logo (both projects share the same .lobby-inner em-scale, so the
   em value resolves to the same literal size at any given viewport). */
.lobby-brand { text-align: center; margin-bottom: .18em; } /* -~50% over .35em — the decorative line+diamond divider that used to fill this gap (.lobby-ornament) is removed, and the rest of the gap is tightened too, to help the lobby fit without scrolling */
/* Logo.png ships as a square 1440×1440 canvas with the actual artwork
   centered in a much shorter horizontal band (measured: visible pixels
   span roughly y 351-993 of 1440, i.e. ~39% of the canvas height) — a
   plain width+height:auto box takes the image's literal 1:1 intrinsic
   ratio, reserving a near-square box that's mostly invisible transparent
   padding. That's what was blowing up .lobby-brand's height and causing
   the lobby to overflow/scroll. Fixed without touching the shipped asset:
   size the box to the artwork's own aspect ratio and let object-fit:cover
   scale+crop the transparent margins out of view (object-position's Y
   anchors on the artwork's actual vertical center, 46.7% down the source
   image, not the box's geometric 50%). */
.lobby-logo {
  display: block; width: 15.675em; max-width: 100%; margin: 0 auto; /* +10% over 14.25em */
  aspect-ratio: 1381 / 562; object-fit: cover; object-position: center 46.7%;
}

/* Section labels */
.lobby-section { width: 100%; margin-bottom: .61em; }
.lobby-section-label {
  font-family: var(--font-ui); font-size: .55em;
  letter-spacing: .1em; color: var(--text-muted);
  text-transform: uppercase; margin-bottom: .35em; padding-left: 2px;
}

/* Mode grid */
.lobby-mode-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .35em; }
.lobby-mode-btn {
  padding: .61em .43em;
  background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text-muted);
  font-family: var(--font-ui); font-size: .65em;
  letter-spacing: .12em; cursor: pointer; text-transform: uppercase;
  transition: all .2s;
  display: flex; flex-direction: column; align-items: center; gap: .26em;
  position: relative; overflow: hidden;
}
.lobby-mode-btn:hover  { border-color: var(--brass); color: var(--brass); }
.lobby-mode-btn.selected {
  border-color: var(--brass); color: var(--brass);
  background: rgba(201,168,76,.07);
  box-shadow: 0 0 12px rgba(201,168,76,.15);
}
.lobby-mode-icon  { font-size: 1.4em; line-height: 1; }
.lobby-mode-label { position: relative; z-index: 1; }

/* Difficulty */
.lobby-diff-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: .26em; }
.lobby-diff-btn {
  padding: .35em .17em;
  background: var(--panel);
  border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text-muted);
  font-family: var(--font-ui); font-size: .82em;
  letter-spacing: .08em; cursor: pointer; transition: all .2s; text-align: center;
}
.lobby-diff-btn.selected { border-color: rgba(100,180,100,.6); color: #90d890; background: rgba(100,180,100,.07); }
.lobby-diff-btn:hover:not(.selected) { border-color: rgba(100,180,100,.3); color: var(--text-primary); }

/* Bottom spacing lives in padding-bottom, not the shared .lobby-section
   margin-bottom — with box-sizing:border-box, padding is inside the box
   that max-height constrains, so it collapses for free along with the
   content when max-height animates to 0. A previous version instead kept
   margin-bottom and added it to the transition list to smooth out a stutter
   (the section's neighbors were snapping to their new position instantly
   while the section itself was still visually mid-collapse) — that fixed
   the stutter but made this a 3-properties transition, which triggered a
   real, reproducible Chromium timing quirk: on two clicks with ~0ms
   between them (hide, then immediately show again), clientHeight briefly
   read 0 while scrollHeight still read the full content height, i.e. a
   transient false "clipped" state (caught by tests/lobby-sections.spec.js
   — reproduced 2/3 runs with 3 transitioned properties, 0/8 with 2).
   Collapsing via max-height alone avoids the second animated property
   without bringing the stutter back. */
.lobby-section.diff-section { transition: opacity .3s, max-height .3s; max-height: 6em; overflow: hidden; margin-bottom: 0; padding-bottom: .61em; }
.lobby-section.diff-section.hidden { opacity: 0; pointer-events: none; max-height: 0; }

/* ── Theme Grid — 4 cols x 2 rows ───────────────────────────── */
.lobby-theme-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: .40em;
  /* Bleeds past the section's normal edges on each side — the grid
     is already width-bound (the 18em cap almost never wins over 100%
     on mobile, but routinely DOES on PC, where .lobby-inner's wider
     content box makes 18em the smaller of the two), so this is the only
     way to make the square tiles genuinely bigger without touching every
     other lobby section's padding too. Stays inside .lobby-inner's own
     padding box (20px each side), so this never causes horizontal
     overflow/scroll.
     A fixed -12px margin assumed `width` (100%+Npx) was always the
     active constraint, bleeding exactly 12px each side; on PC `max-width`
     (the 18em cap) is what actually wins instead, rendering a narrower
     box than that assumption, so a flat -12px bled the full 12px on the
     left but only ~7px on the right — the grid read as shifted left.
     `margin:auto` does NOT fix this the way it would for a box narrower
     than its container — CSS only lets auto margins ADD space; when the
     box is wider than its container (true here in both regimes, by
     design — this is a deliberate bleed), auto margins resolve to 0 and
     the entire overflow gets dumped on one side (the end side in LTR),
     reproducing the exact same left-shifted look. The actual fix: derive
     the margin directly from the real gap between the box's effective
     width (whichever of width/max-width actually wins) and the
     container's width, so it's correct — and symmetric — in both
     regimes instead of assuming which one wins.
     IMPORTANT: `width`'s "+Npx" and the margin formula's "+Npx" MUST
     always match max-width's own "+Npx" exactly (all three share the
     same constant on purpose) — they drifted out of sync once (one was
     manually bumped 24->30 without updating the other two), which
     silently broke the symmetry this comment describes and reintroduced
     the exact left-shifted look this fix exists to prevent. If that
     constant ever changes again, change it in all three places together. */
  max-width: calc(min(100%, 18em) + 30px);
  width: calc(100% + 30px);
  margin: 0 calc((min(100%, 18em) + 30px - 100%) / -2);
}
/* PC only — the theme-grid gap request is scoped here specifically; the
   4-column mobile grid (see the max-width:480px block) is untouched. */
@media (min-width: 481px) {
  .lobby-theme-grid { gap: .3745em; } /* +7% over .35em */
}

.lobby-theme-btn {
  /* aspect-ratio, not an explicit em height — a <button> does not inherit
     font-size from its ancestors by default (browser UA stylesheet), so
     an em-based height here resolves against the wrong scale and silently
     stops being square on some screens/browsers. aspect-ratio is unit-
     independent and guarantees a true square at any width, matching how
     draughts-ember-and-slate does the same tiles. */
  aspect-ratio: 1;
  border: 2px solid var(--border); border-radius: var(--radius);
  cursor: pointer; position: relative; overflow: hidden;
  transition: all .2s; background: transparent;
}
.lobby-theme-btn:hover { border-color: var(--brass); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,.5); }
.lobby-theme-btn.selected { border-color: var(--brass); box-shadow: 0 0 16px rgba(201,168,76,.3), inset 0 0 20px rgba(201,168,76,.05); }
.lobby-theme-btn.selected::after {
  content: '✦'; position: absolute; top:4px; right:5px;
  font-size: .5em; color: var(--brass);
}

.theme-preview {
  position: absolute; inset: 0;
  display: grid;
  grid-template-columns: repeat(4,1fr);
  grid-template-rows: repeat(4,1fr);
  gap: 1px; padding: .17em;
}
.theme-cell { border-radius: 1px; }

/* ── Lobby Actions ─────────────────────────────────────────── */
.lobby-actions { width: 100%; margin-top: .17em; }

.lobby-play-btn {
  width: 100%; padding: .61em;
  background: transparent;
  border: 1px solid var(--brass); border-radius: var(--radius);
  color: var(--brass);
  font-family: var(--font-ui); font-size: .8em; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  cursor: pointer; position: relative; overflow: hidden; transition: color .2s;
  box-shadow: 0 0 20px rgba(201,168,76,.15);
}
.lobby-play-btn::before {
  content: ''; position: absolute; inset: 0;
  background: var(--brass);
  transform: scaleX(0); transform-origin: left; transition: transform .3s ease;
}
.lobby-play-btn:hover { color: var(--bg); }
.lobby-play-btn:hover::before { transform: scaleX(1); }
.lobby-play-btn span { position: relative; z-index: 1; }

/* ── Dialog close icon button ─────────────────────────────── */
/* Shared by every round popup close (X) — Settings and Credits currently.
   Opaque (var(--panel)), not the old translucent white tint: on Credits
   that tint blended into the logo frame behind it, so the whole class was
   made opaque rather than patching Credits alone, keeping every round
   close button the same size AND the same opacity everywhere it appears. */
.dialog-close-icon {
  position: absolute; top: 12px; right: 12px;
  width: 38px; height: 38px;
  background: var(--panel);
  border: 1px solid var(--border); border-radius: 50%;
  color: var(--text-muted);
  cursor: pointer; transition: all .2s;
  display: flex; align-items: center; justify-content: center;
}
.dialog-close-icon svg { width: 16px; height: 16px; }
.dialog-close-icon:hover {
  background: var(--panel);
  color: var(--text-primary);
  border-color: var(--brass);
}

/* ── Restart: floating corner button ──────
   Moves out of the header into its own floating corner —
   position:absolute takes it out of #header's flex flow entirely, so
   Fullscreen/Hint/Pause/Pass simply re-flow to fill the gap it leaves
   behind, no HTML change needed. Same .icon-btn size/style, just
   repositioned. Unconditional (not scoped to a mobile media query) — it
   floats in its corner on every screen size now, desktop included.

   position:absolute (not fixed), scoped to #app (position:relative,
   max-width:520px) — matches draughts-ember-and-slate exactly. A fixed
   position is anchored to the physical viewport edge, so on a wide
   desktop window it can end up well outside the game's own centered
   column; absolute-inside-#app keeps the button pinned to the game's
   own content box no matter how wide the browser window is. This also
   sidesteps the earlier #lobby-screen.hidden gating this project used to
   need for position:fixed corner buttons (see project history) — an
   absolutely-positioned child is contained by its relatively-positioned
   #app parent's own stacking context, so it can never render above
   #lobby-screen regardless of #app's current display state.
   (Cheat used to float in the mirrored bottom-right corner here too —
   removed along with its button/handler/Game.cheatWin(), see project log.) */
#app #btn-restart {
  position: absolute;
  left: .434em; bottom: .5208em;
  z-index: 5;
  transform: translateY(-5px);
}
/* Fullscreen — same floating-corner treatment as Restart/Cheat above, just
   mirrored to the top-left instead of the bottom (bottom-left is already
   Restart's corner). Same exact `left` as Restart's — "same proportions
   as the rest" means literally reusing that number, not picking a new
   one. Out of #header's flex flow the same way, so Hint/Pause simply
   re-flow to fill the gap left behind.
   `top` is a literal 25px, NOT an em value mirroring Restart's `bottom:
   .5208em` — Restart/Cheat's bottom offset resolves against the BUTTON's
   own UA-default font-size (buttons don't inherit #app's fluid clamp),
   which has nothing to do with where #header's row actually sits; using
   the same .5208em for `top` left Fullscreen floating ~18px higher than
   Hint/Pause (direct bug report — "should be Y-aligned with the rest of
   the header row"). 25px is #header's own top position (#app's
   padding-top), i.e. exactly where Hint/Pause's row starts — since
   Fullscreen is the same .icon-btn height as Hint/Pause on both
   platforms, matching `top` alone is enough to match their vertical
   centers too, confirmed by measurement (both centers land on the same
   Y on 1280x800 and 390x844). */
#app #btn-fullscreen-ingame {
  position: absolute;
  left: .434em; top: 25px;
  z-index: 5;
}

/* ══════════════════════════════════════════════════════════════
   RESPONSIVE
   Lobby (.lobby-inner) and game scene (#app) scale continuously via
   their local clamp()'d font-size (see their rules above) instead of
   stepped breakpoints — do not reintroduce a max-width/max-height
   breakpoint here for sizes already covered by those two clamps.
══════════════════════════════════════════════════════════════ */
@media (max-width: 480px) {
  /* Mirrors draughts-ember-and-slate's mobile brand clearance for the
     image logo — shrinks (even goes slightly negative) on short mobile
     viewports instead of a fixed gap, same formula as theirs. */
  .lobby-brand { margin-bottom: clamp(-10px, calc(min(0px, 808px - 100vh) * .6), 0px); }
  .lobby-theme-grid { grid-template-columns: repeat(4,1fr); }
  .lobby-topbar { top: .52em; right: .52em; gap: .6785em; } /* gap +15% over .59em — widened to match the Settings/Credits size bump below */
  .lobby-icon-btn { width: 3.73em; height: 3.73em; } /* +10% over 3.39em */
  .lobby-icon-btn svg { width: 1.76em; height: 1.76em; } /* +10% over 1.6em */
  /* Settings/Credits/Fullscreen, same size — Fullscreen was deliberately
     left out of the prior round's +15% bump, but the developer then asked
     for it to match the other two, so all three now share this size. */
  #btn-settings, #btn-credits, #btn-fullscreen-lobby { width: 4.2895em; height: 4.2895em; }
  #btn-settings svg, #btn-credits svg, #btn-fullscreen-lobby svg { width: 2.024em; height: 2.024em; }
  .credits-dialog { padding: 34px 22px 24px; }
  .pref-toggle-btn { min-width: 80px; padding: 11px 5px; }
  #status-bar { margin-top: .4557em; } /* -25% over .6076em — raises the turn-status text ("Your turn"/"Opponent thinking") closer to the board */
  /* Bottom part of the game scene (Restart, the turn-status text)
     raised 35px as one literal offset — applied to both independently via
     transform so each keeps its own positioning scheme (the corner button
     is position:absolute inside #app, the status text is a normal-flow
     flex child) without needing to touch whatever controls their relative
     spacing to each other. */
  #app #btn-restart, #status-bar { transform: translateY(-40px); }

  /* Begin Match button raised off the bottom edge — percentage translateY
     is relative to the element's OWN box height, so this reads as a
     literal raise/lower without depending on any surrounding layout
     value. Was -40% (manually tuned past the original -15% from an
     earlier round); lowered 5 points back toward the baseline per direct
     request ("opusti nizhe na 5%" — read as +5% added to the translateY
     value itself, i.e. less upward offset, not a relative 5% of 40). */
  .lobby-actions { transform: translateY(-35%); }

  /* Mode→Difficulty and Difficulty→Themes gaps, -25% each, direct
     request — targeted by id so the Themes→Begin-Match gap below (still
     plain .lobby-section, untouched) doesn't shrink along with them. */
  #lobby-mode-section { margin-bottom: .4575em; } /* -25% over the shared .lobby-section margin-bottom, .61em */
  #lobby-diff-section { padding-bottom: .4575em; } /* -25% over its own .61em (Round 9 moved this section's trailing space into padding, not margin) */

  /* Hint/Pause/Fullscreen/Restart, same size — Restart (floating corner
     button, position:absolute, Round 35) was left on the old base
     .icon-btn size through the last two rounds' bumps, on the assumption
     that the floating corner buttons were a visually separate category;
     direct request to match it to the rest regardless. Cheat (the other
     floating corner button) was NOT named in that request and keeps the
     base size — only Restart was asked for here. */
  #btn-hint, #btn-pause, #btn-fullscreen-ingame, #btn-restart { width: 4.5em; height: 4.5em; }
  #btn-hint svg, #btn-pause svg, #btn-fullscreen-ingame svg, #btn-restart svg { width: 2.0591em; height: 2.0591em; }
  /* Cheat is position:absolute (out of flow, Round 35) so this gap only
     ever acted on Fullscreen/Hint/Pause in practice anyway (Restart is
     also absolute, same reason). */
  .header-controls { gap: 1.9em; } /* +15% over 1.6493em */

  /* Rules popup: full-bleed on mobile instead of a smallish floating card
     with a lot of dark backdrop around it — same "full-screen on mobile,
     floating card on desktop" pattern the coins shop already uses
     (.coins-store-panel). #overlay-rules itself loses its centering
     padding so the 100dvh dialog isn't pushed taller than the viewport by
     it. Desktop is untouched (still the single-column floating card from
     the rule above). */
  #overlay-rules { padding: 0; }
  .rules-dialog {
    width: 100vw; height: 100vh; height: 100dvh;
    max-width: 100vw; max-height: 100vh; max-height: 100dvh;
    border-radius: 0;
    display: flex; flex-direction: column;
    /* Was justify-content:center — with a full screen to fill (per this
       round's request, previous round only grew the font/icons, not the
       LAYOUT), centering the title+list as one block just parks unused
       space as two equal dead margins above and below it. flex-start lets
       the title sit at its natural top position; .rules-list below (see
       flex:1 + space-evenly) is what actually claims the leftover room,
       spreading the 4 rules out into their own even slots down the
       screen — using the space on purpose rather than leaving it idle. */
    justify-content: flex-start;
  }
  /* Bigger min/max and steeper vw/vh coefficients than the shared
     (desktop-sized-card) clamp above — there's a whole screen to fill now,
     not a ~560px-wide card, so the same formula would still look small.
     Coefficients found by bisecting the actual max no-scroll font-size at
     several real phone sizes/aspects, then backed off ~12% so longer
     languages (French/German/Ukrainian run notably longer than English)
     still fit — re-verified across all 10 languages afterward. */
  .rules-list {
    font-size: clamp(12px, min(3.9vw, 2.24vh), 22px); /* +12% over clamp(11px, min(3.5vw, 2vh), 20px) */
    /* Grows to fill whatever height is left under the title, then spreads
       its 4 rows evenly across that — a composed, full-screen layout
       instead of a compact cluster with dead space around it. Can never
       overflow: space-evenly only ever ADDS gap when there's slack, it
       degrades to the rows sitting flush (same as before) if the content
       is ever tall enough to fill the space on its own — the no-scroll
       guarantee from the font clamp above is untouched either way. */
    flex: 1; justify-content: space-evenly;
  }
  /* vw-driven rather than one flat size: a single unbroken compound word
     in some languages ("Spielanleitung") needs to shrink on narrower
     phones to keep fitting on one line — overflow-wrap:break-word (on the
     shared .dialog-title rule) is the safety net if it ever doesn't, but
     wrapping mid-word only looks intentional as a last resort, not as the
     normal case. */
  .rules-dialog .dialog-title { font-size: clamp(1.1rem, 8vw, 1.6rem); margin-bottom: clamp(10px, 3.5vh, 26px); flex-shrink: 0; }
  .rules-dialog {
    /* max() with env(safe-area-inset-top) — the same rule this project
       already applies to #lobby-screen/#app (and the shop popup) for
       every full-screen-on-mobile container, extended to this one too. */
    padding-top: max(clamp(24px, 7vh, 56px), env(safe-area-inset-top));
    padding-bottom: clamp(18px, 6vh, 48px);
  }
}

/* ── Hint badge on button ────────────────────────────────── */
.btn-badge {
  position:absolute; top:2px; right:2px;
  min-width:16px; height:16px; padding:0 3px;
  background:#c9a84c; color:#000;
  font-family:var(--font-ui); font-size:.52rem; font-weight:700;
  display:flex; align-items:center; justify-content:center;
  border-radius:0; pointer-events:none; line-height:1;
}
#btn-hint { position:relative; }

/* ── No-Hint popup ───────────────────────────────────────── */
.no-hint-overlay {
  position:fixed; inset:0; z-index:99500;
  background:rgba(3,5,2,.88); -webkit-backdrop-filter:blur(12px); backdrop-filter:blur(12px);
  display:flex; align-items:center; justify-content:center;
}
.no-hint-box {
  width:320px; background:#0b0f09;
  border:1px solid rgba(201,168,76,.35);
  box-shadow:0 24px 60px rgba(0,0,0,.98);
  padding:32px 24px 24px;
  display:flex; flex-direction:column; align-items:center; gap:12px;
  text-align:center;
}
/* Coin balance — moved here from the in-game HUD (which is now hidden
   entirely during a match, see addons/coins/coins.css) so the player still
   has some visibility into their balance at the one in-game moment it's
   actually relevant: deciding whether to buy more hints. Same gold/brass
   diamond-prefixed look as .coins-hud-val/.coins-store-bal elsewhere. */
.no-hint-balance {
  font-family: var(--font-ui); font-size: .85rem; font-weight: 700;
  letter-spacing: .08em; color: #e8c96a; text-align: center;
  margin-bottom: 1.8px; /* +15% over the shared .no-hint-box gap (12px) — only this one pair, not icon→title→sub→btns too */
}
.no-hint-icon  { font-size:2.4rem; line-height:1; }
.no-hint-title {
  font-family:var(--font-ui); font-size:.95rem; font-weight:700;
  letter-spacing:.1em; color:#e8c96a;
}
.no-hint-sub {
  font-family:var(--font-ui); font-size:.9rem;
  font-style:italic; color:rgba(201,168,76,.65); line-height:1.5;
}
.no-hint-btns  { display:flex; flex-direction:column; gap:9.6px; width:100%; margin-top:6px; } /* gap +20% over 8px on desktop, +35% (10.8px) on mobile below */
@media (max-width:480px) {
  /* Placed here, not in the main RESPONSIVE media block near the top of
     the file — that block comes BEFORE this rule in source order, and an
     earlier same-specificity rule loses to this later base rule regardless
     of which media query is active, so the override has to come after it. */
  .no-hint-btns { gap: 10.8px; }
}
.no-hint-btn {
  width:100%; padding:11px;
  font-family:var(--font-ui); font-size:.62rem;
  font-weight:700; letter-spacing:.1em; text-transform:uppercase;
  cursor:pointer; transition:all .2s; border:1px solid transparent;
}
.no-hint-btn--buy {
  background:rgba(201,168,76,.1); border-color:rgba(201,168,76,.45); color:#e8c96a;
}
.no-hint-btn--buy:hover { background:rgba(201,168,76,.2); border-color:#c9a84c; color:#fff; }
.no-hint-btn--close {
  background:transparent; border-color:rgba(201,168,76,.15); color:rgba(201,168,76,.4);
}
.no-hint-btn--close:hover { border-color:rgba(201,168,76,.35); color:rgba(201,168,76,.7); }
