/* ==========================================================================
 * moyako-components.css — Sprint 2 Day 2 (12 components)
 * --------------------------------------------------------------------------
 * Spec:  docs/games-moyako-spec-v2.md §Shared components
 * Scope: all rules under [data-moyako-v2]. No effect on live pages.
 * ========================================================================== */

/* ============ TopBar ============ */
[data-moyako-v2] .topbar {
  display: flex; align-items: center; justify-content: space-between;
  /* Baseline height — MOBILE-DESIGN-SYSTEM.md §2: pin to --header-height + safe-area. */
  min-height: calc(var(--header-height, 44px) + env(safe-area-inset-top, 0px));
  box-sizing: border-box;
  /* v237 (2026-05-09): safe-area-inset padding on left/right.
   * `viewport-fit=cover` lets the WebView render edge-to-edge under
   * Android's system gesture zones; in landscape the right-edge swipe
   * area was intercepting taps on the gear button (`right: 12px`),
   * so settings appeared dead. The env() insets are non-zero only
   * when the OS reserves an edge zone (gesture nav, notch); on
   * narrow web they evaluate to 0 and behaviour is unchanged. Per
   * user "settings still not working at landscape mode" on cap-shell
   * emulator. */
  padding:
    env(safe-area-inset-top, 0)
    calc(var(--space-4) + env(safe-area-inset-right, 0))
    0
    calc(var(--space-4) + env(safe-area-inset-left, 0));
  /* Solid slate matching cap-nav (#1a1e2a) so the chrome reads as a
   * clearly-defined band on every page, regardless of the underlying
   * page bg gradient. Earlier rgba(255,255,255,0.03/0.07) tints were
   * effectively invisible against the cap shell's near-black page
   * (#0F1729) — user repeatedly reported "no header bar." Solid bg
   * + visible border eliminates that whole class of bug. */
  /* v451 (2026-05-22): header presence per Owner — deepened bg to
   * #151C2B, stronger border #263248, + a drop shadow so the chrome
   * sits clearly above the page (light header already has this). */
  background: #151C2B;
  border-bottom: 1px solid #263248;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
  gap: var(--space-3);
  position: relative;     /* anchor for the absolute-centered panda */
  /* v415 (2026-05-15) — z-index above game-end and modal overlays
   * (which sit at z-index:1000) so the settings gear is always
   * reachable. Without this, the overlay covered the topbar and
   * users couldn't change theme from game-end / sign-in / sign-up
   * screens. The bottom-nav gets the same treatment for symmetry. */
  z-index: 1100;
}

/* v238 (2026-05-09): hardcoded minimum landscape phone clearance.
 * Capacitor Android does not always expose env(safe-area-inset-*)
 * reliably — the gesture zone variant in particular depends on the
 * `WindowCompat.setDecorFitsSystemWindows(false)` setting. When env()
 * returns 0, my v237 padding collapsed back to 16 px and the gear
 * stayed inside the right-edge swipe zone. `max(48px, ...)` guarantees
 * at least 48 px of left/right padding on phone landscape regardless. */
/* ============================================================
 * Header / panda / matchup-rail visibility policy (v546, 2026-06-06)
 * ------------------------------------------------------------
 * TOKENIZED show-hide (Owner). The policy lives in three custom
 * properties; @media + state selectors set their VALUES; three consumer
 * rules read them — so a visibility change happens in ONE place, never
 * in scattered per-page display rules.
 *   (1) portrait GAMEPLAY → header hidden + matchup settings/sound rail
 *   (2) mobile LANDSCAPE (phone, ≤500px tall) → header hidden on EVERY
 *       screen (Owner 2026-06-06 — supersedes v507/v513, which hid only
 *       game pages and exempted dashboard/leaderboard/sign-in/lists)
 *   (3) header panda → hidden everywhere
 * Reachability when the header is gone: GAMEPLAY keeps settings/sound on
 * the matchup rail (.matchup-actions, injected by top-bar.js); OTHER
 * screens keep their per-screen rail (.cap-settings-anchor /
 * .page-actions-anchor, stamped by ensureSettingsAnchor) + the bottom-nav
 * for navigation. In portrait the game-end overlay restores the header
 * so results keep brand context.
 * Scope = [data-moyako-v2] → applies on web + iOS + Android (no native
 * gate), per Owner "web, ios, android".
 * ============================================================ */
/* Defaults on :root ONLY (not [data-moyako-v2]) — the .page wrapper ALSO
 * carries data-moyako-v2, so declaring the defaults on [data-moyako-v2]
 * re-set them on .page and shadowed the body-level @media override for the
 * header's subtree. :root declares once; body's override then cascades
 * through .page → .topbar. */
:root {
  --topbar-display: flex;            /* default: header visible */
  --topbar-panda-display: none;      /* (3) panda off in the header, always */
  --matchup-actions-display: none;   /* rail hidden unless a hidden-header gameplay screen shows it */
}
[data-moyako-v2] .topbar               { display: var(--topbar-display) !important; }
[data-moyako-v2] .topbar .topbar-panda { display: var(--topbar-panda-display) !important; }
[data-moyako-v2] .matchup-actions      { display: var(--matchup-actions-display) !important; }

/* (1) PORTRAIT gameplay — hide the header, surface the matchup rail.
 * Triple selector covers every game's state convention: data-state
 * playing/play (sudoku etc.) AND the game-agnostic .gameplay-pane fallback
 * (memory/maze/block-puzzle don't set data-state reliably on web). */
@media (orientation: portrait) {
  body[data-moyako-v2][data-no-bottom-nav][data-state="playing"],
  body[data-moyako-v2][data-no-bottom-nav][data-state="play"],
  body[data-moyako-v2][data-no-bottom-nav]:has(.gameplay-pane:not([hidden])) {
    --topbar-display: none;
    --matchup-actions-display: inline-flex;
  }
  /* Restore the header on the game-end / modal overlay (context + settings). */
  body[data-moyako-v2][data-no-bottom-nav]:has(.overlay.active, .overlay.show, .modal.overlay.active) {
    --topbar-display: flex;
    --matchup-actions-display: none;
  }
}
/* (2) MOBILE LANDSCAPE (phone, ≤500px tall) — hide the header on EVERY screen.
 * Tablets (height > 500px) keep the header (room for it). */
@media (orientation: landscape) and (max-height: 500px) {
  body[data-moyako-v2] { --topbar-display: none; }
  /* Gameplay also surfaces the matchup settings/sound rail. */
  body[data-moyako-v2][data-no-bottom-nav][data-state="playing"],
  body[data-moyako-v2][data-no-bottom-nav][data-state="play"],
  body[data-moyako-v2][data-no-bottom-nav]:has(.gameplay-pane:not([hidden])) {
    --matchup-actions-display: inline-flex;
  }
}
/* Matchup settings/sound rail — intrinsic styling, GLOBAL scope (v546,
 * 2026-06-06). Hoisted from the landscape-only @media block (moyako-layout
 * ~6957) so the rail also renders correctly in PORTRAIT gameplay, now that
 * the token policy shows it there (req 1). DISPLAY stays governed by
 * --matchup-actions-display above; this only sizes + positions the rail and
 * its buttons. Same values as the landscape block, so the two coexist
 * (identical result where both apply; only this one applies in portrait). */
[data-moyako-v2] .matchup-actions {
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
}
[data-moyako-v2] .matchup-actions button {
  width: 32px !important; height: 32px !important;
  min-width: 32px !important; min-height: 32px !important;
  max-width: 32px !important; max-height: 32px !important;
  flex: 0 0 32px !important;
  padding: 0 !important;
  background: transparent !important;
  border: 0 !important;
  font-size: 18px !important;
  line-height: 32px !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  text-align: center !important;
  vertical-align: middle !important;
}
/* High-contrast icons on the dark matchup band + position flush-right
 * inside the relative matchup row (parity with the landscape rules). */
[data-moyako-v2] .moyako-board-center .board-lower-matchup { position: relative; }
[data-moyako-v2] .moyako-board-center .matchup-actions button { color: rgba(255, 255, 255, 0.92) !important; }
[data-moyako-v2] .moyako-board-center .matchup-actions button svg { stroke: currentColor !important; }
[data-moyako-v2] .moyako-board-center .matchup-actions {
  position: absolute !important;
  right: 8px !important;
  top: 50% !important;
  transform: translateY(-50%) !important;
  margin: 0 !important;
  z-index: 2 !important;
}

/* Light theme — silver/gray metallic 3D gradient on TopBar (per
 * user "header... gradient which feels elegant professional and
 * vivid"). Matches the picker-card metallic aesthetic so the chrome
 * reads as one design family. */
[data-theme="light"] [data-moyako-v2] .topbar {
  background: linear-gradient(180deg, #EAEFF7 0%, #C8D2E2 100%);
  border-bottom: 1px solid rgba(15, 23, 42, 0.14);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    inset 0 -1px 0 rgba(15, 23, 42, 0.06),
    0 2px 6px rgba(15, 23, 42, 0.08);
}
[data-theme="light"] [data-moyako-v2] .topbar-title,
[data-theme="light"] [data-moyako-v2] .topbar-back,
[data-theme="light"] [data-moyako-v2] .topbar-sound,
[data-theme="light"] [data-moyako-v2] .topbar-settings,
[data-theme="light"] [data-moyako-v2] .topbar-streak {
  color: #0F172A;
}
[data-theme="light"] [data-moyako-v2] .topbar-subtitle {
  color: #475569;
}
/* Task 2.3 — Panda is in the topbar's normal flex flow as the middle
 * sibling of [.topbar-left | .topbar-panda | .topbar-right]. The parent
 * .topbar's `justify-content: space-between` distributes them naturally,
 * which means panda's vertical position inherits the bar's
 * `align-items: center` and moves WITH the chrome on rotation / inset
 * changes — no absolute-positioning math needed.
 *
 * Earlier revisions positioned panda absolutely at top:50% (or with an
 * env(safe-area-inset-top) offset to compensate for padding-top inset).
 * When the bar's effective height changed (cap shell padding, S4
 * viewport revert, etc.) the absolute math floated panda outside the
 * visible bar. Going static eliminates the whole class of bugs. */
[data-moyako-v2] .topbar .topbar-panda {
  position: static;
  transform: none;
}
[data-moyako-v2] .topbar-left {
  display: flex; align-items: center; gap: var(--space-3); min-width: 0;
}
[data-moyako-v2] .topbar-back {
  font-size: 24px; color: var(--text-primary); text-decoration: none;
  line-height: 1; padding: 4px 8px;
}
[data-moyako-v2] .topbar-logo {
  display: inline-flex; align-items: center;
  height: 48px; text-decoration: none;
  margin-right: var(--space-1);
}
/* Logo + panda sized to span the title-top to subtitle-bottom range
 * (~44px on a 56px topbar). Without this they sat smaller than the
 * title+subtitle stack and looked under-weighted. */
[data-moyako-v2] .topbar-logo img {
  /* v526 — 44px→38px: was a wooden-only override; promoted to the shared base so
     the compact topbar logo is identical across themes (Owner §15: structure shared,
     colour/font per theme). */
  height: 38px; width: auto; display: block;
}
[data-moyako-v2] .topbar-titles { display: flex; flex-direction: column; min-width: 0; }
[data-moyako-v2] .topbar-title {
  font-size: var(--font-size-h3); font-weight: 500; color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Moyako+ subscribers get a ✨ after the shared header title → "Moyako ✨" on
   EVERY page (Owner 2026-05-27). Keyed on body.has-plus — the same subscriber
   flag the Moyako+ subscriber badge (top-bar.js) uses, so they light up together
   when entitlement/billing is wired. Not v2-scoped, so it also covers the older
   (non-v2) pages that render the shared topbar. */
body.has-plus .topbar-title::after {
  content: " \2728";   /* ✨ */
  margin-left: 3px;
}
[data-moyako-v2] .topbar-subtitle {
  font-size: var(--font-size-small); color: var(--text-secondary);
}
/* v289_2 (2026-05-10): subtitle stays visible on narrow phones too.
 * Per user "Header bar Under Moyako, there's a subtitle, which I
 * couldn't see on the preview screen now". The earlier hide at
 * < 400 px was an older space-saving rule; the topbar now has enough
 * room for the subtitle on a 375 px viewport. */
[data-moyako-v2] .topbar-panda {
  background: transparent; border: 0; cursor: pointer;
  padding: 4px;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 0;
}
[data-moyako-v2] .topbar-panda img {
  width: 44px; height: 44px;
  display: block; border-radius: 50%;
}
[data-moyako-v2] .topbar-right {
  display: flex; align-items: center; gap: var(--space-2);
}
[data-moyako-v2] .topbar-streak {
  font-size: var(--font-size-small); color: var(--accent); font-weight: 500;
}
[data-moyako-v2] .topbar-sound,
[data-moyako-v2] .topbar-settings {
  background: transparent; border: 0; color: var(--text-secondary);
  cursor: pointer; padding: 4px; line-height: 1;
  /* Equal size + bigger glyphs (per user "should be same size
   * and big better"). Fixed 36 px square keeps both icons on the
   * same baseline regardless of glyph metrics; font-size 22 px
   * scales the SVG/emoji inside.
   *
   * v289 (2026-05-10): !important + min/max overrides to beat the
   * global `.btn { min-width: 44 }` tap-target rule that was
   * inflating these to 44×44 on web. Per user "cap mobile one is
   * the base align to that icons" — web matches the 36×36 cap
   * mobile baseline. */
  width: 36px !important;
  height: 36px !important;
  min-width: 36px !important;
  min-height: 36px !important;
  max-width: 36px !important;
  max-height: 36px !important;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 22px;
  /* v237 (2026-05-09): tap-priority hardening for cap-shell landscape.
   * `touch-action: manipulation` opts out of double-tap-zoom and
   * scroll gestures so the WebView doesn't delay/cancel the click;
   * `position:relative; z-index:2` keeps the button above any
   * sibling pseudo-elements that overlap (e.g. ad-slot pseudo-
   * borders). Combined with the parent topbar's safe-area-inset
   * padding, the gear is clear of Android's right-edge swipe
   * gesture zone. */
  position: relative;
  z-index: 2;
  touch-action: manipulation;
}
[data-moyako-v2] .topbar-sound svg,
[data-moyako-v2] .topbar-settings svg {
  width: 22px; height: 22px;
  display: block;
}

/* ============ BottomNav ============ */
[data-moyako-v2] .bottom-nav {
  display: flex;
  /* Same subtle slate as the TopBar so chrome reads as one band. */
  background: rgba(255, 255, 255, 0.03);
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  /* v4 §D.2 canon: 56px tall on every page, even when the .page flex
     wrapper isn't present (e.g. marketing homepage). Ensures each
     .nav-item meets Apple HIG 44pt / Android 48dp tap target. */
  min-height: 56px;
  height: 56px;             /* equal across all pages — no padding from legacy rules */
  padding: 0;
  box-sizing: border-box;
  /* v415 (2026-05-15) — sits above game-end / modal overlays (z-index
   * 1000) so the cap-nav stays reachable. */
  position: relative;
  z-index: 1100;
}
[data-moyako-v2] .bottom-nav .nav-item {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px;
  text-decoration: none;
  /* v451 (2026-05-22): readable inactive label (was faint --text-tertiary)
   * per Owner "make inactive labels darker for readability".
   * v453 (2026-05-22): brightened #94A3B8 → #AAB6C8 per Owner results
   * audit — better dark-mode contrast for inactive nav. */
  color: #AAB6C8;
  font-size: var(--font-size-micro);
  font-weight: 600;
  /* Make the full item height tappable, not just the text/icon stack. */
  min-height: 56px;
}
/* v451: one consistent active colour — orange #F97316 (--nav-active),
 * per Owner "use one active color only: orange".
 * v454 (2026-05-22): !important — the legacy dark-mode chrome block in
 * components.js (`.nav-item { color:#C5CAD5 !important }`) blankets every nav
 * item grey in DARK mode and targets the old `.active` class, not the v2
 * `.is-active`, so the active item never went orange in dark mode (light mode
 * was fine — that block is dark-only). Higher specificity + !important wins. */
[data-moyako-v2] .bottom-nav .nav-item.is-active { color: #F97316 !important; }
/* Light theme — same metallic gradient as TopBar (mirror, with
 * border-top instead of bottom). */
[data-theme="light"] [data-moyako-v2] .bottom-nav {
  background: linear-gradient(180deg, #C8D2E2 0%, #EAEFF7 100%);
  border-top: 1px solid rgba(15, 23, 42, 0.14);
  box-shadow:
    inset 0 -1px 0 rgba(255, 255, 255, 0.9),
    inset 0 1px 0 rgba(15, 23, 42, 0.06),
    0 -2px 6px rgba(15, 23, 42, 0.08);
}
[data-theme="light"] [data-moyako-v2] .bottom-nav .nav-item {
  color: #64748B;           /* v451: --nav-inactive (readable) */
}
[data-theme="light"] [data-moyako-v2] .bottom-nav .nav-item.is-active {
  color: #F97316 !important;  /* v451: --nav-active orange, consistent. v454 !important — see dark rule above. */
}
[data-moyako-v2] .bottom-nav .nav-icon { font-size: 18px; line-height: 1; }
[data-moyako-v2] .bottom-nav .nav-label { font-size: 11px; }  /* v451: slightly larger per Owner */

/* Party nav icon is the 🎉 emoji like the other tabs (Owner). Renders in
 * colour on real devices; the headless preview's emoji font lacks U+1F389
 * (QA-tool gap only). No special per-slug styling needed. */

/* Mobile: nav-bar slightly shorter than the 56px header (per user
   "close to header may be a bit smaller than it") — 52px keeps icon
   + label legible without dwarfing the topbar. Apple HIG 44pt tap
   target preserved on the .nav-item. Stays equal across all v2
   pages because every page reads from this shared rule. */
@media (max-width: 768px) {
  [data-moyako-v2] .bottom-nav {
    min-height: 52px;
    height: 52px;
    flex: 0 0 52px;     /* overrides --footer-h flex-basis from layout */
  }
  [data-moyako-v2] .page .bottom-nav { flex: 0 0 52px; }
  [data-moyako-v2] .bottom-nav .nav-item {
    min-height: 52px;
  }
  [data-moyako-v2] .bottom-nav .nav-icon { font-size: 17px; }
  [data-moyako-v2] .bottom-nav .nav-label { font-size: 11px; }  /* v451: was 10 — bumped per Owner */
}

/* ── Flicker-free bottom-nav: reserve its slot BEFORE the JS mounts it ─────────
 * The nav is rendered by JS after first paint, so on each navigation the page
 * painted WITHOUT it, then it popped in with a content shift (Owner 2026-06-08:
 * "navbar hides and shows back"). This ::after spacer reserves the nav's exact
 * height AND mirrors its background, so the bar reads as present from the first
 * frame; it vanishes the instant the real nav exists (:has()), so the two never
 * stack — only the nav's icons hydrate in (imperceptible). Kept SEPARATE from
 * the .bottom-nav rule so a :has()-unaware engine can only ever drop the
 * spacer, never the real nav's styling. Gated to web (cap uses #cap-nav) +
 * pages that actually carry a nav (not [data-no-bottom-nav] game shells). */
html:not([data-moyako-native]) body:not([data-no-bottom-nav]) [data-moyako-v2].page:not(:has(.footer.bottom-nav))::after {
  content: '';
  display: block;
  flex: 0 0 56px;
  background: rgba(255, 255, 255, 0.03);
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  box-sizing: border-box;
}
@media (max-width: 768px) {
  html:not([data-moyako-native]) body:not([data-no-bottom-nav]) [data-moyako-v2].page:not(:has(.footer.bottom-nav))::after { flex: 0 0 52px; }
}
/* Light theme mirrors the bottom-nav's metallic gradient (see the .bottom-nav
 * [data-theme="light"] rule above). */
html[data-theme="light"]:not([data-moyako-native]) body:not([data-no-bottom-nav]) [data-moyako-v2].page:not(:has(.footer.bottom-nav))::after {
  background: linear-gradient(180deg, #C8D2E2 0%, #EAEFF7 100%);
  border-top: 1px solid rgba(15, 23, 42, 0.14);
  box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.9), inset 0 1px 0 rgba(15, 23, 42, 0.06), 0 -2px 6px rgba(15, 23, 42, 0.08);
}

/* ============ CapNav (Capacitor-only bottom navigation) ============
 *
 * Mounted by `shared/components/cap-nav.js` on cap-shell pages
 * (html[data-moyako-native]). Visually mirrors `.bottom-nav` but is
 * its own DOM element (#cap-nav) because the cap shell needs an
 * always-fixed footer that suppresses during gameplay/question state.
 *
 * Uses design tokens per CAP-SHELL-LAYOUT §2.6 — no hardcoded colours.
 * Migrated from inline <style> injection in cap-nav.js (v272). */
#cap-nav {
  position: fixed; left: 0; right: 0; top: auto; bottom: 0;
  height: var(--footer-h, 56px);
  /* content-box (overrides the global *{box-sizing:border-box} reset that
   * #cap-nav otherwise inherits) so padding-bottom: env(safe-area-inset-bottom)
   * below ADDS to the footer-h tap row instead of eating into it. Without this,
   * on iOS the 4 buttons crush into footer-h − inset (~22px) and overlap the
   * home-indicator (below the 44px tap floor). Matches CAP-SHELL-LAYOUT
   * §1.1/§2.9. env()=0 on web/Android → total stays footer-h (unchanged). */
  box-sizing: content-box;
  display: flex;
  background: var(--bg-surface, #1A2744);
  border-top: 1px solid var(--border, rgba(255,255,255,0.12));
  z-index: 9999;
  margin: 0;
  transform: none;
  /* Sys-info gesture inset reserved as padding-bottom (CAP-SHELL §2.1).
   * L/R env insets handled by moyako-layout.css §3.14 cap-shell rule. */
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
#cap-nav button {
  flex: 1; min-width: 0;
  background: transparent; border: 0;
  color: var(--text-tertiary, #9AA3B7);
  font: 500 10px/1 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  padding: 0 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#cap-nav button .cap-nav-icon { font-size: 18px; line-height: 1; }
#cap-nav button span:last-child { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
#cap-nav button[aria-current="page"] { color: var(--action, #4CAF50); }
#cap-nav button:active {
  /* Subtle press tint — derive from --action token via colour-mix
   * fallback. The 8% rgba mirrors the legacy emerald active flash. */
  background: rgba(76, 175, 80, 0.08);
}

/* Cap-nav suppression — gameplay + assessment-question states.
 * Per CAP-SHELL-LAYOUT §2.4. Mirrors the body-state logic from the
 * pre-v272 inline-style block. */
body[data-cap-state="play"] #cap-nav,
body[data-state="playing"] #cap-nav,
body[data-state="play"] #cap-nav,
body.verbal-active #cap-nav,
body.pattern-active #cap-nav,
body.reaction-active #cap-nav,
body.spatial-active #cap-nav,
body.memory-active #cap-nav,
body.number_seq-active #cap-nav,
body.attention-active #cap-nav { display: none; }

/* Per-theme chrome — #cap-nav visually mirrors .bottom-nav.
 *
 * v436 (2026-05-16) fix: #cap-nav cannot rely on var(--bg-surface).
 * In the light theme that token is the dark-navy TILE colour BY DESIGN
 * (cards pop on the white page) — so an unthemed #cap-nav stayed dark
 * navy on a light page. Each theme now gets explicit chrome rules that
 * match the .bottom-nav treatment exactly. Dark theme keeps the base
 * rule's var(--bg-surface) (#1A2744 — correct there). Active colour
 * picks up --action (theme-aware); the press tint stays a green wash. */
[data-theme="light"] #cap-nav {
  background: linear-gradient(180deg, #C8D2E2 0%, #EAEFF7 100%);
  border-top: 1px solid rgba(15, 23, 42, 0.14);
  box-shadow:
    inset 0 -1px 0 rgba(255, 255, 255, 0.9),
    inset 0 1px 0 rgba(15, 23, 42, 0.06),
    0 -2px 6px rgba(15, 23, 42, 0.08);
}
[data-theme="light"] #cap-nav button { color: #475569; }

/* Wooden — #cap-nav mirrors the web .bottom-nav walnut footer (styles.css:768).
 * Without it the cap-nav fell back to var(--bg-surface) = parchment; on iOS the
 * env(safe-area-inset-bottom) padding then filled the home-indicator strip with
 * parchment. Walnut blends it into the wood. (Owner 2026-06-09.) */
[data-theme="wooden"] #cap-nav {
  background: linear-gradient(180deg, #3A220F 0%, #241307 100%) !important;
  border-top: 1px solid var(--moyako-border-strong) !important;
}
[data-theme="wooden"] #cap-nav button { color: #D6BE8A !important; }
[data-theme="wooden"] #cap-nav button[aria-current="page"] { color: #F1C46B !important; }

/* ============ AdSlot ============ */
[data-moyako-v2] .ad-slot {
  background: var(--ad-placeholder);
  display: flex; align-items: center; justify-content: center;
}
[data-moyako-v2] .ad-slot .ad-house {
  color: var(--text-tertiary); font-size: var(--font-size-small);
}

/* ============ GameCard ============ */
[data-moyako-v2] .game-card {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-4);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  text-decoration: none; color: var(--text-primary);
  aspect-ratio: 3 / 2;
}
@media (min-width: 900px) {
  [data-moyako-v2] .game-card { aspect-ratio: 1.5 / 1; }
}
[data-moyako-v2] .game-card .game-icon { font-size: 32px; line-height: 1; }
[data-moyako-v2] .game-card .game-title {
  font-size: var(--font-size-h3); font-weight: 500;
}
[data-moyako-v2] .game-card .game-skills {
  display: flex; flex-wrap: wrap; gap: var(--space-1);
}
[data-moyako-v2] .skill-pill {
  font-size: var(--font-size-micro);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: var(--bg-surface-2);
  color: var(--text-secondary);
}
[data-moyako-v2] .game-card .btn-action {
  margin-top: auto;
  /* --action-dark on white meets 4.12:1 AA-large; --action fails at 2.78:1 */
  background: var(--action-dark); color: #fff;
  font-size: var(--font-size-small); font-weight: 500;
  padding: 6px 14px;
  border-radius: var(--radius-sm);
}

/* ============ Game icons — iconography-token pattern (v400) ============
 *
 * Architecture: each game has a CSS custom property like --icon-chess
 * holding a url(). The default set is defined alongside colour tokens
 * in moyako-layout.css. Per-theme overrides slot in there too — so
 * adding a "light variant" icon set later is one line per icon in a
 * theme block, zero JS, zero per-card edits.
 *
 * Sizing/shape live HERE (shared); URL choice lives in tokens.
 *
 * Usage in markup:
 *   <span class="game-icon game-icon--chess" aria-hidden="true"></span>
 * — or for back-compat with <img> tags from v399, that still works.
 */
[data-moyako-v2] .game-icon,
.game-icon {
  /* v401 — centred horizontally within the card. Was inline-block,
   * which left-aligned the icon. Now block + margin auto.
   * v433 (2026-05-15) — backdrop tile REMOVED per user "game tiles
   * not colored as tests". Test tile icons sit directly on the card
   * background with no inner rectangle; v422's per-theme tile recipe
   * created a "box-in-a-box" look on game tiles that read busier than
   * the test tile equivalent. Icon PNGs already have their own
   * branded backdrop, so the outer rectangle was visually redundant.
   *
   * Reverted: background-color, border, box-shadow (all 3 layers of
   * v422's tile recipe). Kept: size, centering, background-image
   * positioning — the icon still renders at 72×72 sized 86% within
   * that box, just without the cream/dark surface behind it. */
  display: block;
  margin-left: auto;
  margin-right: auto;
  width: 72px;
  height: 72px;
  background-position: center;
  background-repeat: no-repeat;
  background-size: 86% 86%;
  border-radius: 14px;
}
.game-icon--chess        { background-image: var(--icon-chess); }
.game-icon--sudoku       { background-image: var(--icon-sudoku); }
.game-icon--memory       { background-image: var(--icon-memory); }
.game-icon--maze         { background-image: var(--icon-maze); }
.game-icon--block-puzzle { background-image: var(--icon-block-puzzle); }
.game-icon--word-puzzle  { background-image: var(--icon-word-puzzle); }
.game-icon--backgammon   { background-image: var(--icon-backgammon); }
.game-icon--word-search  { background-image: var(--icon-word-search); }
.game-icon--slide-out-puzzle { background-image: var(--icon-slide-out-puzzle); }
/* v449v (2026-05-21): template-game icon → moyako master logo. */
.game-icon--template     { background-image: var(--icon-template); }

/* <img> children (back-compat with v399 markup) honour the same sizing
 * and shadow without needing to rewrite cards. */
.game-icon img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
}

/* v422 per-theme tile recipes for .game-icon REMOVED in v433 — the
 * outer cream/dark rectangle around game icons read as "box-in-a-box"
 * against the parent .moyako-game-card. Test tiles (assessment) have
 * never had this backdrop and read cleaner; aligned game tiles to
 * match per user direction 2026-05-15. Icon PNG renders directly on
 * the card background now. */

/* ============ RankRow ============ */
[data-moyako-v2] .rank-row {
  display: grid;
  grid-template-columns: 40px 32px 1fr auto auto;
  align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  font-size: var(--font-size-small);
}
[data-moyako-v2] .rank-row.is-self {
  background: rgba(255, 152, 0, 0.12);
  border-left: 3px solid var(--accent);
}
[data-moyako-v2] .rank-row .rank { color: var(--text-tertiary); font-weight: 500; }
[data-moyako-v2] .rank-row .avatar { font-size: 20px; }
[data-moyako-v2] .rank-row .name { color: var(--text-primary); }
[data-moyako-v2] .rank-row .you-tag { color: var(--accent); font-weight: 500; }
[data-moyako-v2] .rank-row .streak { color: var(--accent); }
[data-moyako-v2] .rank-row .score { color: var(--text-secondary); }

/* ============ StatGrid ============ */
[data-moyako-v2] .stat-grid {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2);
}
[data-moyako-v2] .stat-card {
  padding: var(--space-3);
  background: var(--bg-surface);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
}
[data-moyako-v2] .stat-value {
  font-size: var(--font-size-h1); font-weight: 500; color: var(--action);
}
[data-moyako-v2] .stat-label {
  font-size: var(--font-size-micro); color: var(--text-tertiary);
  text-transform: uppercase; letter-spacing: 0.5px;
}

/* ============ ProgressBar ============ */
[data-moyako-v2] .progress-bar {
  width: 100%; height: 6px;
  background: var(--bg-surface-2);
  border-radius: 3px; overflow: hidden;
}
[data-moyako-v2] .progress-bar.is-tall { height: 10px; }
[data-moyako-v2] .progress-fill {
  height: 100%;
  background: var(--success);
  transition: width 200ms ease;
}

/* ============ PlayerCard ============ */
[data-moyako-v2] .player-card {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3);
  background: var(--bg-surface);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
}
[data-moyako-v2] .player-card .avatar { font-size: 32px; line-height: 1; }
[data-moyako-v2] .player-card .identity { flex: 1; min-width: 0; }
[data-moyako-v2] .player-card .name { font-size: var(--font-size-h3); font-weight: 500; }
[data-moyako-v2] .player-card .meta {
  font-size: var(--font-size-small); color: var(--text-secondary);
}
[data-moyako-v2] .player-card .clock {
  font-size: var(--font-size-h2); color: var(--action);
  font-variant-numeric: tabular-nums;
}
[data-moyako-v2] .player-card.is-large .avatar { font-size: 56px; }
[data-moyako-v2] .player-card.is-large .name { font-size: var(--font-size-h1); }
[data-moyako-v2] .player-card.is-inline {
  padding: var(--space-2); border: 0; background: transparent;
}
[data-moyako-v2] .player-card.is-inline .avatar { font-size: 22px; }

/* ============ PandaCoach ============ */
[data-moyako-v2] .panda-coach {
  display: flex; gap: var(--space-3);
  padding: var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
[data-moyako-v2] .panda-coach .panda-avatar { font-size: 40px; line-height: 1; }
[data-moyako-v2] .panda-coach .speech { flex: 1; min-width: 0; }
[data-moyako-v2] .panda-coach .speech-label {
  font-size: var(--font-size-micro); color: var(--text-tertiary);
  letter-spacing: 0.5px; text-transform: uppercase;
  margin-bottom: 4px;
}
[data-moyako-v2] .panda-coach .speech-message {
  margin: 0;
  font-size: var(--font-size-small); color: var(--text-primary);
  line-height: 1.4;
}

/* ============ InfoDrawer ============
   Rules moved to shared/css/info-drawer.css on 2026-04-26 so they load
   on all game pages (chess + the 6 non-v2 games) rather than only when
   [data-moyako-v2] + moyako-components.css are both present. The
   standalone file is a <link> one-liner added to every game page. */

/* ============ SettingsModal — popover ============
 * Bottom-left of the gear icon (top-right of TopBar), small box, no
 * full-screen backdrop. Click-outside dismiss handled by JS. */
[data-moyako-v2] .settings-modal.settings-popover {
  position: fixed;
  /* v237 (2026-05-09): popover top accounts for `viewport-fit=cover`
   * safe-area-inset-top so the panel doesn't tuck behind the topbar
   * on devices with notch/status-bar overlap (cap-shell APK on
   * Android landscape). Right edge similarly stays clear of the
   * gesture zone. */
  top: calc(env(safe-area-inset-top, 0px) + var(--header-h, 56px) + 6px);
  right: calc(env(safe-area-inset-right, 0px) + var(--space-3, 12px));
  z-index: 1000;
  visibility: hidden;
  pointer-events: none;
}
[data-moyako-v2] .settings-modal.settings-popover.is-open {
  visibility: visible;
  pointer-events: auto;
}
[data-moyako-v2] .settings-popover .settings-panel {
  width: min(280px, calc(100vw - 24px));
  max-height: calc(100dvh - var(--header-h, 56px) - 24px);
  background: var(--bg-surface);
  color: var(--text-primary);
  display: flex; flex-direction: column;
  border: 1px solid var(--border-strong, var(--border));
  border-radius: var(--radius-md);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.4);
  /* Subtle slide-down + fade entrance from the gear button. */
  transform: translateY(-8px);
  opacity: 0;
  transition: transform 180ms ease, opacity 180ms ease;
}
[data-moyako-v2] .settings-popover.is-open .settings-panel {
  transform: translateY(0);
  opacity: 1;
}
/* Light theme — flip the popover's interior tokens to dark-on-white
 * so all the children (.settings-title, .seg-btn, .settings-select,
 * etc.) read correctly. The page-level Light palette keeps text-* light
 * because tiles have a navy bg; the popover is a white card so text
 * needs to be dark inside it. */
[data-theme="light"] [data-moyako-v2] .settings-popover .settings-panel {
  --bg-surface:     #FFFFFF;
  --bg-surface-2:   rgba(15,23,42,0.04);
  --text-primary:   #0F1729;
  --text-secondary: #334155;
  --text-tertiary:  #64748B;
  --border:         rgba(15,23,42,0.10);
  --border-strong:  rgba(15,23,42,0.18);
  background: #FFFFFF;
  color: #0F1729;
  border-color: rgba(15,23,42,0.12);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
}
/* (v183, 2026-05-08) Settings-select light-theme override removed
 * per user 'align settings dropdown as well to create account -
 * avatar dropdown style'. The shared v171/v181 solid-3D select
 * rule now applies in both themes — settings dropdown matches the
 * avatar dropdown exactly (silver gradient surface in light,
 * navy in dark, inset 3D shadow + green focus ring). The option
 * popup-list bg still inherits from the universal
 * [data-moyako-v2] .settings-select option rule below. */
[data-theme="light"] [data-moyako-v2] .settings-popover .settings-select option {
  background: #FFFFFF;
  color: #0F1729;
}
/* ===== Settings popover — condensed interior ===== */
[data-moyako-v2] .settings-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
}
[data-moyako-v2] .settings-title {
  margin: 0; font-size: 13px; font-weight: 600;
  letter-spacing: 0.5px; text-transform: uppercase;
  color: var(--text-secondary);
}
[data-moyako-v2] .settings-close {
  background: transparent; border: 0; color: var(--text-primary);
  font-size: 18px; line-height: 1; cursor: pointer; padding: 2px 6px;
  min-width: 0; min-height: 0;
}
[data-moyako-v2] .settings-body {
  flex: 1; overflow-y: auto;
  /* v230 (2026-05-09): tightened padding 10/12 → 8/12 and gap 10 → 6
   * per user "settings can be more compact". */
  padding: 8px 12px;
  display: flex; flex-direction: column;
  gap: 6px;
}
[data-moyako-v2] .settings-group {
  display: flex; flex-direction: column; gap: 4px;
}
/* The HTML `hidden` attribute should always win — it's how the
   Remove Ads tile keeps itself off-screen on web while the cap
   shell reveals it. Without this, the .settings-group `display:flex`
   above shadows `hidden` and the row leaks on every surface. */
[data-moyako-v2] .settings-group[hidden] {
  display: none !important;
}
/* Same problem at the sub-row level: when hydrateRemoveAds toggles
   between data-remove-ads-state="free" (Buy CTA) and "purchased"
   (Ads Removed · Thank you), it sets el.hidden = true/false. But
   .sm-toggle-row carries `display: flex` from its own rule below,
   which shadows the hidden attribute — both rows leak together.
   This rule restores `hidden` semantics for every sm-toggle-row,
   not just the Remove Ads pair, so the same fix covers any future
   two-state row pattern. */
[data-moyako-v2] .sm-toggle-row[hidden] {
  display: none !important;
}
[data-moyako-v2] .settings-label {
  font-size: 10px;
  color: var(--text-tertiary);
  letter-spacing: 0.6px;
  text-transform: uppercase;
}
[data-moyako-v2] .settings-segmented {
  display: flex;
  background: var(--bg-surface-2);
  border-radius: 6px;
  padding: 2px;
}
[data-moyako-v2] .seg-btn {
  flex: 1;
  background: transparent; border: 0;
  color: var(--text-secondary);
  padding: 5px 8px;
  cursor: pointer;
  font-size: 12px;
  border-radius: 4px;
  font-family: inherit;
  min-width: 0; min-height: 0;
}
[data-moyako-v2] .seg-btn.is-active {
  background: var(--action);
  color: #fff;
  font-weight: 500;
}
/* Background picker — icon-only chips. Each chip shows its season
 * glyph; active chip gets an outline ring instead of a fill swap so
 * the icon stays legible. */
[data-moyako-v2] .seg-btn--bg {
  flex: 0 0 auto;
  padding: 4px 8px;
  font-size: 14px;
  line-height: 1;
}
[data-moyako-v2] .seg-btn--bg .seg-icon {
  display: inline-block;
}
[data-moyako-v2] .seg-btn--bg.is-active {
  background: transparent;
  outline: 2px solid var(--action);
  outline-offset: -2px;
  color: inherit;
}
/* (v183, 2026-05-08) .settings-select bg / border / radius /
 * padding / font-size overrides REMOVED per user 'align settings
 * dropdown as well to create account - avatar dropdown style'. The
 * shared v171/v181 solid-3D rule covers all <select> elements
 * (var(--bg-surface) bg, var(--border) border, 10 px radius, inset
 * 3D shadow). Only the OS-level option list bg stays here so the
 * dropdown popup contrasts on dark theme. */
[data-moyako-v2] .settings-select option { background: var(--bg-surface); }
/* Theme/Language dropdown VALUE matched to the row-label size. The shared v2
   solid-3D <select> rule (§3.18) renders selects at 15px !important, but the row
   title (.sm-toggle-label) is 12px — so the dropdown value read LARGER than its
   own title (Owner 2026-05-27: "theme and language texts inside the dropdown
   seem bigger than their titles"). Pin the inline settings selects to 12px. */
[data-moyako-v2] .settings-select.sm-select-inline { font-size: 12px !important; }
[data-moyako-v2] .settings-divider {
  border: 0; border-top: 1px solid var(--border); margin: 2px 0;
}
[data-moyako-v2] .settings-links {
  display: flex; flex-wrap: wrap; gap: 4px 12px;
  font-size: 11px;
}
[data-moyako-v2] .settings-links a {
  color: var(--text-secondary);
  text-decoration: none;
  padding: 4px 0;
  font-size: 11px;
  border: 0;
}
[data-moyako-v2] .settings-links a:hover { color: var(--text-primary); text-decoration: underline; }
[data-moyako-v2] .settings-footer {
  padding: 6px 12px;
  text-align: center;
  font-size: 10px;
  color: var(--text-tertiary);
  border-top: 1px solid var(--border);
}

/* v214 (2026-05-08): top guest CTA inside the settings panel. Big primary
 * + secondary buttons stacked, prominent enough to be the first thing a
 * guest sees on opening Settings. Per user "for the guest user Sign in /
 * Create user should be on the top to encourage the users apply best
 * practice design". Belt-and-braces guest-hiding: any element marked
 * data-auth-only="user" stays hidden until hydrateAccount confirms a
 * logged-in session, killing the brief flash where logged-in items would
 * render before being hidden by JS. */
[data-moyako-v2] .settings-popover .sm-guest-cta-top {
  display: flex !important;
  flex-direction: column;
  gap: 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
[data-moyako-v2] .settings-popover .sm-guest-cta-msg {
  margin: 0;
  font-size: 12px;
  line-height: 1.4;
  color: var(--text-secondary);
}
[data-moyako-v2] .settings-popover .sm-guest-cta-row {
  display: flex;
  gap: 8px;
}
[data-moyako-v2] .settings-popover .sm-cta-btn {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 10px 12px;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  letter-spacing: 0.2px;
  box-sizing: border-box;
  transition: opacity 0.15s ease, transform 0.08s ease;
}
[data-moyako-v2] .settings-popover .sm-cta-btn--primary {
  /* v395 — tokenised; was hard-coded green gradient (#45a049 end).
   * Now picks up --action / --action-dark which flip per theme:
   *  dark: green        light: orange */
  background: linear-gradient(135deg, var(--action) 0%, var(--action-dark) 100%);
  color: #fff;
  border: 1px solid var(--action-dark);
}
[data-moyako-v2] .settings-popover .sm-cta-btn--secondary {
  background: transparent;
  color: var(--action);
  border: 1px solid var(--action);
}
[data-moyako-v2] .settings-popover .sm-cta-btn:hover { opacity: 0.92; }
[data-moyako-v2] .settings-popover .sm-cta-btn:active { transform: translateY(1px); }
/* Belt-and-braces hide. data-auth-only attributes already carry the `hidden`
 * boolean attribute initially; this reinforces it with !important so any
 * ancestor display rule can't unhide a logged-in-only block when no user
 * is signed in. hydrateAccount sets `hidden = false` on the relevant
 * elements once it confirms a session. */
[data-moyako-v2] .settings-popover [data-auth-only="user"][hidden],
[data-moyako-v2] .settings-popover [data-auth-only="guest"][hidden] {
  display: none !important;
}

/* ============ InfoIcon ============ */
[data-moyako-v2] .info-icon {
  width: 28px; height: 28px;
  border-radius: 50%;
  /* --info border gives 9.91:1 vs border-strong 2.01:1 — info-icon border is
   * the sole visual identifier of the control, so WCAG 1.4.11 applies. */
  border: 1px solid var(--info);
  background: transparent; color: var(--info);
  cursor: pointer;
  font-size: var(--font-size-h3); line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}

/* ============ DifficultyList ============ */
[data-moyako-v2] .difficulty-list {
  display: flex; flex-direction: column; gap: var(--space-2);
}
[data-moyako-v2] .difficulty {
  /* Compact pill (v161, 2026-05-08): min-height 44 → 40 to bring
   * 5-pill + 2-action + sign-in row stack inside Container 2 without
   * scroll on phone landscape (288 px budget). 40 px is below the
   * WCAG 2.2 AA 44 px floor but stays within the W3C "important
   * controls in dense lists" 24 px allowance — pills are still
   * comfortably tappable and the picker is effectively a list. */
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  cursor: pointer;
  font-family: inherit; font-size: var(--font-size-body);
  text-align: left;
  min-height: 40px;
  width: 100%;
}
/* v449 (2026-05-20): selected difficulty pill — no extra ring, no
 * tinted green background. The existing 1px border is recoloured to
 * match the tier's dot colour (per user "no additional border needed,
 * may be existing tiny border can colored with the dot color inside").
 * Background stays neutral so the dot + label hold the visual weight,
 * and the dot's hue carries through to the border for an at-a-glance
 * "this tier is selected" cue without an over-saturated highlight. */
[data-moyako-v2] .difficulty.is-selected {
  background: var(--bg-surface);
}
[data-moyako-v2] .difficulty.is-selected[data-tier="beginner"] { border-color: #A5D6A7; }
[data-moyako-v2] .difficulty.is-selected[data-tier="easy"]     { border-color: #81C784; }
[data-moyako-v2] .difficulty.is-selected[data-tier="medium"]   { border-color: #FFB74D; }
[data-moyako-v2] .difficulty.is-selected[data-tier="hard"]     { border-color: #EF5350; }
[data-moyako-v2] .difficulty.is-selected[data-tier="expert"]   { border-color: #9C27B0; }
[data-moyako-v2] .difficulty.is-selected[data-tier="auto"]     { border-color: #29B6F6; }

/* v319 — Game-end overlay: 2-row button layout + Start Again label
 * promotion + guest sign-in CTA. Per user 2026-05-11:
 *   - Industry best-practice button hierarchy (Sudoku.com/Chess.com)
 *   - Single visual class for selection state across surfaces
 *   - Guest conversion at the highest-engagement moment (post-win)
 */
.overlay-buttons--secondary {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 10px;
}
.overlay-buttons--primary {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 10px;
}
.overlay-buttons--secondary .overlay-action-btn,
.overlay-buttons--primary .overlay-action-btn {
  padding: 12px 10px;
  border-radius: 12px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid transparent;
  transition: transform 0.12s ease-out, box-shadow 0.15s, background 0.15s;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  /* v322 — equalize button heights so the secondary row (View / Share)
   * and the primary row (Exit / Start Again) sit at the same vertical
   * pitch. Without this, the primary buttons rendered taller due to
   * gradient/shadow padding, leaving the secondary row visibly shorter
   * and the rows looking "stepped". */
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
}
/* v323 — both rows are 3-up equal columns. Secondary: View / Share /
 * Rate Us. Primary: Exit / Replay / Next (always visible). Column
 * edges align cleanly across both rows. */
.overlay-buttons--secondary {
  grid-template-columns: 1fr 1fr 1fr;
}
.overlay-buttons--primary {
  grid-template-columns: 1fr 1fr 1fr !important;
}
/* v320 — primary action gets a wider track so the "START AGAIN" label
 * doesn't compete with Exit for equal space (Exit is tertiary anyway). */
.overlay-buttons--primary {
  grid-template-columns: 1fr 1.4fr;
}
/* When Next Level IS queued, restore 3-up equal-width grid */
.overlay-buttons--primary:has(#nextLevelBtn:not([style*="display: none"])):not(:has(#nextLevelBtn[style*="display:none"])) {
  grid-template-columns: 1fr 1fr 1fr;
}

/* v320 — hide the rank pill ("Sign in to rank") when the guest CTA tile
 * is present. Same call-to-action, redundant otherwise. */
.overlay-actions-card:has(.moyako-guest-cta) ~ .overlay-results-card .overlay-pills .overlay-pill:has(#rankValue),
.overlay-content:has(.moyako-guest-cta) .overlay-pill:has(#rankValue:not([data-has-rank])) {
  display: none;
}
/* Tertiary action (Exit) — lowest visual weight; readable but recedes */
.overlay-buttons--primary .btn-tertiary {
  background: rgba(255, 255, 255, 0.03);
  border-color: rgba(255, 255, 255, 0.08);
  color: rgba(236, 239, 244, 0.55);
}
.overlay-buttons--primary .btn-tertiary:hover {
  background: rgba(255, 255, 255, 0.06);
  color: rgba(236, 239, 244, 0.78);
}
[data-theme="light"] .overlay-buttons--primary .btn-tertiary {
  background: rgba(0, 0, 0, 0.03);
  border-color: rgba(0, 0, 0, 0.08);
  color: #6B7280;
}

/* v322 — Compact guest sign-in CTA. Single-row layout (no avatar):
 *   [Title + sub] · spacer · [Sign in] [Sign up]
 * Per user 2026-05-11: "make sign in/up container more compact, no
 * avatar needed there". Tile-styled per v318 vocab. */
.moyako-guest-cta {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  margin-bottom: 10px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, rgba(0, 0, 0, 0) 50%),
    linear-gradient(135deg, rgba(76, 175, 80, 0.14) 0%, rgba(33, 150, 243, 0.10) 100%);
  border: 1px solid rgba(76, 175, 80, 0.32);
  border-radius: 12px;
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.06) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.15) inset,
    0 4px 10px rgba(76, 175, 80, 0.08);
}
.moyako-guest-cta__body {
  flex: 1;
  min-width: 0;
}
.moyako-guest-cta__title {
  font-size: 13px;
  font-weight: 700;
  color: #ECEFF4;
  line-height: 1.2;
  margin-bottom: 2px;
}
.moyako-guest-cta__sub {
  font-size: 11px;
  color: rgba(236, 239, 244, 0.62);
  line-height: 1.3;
}
.moyako-guest-cta__actions {
  display: flex;
  flex-direction: row;
  gap: 6px;
  flex: 0 0 auto;
}
.moyako-guest-cta .btn {
  padding: 6px 10px;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 700;
  text-decoration: none;
  text-align: center;
  border: 1px solid transparent;
  transition: transform 0.12s ease-out, background 0.15s;
  white-space: nowrap;
}
.moyako-guest-cta__signin {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.12);
  color: rgba(236, 239, 244, 0.82);
}
.moyako-guest-cta__signin:hover { background: rgba(255, 255, 255, 0.10); }
.moyako-guest-cta__signup {
  /* v395 — tokenised; flips per theme via --action / --action-dark. */
  background: linear-gradient(135deg, var(--action) 0%, var(--action-dark) 100%);
  color: #FFFFFF;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.32);
}
.moyako-guest-cta__signup:hover { transform: translateY(-1px); }

/* v366 (2026-05-12) — DESIGN-RULES §5.6 proposal #3: post-game native
 * ad lives inside .overlay-narrative as a slide that fades in 3s
 * after the overlay opens (so the win-message reads first). Sized
 * compactly (max 72px tall) so it doesn't dominate the narrative.
 * Hidden when empty so we don't show a blank box. */
.gameend__ad[data-narrative-ad] {
  margin: 8px 0 0 0;
  min-height: 0;
  max-height: 120px;
  overflow: hidden;
  opacity: 0;
  animation: gameendAdFadeIn 320ms ease-out 3s forwards;
}
.gameend__ad[data-narrative-ad]:empty {
  display: none !important;
  animation: none;
}
@keyframes gameendAdFadeIn {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .gameend__ad[data-narrative-ad] {
    animation: none;
    opacity: 1;
  }
}

/* v351 (2026-05-12) — single-line banner with inline hyperlinks.
 * Replaces the side-by-side button pair with a conversational
 * sentence: "Please sign in / Sign Up to save your progress".
 * Compact horizontal banner appropriate for the new above-cards
 * position. */
.moyako-guest-cta--banner {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px 12px;
  font-size: 13px;
  line-height: 1.4;
  color: #ECEFF4;
}
.moyako-guest-cta--banner .moyako-guest-cta__message {
  text-align: center;
}
.moyako-guest-cta__link {
  color: #4CAF50;
  font-weight: 700;
  text-decoration: underline;
  cursor: pointer;
}
.moyako-guest-cta__link:hover {
  color: #66BB6A;
}
[data-theme="light"] .moyako-guest-cta--banner { color: #0F172A; }
[data-theme="light"] .moyako-guest-cta__link    { color: #2E7D32; }
[data-theme="light"] .moyako-guest-cta__link:hover { color: #1B5E20; }
[data-theme="light"] .moyako-guest-cta {
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.02) 0%, rgba(0, 0, 0, 0) 50%),
    linear-gradient(135deg, rgba(76, 175, 80, 0.10) 0%, rgba(33, 150, 243, 0.08) 100%);
}
[data-theme="light"] .moyako-guest-cta__title { color: #0F172A; }
[data-theme="light"] .moyako-guest-cta__sub   { color: #4A5568; }
[data-theme="light"] .moyako-guest-cta__signin {
  background: #FFFFFF;
  border-color: #D1D5DB;
  color: #1F2937;
}

/* v318 — Rewarded Modal styled as a premium Moyako tile.
 *
 * Per user 2026-05-11: "rewarded screen and containers with our tile
 * style, stylish". Upgrades the basic card to the same solid-3D tile
 * aesthetic used elsewhere (DESIGN-RULES §3.18) — inset highlights for
 * depth, accent top-border in brand green, sub-tile containers for the
 * panda crown + reward badge + action row. Everything reads as a stack
 * of Moyako tiles instead of a generic dialog.
 */
.moyako-rewarded-overlay {
  position: fixed; inset: 0; z-index: 10000;
  display: flex !important; align-items: center; justify-content: center;
  /* v388 (2026-05-13) — landscape safety: shorter viewports (~400px tall
   * on phone landscape) were clipping the action row. Allow the overlay
   * to scroll so the card is fully reachable; pad insets so the card
   * never touches the screen edges. */
  overflow-y: auto;
  padding: 12px;
  -webkit-overflow-scrolling: touch;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0.75) 100%);
  backdrop-filter: blur(8px) saturate(120%);
  -webkit-backdrop-filter: blur(8px) saturate(120%);
  padding: 20px;
  animation: moyakoRewardedFadeIn 0.22s ease-out;
}
.moyako-rewarded-overlay.is-leaving { animation: moyakoRewardedFadeOut 0.18s ease-out forwards; }
@keyframes moyakoRewardedFadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes moyakoRewardedFadeOut { from { opacity: 1; } to { opacity: 0; } }

/* Outer tile — the dialog itself.
 * Tile-style: linear-gradient bg, brand-green top accent border, inset
 * top highlight for depth, outset bottom shadow for elevation. */
.moyako-rewarded-card {
  /* v367 (2026-05-12): removed green top-border accent stripe per user.
   * Card now has uniform 1px border on all sides. Padding-top trimmed
   * to 20px (was 28px) since the panda no longer juts above the card. */
  position: relative;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, rgba(0, 0, 0, 0) 32%),
    linear-gradient(165deg, #2A3142 0%, #1C2233 100%);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: 18px;
  padding: 20px 22px 20px;
  max-width: 380px; width: 100%; text-align: center;
  /* v388 — pair with overlay overflow-y:auto. flex-shrink:0 stops the
   * card from being squished to nothing in a scroll container. */
  flex-shrink: 0;
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.06) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.30) inset,
    0 24px 60px rgba(0, 0, 0, 0.55);
  animation: moyakoRewardedScale 0.28s cubic-bezier(0.22, 1.2, 0.36, 1);
}

/* v388 — landscape compact mode. Phone landscape (≤ 500px tall)
 * collapses panda + paddings + margins so the card fits without
 * scrolling on a typical 360–420px landscape viewport. */
@media (orientation: landscape) and (max-height: 500px) {
  .moyako-rewarded-card {
    padding: 14px 18px 14px;
    max-width: 420px;
  }
  .moyako-rewarded-panda { width: 56px; height: 56px; padding: 4px; margin-bottom: 6px; }
  .moyako-rewarded-title { font-size: 18px; margin: 2px 0 4px; }
  .moyako-rewarded-body  { font-size: 13px; margin: 0 0 10px; }
  .moyako-rewarded-reward { padding: 6px 14px; margin-bottom: 10px; }
  .moyako-rewarded-actions .btn { padding: 10px 14px; font-size: 13px; }
}
@keyframes moyakoRewardedScale {
  from { transform: scale(0.90) translateY(8px); opacity: 0; }
  to   { transform: scale(1) translateY(0);     opacity: 1; }
}

/* Panda mascot — v367: keep inside the card (was margin-top: -52px
 * pulling it above the card boundary, which read as "cut from top").
 * Now sits fully inside the card with normal top spacing. */
.moyako-rewarded-panda {
  display: block; margin: 0 auto 12px;
  width: 96px; height: 96px;
  padding: 6px;
  background:
    radial-gradient(circle at 50% 35%, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0) 60%),
    linear-gradient(160deg, #2F3648 0%, #1E2535 100%);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 50%;
  object-fit: contain;
  box-shadow:
    0 8px 22px rgba(0, 0, 0, 0.30),
    0 1px 0 0 rgba(255, 255, 255, 0.08) inset;
}
.moyako-rewarded-title {
  margin: 4px 0 6px;
  font-size: 22px; font-weight: 800; letter-spacing: -0.01em;
  color: #ECEFF4;
  line-height: 1.2;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
}
.moyako-rewarded-body {
  margin: 0 0 18px;
  font-size: 14px; line-height: 1.5;
  color: rgba(236, 239, 244, 0.72);
  padding: 0 4px;
}

/* Reward pill — its own mini-tile. Gold-trimmed brand-green vibe so
 * the actual reward "pops" above body copy. */
.moyako-rewarded-reward {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 20px; margin-bottom: 20px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, rgba(0, 0, 0, 0) 50%),
    linear-gradient(135deg, rgba(76, 175, 80, 0.28) 0%, rgba(56, 142, 60, 0.18) 100%);
  border: 1px solid rgba(76, 175, 80, 0.50);
  border-radius: 999px;
  font-size: 15px; font-weight: 700;
  color: #A5D6A7;
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.08) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.18) inset,
    0 6px 16px rgba(76, 175, 80, 0.22);
}
.moyako-rewarded-reward-icon {
  font-size: 18px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
}
.moyako-rewarded-reward-text strong {
  color: #FFFFFF;
  font-weight: 800;
  font-size: 16px;
}

/* Action row — 2-up button tiles. */
.moyako-rewarded-actions {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
}
.moyako-rewarded-actions .btn {
  padding: 13px 16px;
  border-radius: 14px;
  font-size: 14px; font-weight: 700; letter-spacing: 0.01em;
  cursor: pointer;
  border: 1px solid transparent;
  transition: transform 0.12s ease-out, box-shadow 0.15s, background 0.15s;
  position: relative;
}
.moyako-rewarded-actions .btn-secondary {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, rgba(0, 0, 0, 0) 60%),
    rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.12);
  color: rgba(236, 239, 244, 0.78);
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.06) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.2) inset;
}
.moyako-rewarded-actions .btn-secondary:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.18);
}
.moyako-rewarded-actions .btn-secondary:active { transform: translateY(1px); }
.moyako-rewarded-actions .btn-primary {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 50%),
    linear-gradient(135deg, #66BB6A 0%, #388E3C 100%);
  color: #FFFFFF;
  border-color: rgba(0, 0, 0, 0.18);
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.30) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.18) inset,
    0 8px 18px rgba(76, 175, 80, 0.45);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
}
.moyako-rewarded-actions .btn-primary:hover {
  transform: translateY(-1px);
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.35) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.18) inset,
    0 10px 24px rgba(76, 175, 80, 0.55);
}
.moyako-rewarded-actions .btn-primary:active { transform: translateY(0); }

/* Light theme harmonization — keep the tile aesthetic, swap colors. */
[data-theme="light"] .moyako-rewarded-card {
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.02) 0%, rgba(0, 0, 0, 0) 32%),
    linear-gradient(165deg, #FFFFFF 0%, #F2F5FA 100%);
  border-color: #D1D5DB;
  /* v526 — accent expressed as COLOUR only; border-WIDTH stays 1px from the base
     rule so it doesn't diverge per theme (Owner §15). Was `border-top: 3px solid`. */
  border-top-color: #4CAF50;
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.6) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.05) inset,
    0 24px 60px rgba(0, 0, 0, 0.18),
    0 6px 18px rgba(76, 175, 80, 0.12);
}
[data-theme="light"] .moyako-rewarded-panda {
  background:
    radial-gradient(circle at 50% 35%, rgba(0, 0, 0, 0.03) 0%, rgba(0, 0, 0, 0) 60%),
    linear-gradient(160deg, #FFFFFF 0%, #E8EAED 100%);
  border-color: #D1D5DB;
}
[data-theme="light"] .moyako-rewarded-title { color: #0F172A; text-shadow: none; }
[data-theme="light"] .moyako-rewarded-body  { color: #4A5568; }
[data-theme="light"] .moyako-rewarded-reward-text strong { color: #1F2937; }
[data-theme="light"] .moyako-rewarded-actions .btn-secondary {
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.02) 0%, rgba(0, 0, 0, 0) 50%),
    #FFFFFF;
  border-color: #D1D5DB;
  color: #4A5568;
}

/* Wooden theme harmonization (#532) — parchment card + brass trim + green-felt
 * primary, mirroring [data-theme="wooden"] .modal-screen-card. That recipe was
 * meant for "every confirm/pause/reward modal" (Owner exit-modal 7.6), but the
 * older bespoke rewarded modal uses .moyako-rewarded-* class names, so the skin
 * never reached it → it rendered dark navy over a wooden game. */
[data-theme="wooden"] .moyako-rewarded-card {
  background: linear-gradient(180deg, #F8EAC5 0%, #E5D2AC 100%);
  border: 1px solid #B8893A;
  box-shadow:
    inset 0 1px 0 rgba(255, 247, 224, 0.5),
    0 16px 40px rgba(16, 8, 3, 0.45);
}
[data-theme="wooden"] .moyako-rewarded-panda {
  background:
    radial-gradient(circle at 50% 35%, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0) 60%),
    linear-gradient(160deg, #F8EAC5 0%, #E5D2AC 100%);
  border: 2px solid #B8893A;
  box-shadow: 0 2px 6px rgba(16, 8, 3, 0.30), inset 0 1px 0 rgba(255, 247, 224, 0.5);
}
[data-theme="wooden"] .moyako-rewarded-title { color: #2A1B12; text-shadow: none; }
[data-theme="wooden"] .moyako-rewarded-body  { color: #4B3626; }
[data-theme="wooden"] .moyako-rewarded-reward {
  background:
    linear-gradient(180deg, rgba(255, 247, 224, 0.40) 0%, rgba(0, 0, 0, 0) 60%),
    linear-gradient(135deg, #3D9A55 0%, #2F7D46 100%);
  border: 1px solid #D7AE54;
  color: #FFF3D0;
  box-shadow:
    inset 0 1px 0 rgba(255, 247, 224, 0.40),
    0 6px 16px rgba(16, 8, 3, 0.25);
}
[data-theme="wooden"] .moyako-rewarded-reward-text strong { color: #FFFFFF; }
[data-theme="wooden"] .moyako-rewarded-actions .btn-secondary {
  background: linear-gradient(180deg, #F8EAC5 0%, #E5D2AC 100%);
  border: 1px solid #B8893A;
  color: #3A2817;
  box-shadow: inset 0 1px 0 rgba(255, 247, 224, 0.5), 0 2px 6px rgba(16, 8, 3, 0.18);
}
[data-theme="wooden"] .moyako-rewarded-actions .btn-secondary:hover {
  background: linear-gradient(180deg, #FBEFCF 0%, #EAD8B4 100%);
}
[data-theme="wooden"] .moyako-rewarded-actions .btn-primary {
  background: linear-gradient(180deg, #3D9A55 0%, #2F7D46 100%);
  border: 1px solid #D7AE54;
  color: #FFF3D0;
  box-shadow: inset 0 1px 0 rgba(255, 247, 224, 0.35), 0 8px 18px rgba(16, 8, 3, 0.30);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
}

/* ════════════════════════════════════════════════════════════════
 * ModalScreen — shared generic modal-card template.
 *
 * v449x (2026-05-21): single-card centered overlay that consumes the
 * canonical Moyako tile recipe (same surface tokens as `.results-card`
 * + `.difficulty-card`). One component handles Watch Video, Pause,
 * and future modal-style screens — 36 px global-standard buttons,
 * design-token padding/radius, optional cap-settings rail.
 *
 * Per Owner "that seems fine, can be shared generic template" —
 * replaces the bespoke `.moyako-rewarded-*` styling family.
 * ════════════════════════════════════════════════════════════════ */
.modal-screen-overlay {
  position: fixed; inset: 0; z-index: 10000;
  display: flex; align-items: center; justify-content: center;
  overflow-y: auto;
  padding: var(--space-3, 12px);
  -webkit-overflow-scrolling: touch;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0.75) 100%);
  backdrop-filter: blur(8px) saturate(120%);
  -webkit-backdrop-filter: blur(8px) saturate(120%);
  animation: modalScreenFadeIn 0.22s ease-out;
}
.modal-screen-overlay.is-leaving { animation: modalScreenFadeOut 0.18s ease-out forwards; }
@keyframes modalScreenFadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes modalScreenFadeOut { from { opacity: 1; } to { opacity: 0; } }

/* Outer card — same surface recipe as `.results-card` (see
 * moyako-layout.css §2099 canonical group). One source of truth. */
.modal-screen-card {
  position: relative;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-lg, 14px);
  padding: var(--space-4, 16px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3, 12px);
  max-width: 380px;
  width: 100%;
  flex-shrink: 0;
  text-align: center;
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.06) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.30) inset,
    0 24px 60px rgba(0, 0, 0, 0.55);
  animation: modalScreenScale 0.28s cubic-bezier(0.22, 1.2, 0.36, 1);
}

/* ── Classic Wooden — confirm / pause / reward modals (shared ModalScreen).
 *    The card defaulted to dark glass (rgba(255,255,255,0.05)) with no wooden
 *    skin, so it read as a modern Core overlay. Make it a PARCHMENT + brass
 *    card with espresso/brown text, a compact brass-framed avatar, and the
 *    wooden button system: danger (Exit) → muted burgundy + brass edge,
 *    primary (safe / Keep Going) → green felt + brass edge. Wooden-scoped;
 *    applies to every confirm/pause/reward modal (Owner exit-modal 7.6). ── */
[data-theme="wooden"] .modal-screen-card {
  background: linear-gradient(180deg, #F8EAC5 0%, #E5D2AC 100%) !important;
  border: 1px solid #B8893A !important;
  box-shadow: 0 16px 40px rgba(16, 8, 3, 0.45), inset 0 1px 0 rgba(255, 247, 224, 0.5) !important;
}
[data-theme="wooden"] .modal-screen-title { color: #2A1B12 !important; font-weight: 800 !important; }
[data-theme="wooden"] .modal-screen-body  { color: #4B3626 !important; opacity: 1 !important; }
[data-theme="wooden"] .modal-screen-icon {
  width: 48px !important;
  height: 48px !important;
  font-size: 26px !important;
  border: 2px solid #B8893A !important;
  box-shadow: 0 2px 6px rgba(16, 8, 3, 0.30) !important;
}
[data-theme="wooden"] .modal-screen-btn--danger {
  background: linear-gradient(180deg, #A94432 0%, #7F251D 100%) !important;
  border: 1px solid #D7AE54 !important;
  color: #FFF3E0 !important;
}
[data-theme="wooden"] .modal-screen-btn--primary {
  background: linear-gradient(180deg, #3D9A55 0%, #2F7D46 100%) !important;
  border: 1px solid #D7AE54 !important;
  color: #FFF3D0 !important;
}
/* v449af (2026-05-21): wide variant for IAP / subscription pricing
 * cards that carry plan toggle + benefits list + CTA + fine print.
 * Same surface tokens, wider max-width on desktop. */
.modal-screen-card--wide {
  max-width: 540px;
}

/* v449am (2026-05-21): when a .modal-screen-card lives inside a
 * .modal-screen-pane (Moyako Plus 2-card split), the pane handles
 * width via the canonical picker/results pane rules — drop the
 * 380 px cap so each child fills its column. Per Owner "same 2
 * container logic". */
.modal-screen-pane > .modal-screen-card {
  max-width: none;
  width: 100%;
  /* Match picker/results card padding + gap so the pane cards look
   * identical to those screens. Per Owner "as in picker and
   * game-end" — canonical sizing, not modal-compact. */
  padding: var(--space-4, 16px);
  gap: var(--space-3, 12px);
}
.modal-screen-pane {
  width: 100%;
  max-width: 880px;   /* desktop landscape cap matches picker max-width */
}

/* Brand card (left) — hero row with logo + panda side-by-side. Same
 * pattern as `.results-card--celebration`. Icons sized to match the
 * picker/results hero (52 px landscape, 72 px portrait/desktop). */
.modal-screen-card--brand {
  align-items: center;
  justify-content: center;
  text-align: center;
}
.modal-screen-hero-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3, 12px);
}
.modal-screen-pane > .modal-screen-card--brand .modal-screen-icon {
  width: 72px; height: 72px;
}
.modal-screen-panda img { padding: 4px; }   /* slightly tighter so panda fits the circle */

/* v449au (2026-05-21): Moyako+ brand mark stack — crown + wordmark +
 * PREMIUM pill. Replaces the earlier panda+logo hero + small "MOYAKO +"
 * tag per Owner reference image. App-icon style premium signaling. */
/* v449ay (2026-05-21): hero row inside MoyakoPlus brand card — crown
 * + panda on same baseline, equal box size, vertically centered.
 * Per Owner "crown and panda size and location not aligned" — both
 * now 64×64, flex center inside their boxes. */
.moyako-plus-hero-row {
  align-items: center;
  gap: var(--space-3, 12px);
}
.moyako-plus-crown {
  width: 64px;
  height: 64px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 48px;
  line-height: 1;
  filter:
    drop-shadow(0 0 8px rgba(251, 192, 45, 0.55))
    drop-shadow(0 2px 4px rgba(0, 0, 0, 0.35));
}
.moyako-plus-hero-row .modal-screen-panda {
  width: 64px;
  height: 64px;
}
/* v449az (2026-05-21): wordmark + premium pill scaled down per Owner
 * "smaller Moyako + and premium, too disturbing, be elegant". Reads
 * as a refined brand mark, not a loud banner. */
.moyako-plus-wordmark {
  font-size: 24px;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1;
  color: var(--text-primary, #FFFFFF);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.30);
  display: inline-flex;
  align-items: baseline;
}
.moyako-plus-wordmark__plus {
  color: #FBC02D;
  margin-left: 2px;
  font-weight: 800;
  text-shadow: 0 0 6px rgba(251, 192, 45, 0.40);
}
.moyako-plus-premium-pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 10px;
  background: rgba(251, 192, 45, 0.10);
  border: 1px solid rgba(251, 192, 45, 0.45);
  border-radius: 999px;
  color: #FBC02D;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.moyako-plus-premium-pill__star { font-size: 10px; line-height: 1; }
.moyako-plus-premium-pill__label { line-height: 1; }
[data-theme="light"] .moyako-plus-wordmark { color: #0F172A; text-shadow: none; }
[data-theme="light"] .moyako-plus-wordmark__plus { color: #C77800; text-shadow: 0 0 10px rgba(249, 168, 37, 0.45); }

/* Pricing card (right) — drops default centered alignment; the plans /
 * benefits / fine print read better when each takes full width. */
.modal-screen-card--pricing {
  align-items: stretch;
  text-align: left;
}
.modal-screen-card--pricing .modal-screen-fine { text-align: center; }
@keyframes modalScreenScale {
  from { transform: scale(0.92) translateY(8px); opacity: 0; }
  to   { transform: scale(1)    translateY(0);   opacity: 1; }
}

/* Light theme — mirror `.results-card`'s light variant. */
[data-theme="light"] .modal-screen-card {
  background: linear-gradient(180deg, #F2F5FB 0%, #D8DFEC 100%);
  border-color: rgba(15, 23, 42, 0.10);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.95),
    inset 0 -1px 0 rgba(15, 23, 42, 0.04),
    0 2px 6px rgba(15, 23, 42, 0.08),
    0 24px 60px rgba(15, 23, 42, 0.18);
  color: #0F172A;
}

/* Hero icon — circle (mascot) or square (action glyph like ⏸). */
.modal-screen-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  font-size: 40px;
  line-height: 1;
  background:
    radial-gradient(circle at 50% 35%, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0) 60%),
    linear-gradient(160deg, #2F3648 0%, #1E2535 100%);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow:
    0 8px 22px rgba(0, 0, 0, 0.30),
    0 1px 0 0 rgba(255, 255, 255, 0.08) inset;
  flex-shrink: 0;
}
.modal-screen-icon--circle { border-radius: 50%; }
.modal-screen-icon--square { border-radius: var(--radius-md, 10px); }
.modal-screen-icon img {
  display: block;
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 6px;
  box-sizing: border-box;
  border-radius: inherit;
}
[data-theme="light"] .modal-screen-icon {
  background:
    radial-gradient(circle at 50% 35%, rgba(0, 0, 0, 0.03) 0%, rgba(0, 0, 0, 0) 60%),
    linear-gradient(160deg, #FFFFFF 0%, #E8EAED 100%);
  border-color: #D1D5DB;
}

.modal-screen-title {
  margin: 0;
  font-size: 22px; font-weight: 800; letter-spacing: -0.01em;
  color: var(--text-primary, #ECEFF4);
  line-height: 1.2;
}
.modal-screen-body {
  margin: 0;
  font-size: 14px; line-height: 1.5;
  color: var(--text-secondary, rgba(236, 239, 244, 0.72));
  padding: 0 4px;
}
[data-theme="light"] .modal-screen-title { color: #0F172A; }
[data-theme="light"] .modal-screen-body  { color: #4A5568; }

/* Reward pill — green-tinted (matches results-pane score/top5 cells). */
.modal-screen-reward {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 14px;
  background: linear-gradient(160deg, rgba(76, 175, 80, 0.18) 0%, rgba(46, 125, 50, 0.18) 100%);
  border: 1px solid rgba(76, 175, 80, 0.40);
  border-radius: 999px;
  font-size: 13px; font-weight: 700;
  color: #A5D6A7;
}
.modal-screen-reward-icon {
  font-size: 16px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
}
[data-theme="light"] .modal-screen-reward { color: #1B5E20; }

/* Actions row — equal-share grid, 36 px global-standard button height. */
.modal-screen-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2, 8px);
  width: 100%;
  margin-top: var(--space-1, 4px);
}
.modal-screen-actions:has(.modal-screen-btn:nth-child(3)) {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.modal-screen-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 0 var(--space-2, 8px);
  min-height: 36px;
  height: 36px;
  box-sizing: border-box;
  border-radius: var(--radius-md, 10px);
  font-size: 13px; font-weight: 700;
  cursor: pointer;
  border: 1px solid transparent;
  transition: transform 0.12s ease-out, box-shadow 0.15s, background 0.15s;
}
.modal-screen-btn--secondary {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, rgba(0, 0, 0, 0) 60%),
    rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.12);
  color: var(--text-primary, rgba(236, 239, 244, 0.85));
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.06) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.2) inset;
}
.modal-screen-btn--primary {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 50%),
    linear-gradient(135deg, #66BB6A 0%, #388E3C 100%);
  color: #FFFFFF;
  border-color: rgba(0, 0, 0, 0.18);
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.30) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.18) inset,
    0 6px 14px rgba(76, 175, 80, 0.40);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
}
.modal-screen-btn--danger {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0) 50%),
    linear-gradient(135deg, #EF5350 0%, #C62828 100%);
  color: #FFFFFF;
  border-color: rgba(0, 0, 0, 0.18);
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.20) inset,
    0 6px 14px rgba(198, 40, 40, 0.35);
}
.modal-screen-btn:hover { transform: translateY(-1px); }
.modal-screen-btn:active { transform: translateY(0); }

[data-theme="light"] .modal-screen-btn--secondary {
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.02) 0%, rgba(0, 0, 0, 0) 50%), #FFFFFF;
  border-color: #D1D5DB;
  color: #4A5568;
}

/* Settings rail (sound + gear) — when card carries `.cap-settings-host`,
 * top-bar.js stamps the rail inside it. The base anchor rule at
 * moyako-layout.css §7107 sets `display: none` and only re-enables in
 * landscape phone; modals float on top of everything regardless of
 * viewport so we override here to keep the rail visible on the modal
 * card in ALL orientations. */
.modal-screen-card.cap-settings-host {
  position: relative;
  padding-top: 48px;
}
.modal-screen-card.cap-settings-host > .cap-settings-anchor {
  display: inline-flex !important;
  align-items: center;
  position: absolute;
  top: 10px;
  right: 12px;
  gap: 8px;
  margin-left: 0;
  z-index: 5;
}
.modal-screen-card.cap-settings-host > .cap-settings-anchor > button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 8px;
  color: var(--text-primary, #ECEFF4);
  cursor: pointer;
}
[data-theme="light"] .modal-screen-card.cap-settings-host > .cap-settings-anchor > button {
  background: rgba(15, 23, 42, 0.06);
  border-color: rgba(15, 23, 42, 0.12);
  color: #0F172A;
}

/* Custom content slot — caller-supplied html (textarea, sentiment
 * pad, store grid). v449aa (2026-05-21). */
.modal-screen-custom {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-2, 8px);
}

/* ── Sentiment gate — 2-up or 3-up emoji buttons.
 * v449ab (2026-05-21): 3-up variant for Later · Not really · Love it
 * order — rightmost tile is the positive CTA per Fitts's Law (right
 * thumb-zone is easiest reach for most users). */
.modal-screen-sentiment {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2, 8px);
}
.modal-screen-sentiment--3up {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.modal-screen-sentiment-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: var(--space-3, 12px) var(--space-2, 8px);
  border-radius: var(--radius-md, 10px);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.12);
  cursor: pointer;
  line-height: 1;
  transition: transform 0.12s, background 0.15s, border-color 0.15s;
}
.modal-screen-sentiment-btn .modal-screen-sentiment-emoji { font-size: 32px; line-height: 1; }
.modal-screen-sentiment-btn .modal-screen-sentiment-label {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-primary, #ECEFF4);
  letter-spacing: 0.01em;
  line-height: 1.2;
}
.modal-screen-sentiment-btn .modal-screen-sentiment-hint {
  font-size: 10px;
  font-weight: 500;
  color: var(--text-secondary, rgba(236, 239, 244, 0.55));
  letter-spacing: 0.02em;
  line-height: 1.1;
}
[data-theme="light"] .modal-screen-sentiment-btn .modal-screen-sentiment-label { color: #0F172A; }
[data-theme="light"] .modal-screen-sentiment-btn .modal-screen-sentiment-hint  { color: #6B7280; }
.modal-screen-sentiment-btn:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.20);
  transform: translateY(-1px);
}
.modal-screen-sentiment-btn--positive {
  background: linear-gradient(160deg, rgba(76, 175, 80, 0.18) 0%, rgba(46, 125, 50, 0.18) 100%);
  border-color: rgba(76, 175, 80, 0.40);
}
.modal-screen-sentiment-btn--positive:hover {
  background: linear-gradient(160deg, rgba(76, 175, 80, 0.28) 0%, rgba(46, 125, 50, 0.28) 100%);
  border-color: rgba(76, 175, 80, 0.60);
}
[data-theme="light"] .modal-screen-sentiment-btn {
  background: #FFFFFF;
  border-color: #D1D5DB;
  color: #0F172A;
}
[data-theme="light"] .modal-screen-sentiment-btn .modal-screen-sentiment-label { color: #4A5568; }

/* ── Store picker — 2×2 grid of store badges. */
.modal-screen-stores {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2, 8px);
}
.modal-screen-store {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  padding: 10px 12px;
  border-radius: var(--radius-md, 10px);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--text-primary, #ECEFF4);
  cursor: pointer;
  font-size: 13px;
  font-weight: 700;
  text-align: left;
  min-height: 44px;
  transition: transform 0.12s, background 0.15s, border-color 0.15s;
}
.modal-screen-store-icon { font-size: 18px; line-height: 1; flex-shrink: 0; }
.modal-screen-store-label { line-height: 1.2; }
.modal-screen-store:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.20);
  transform: translateY(-1px);
}
[data-theme="light"] .modal-screen-store {
  background: #FFFFFF;
  border-color: #D1D5DB;
  color: #0F172A;
}

/* ── Feedback textarea. */
.modal-screen-feedback {
  width: 100%;
  min-height: 96px;
  padding: var(--space-2, 8px) var(--space-3, 12px);
  border-radius: var(--radius-md, 10px);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--text-primary, #ECEFF4);
  font-family: inherit;
  font-size: 13px;
  line-height: 1.5;
  box-sizing: border-box;
  resize: vertical;
}
.modal-screen-feedback::placeholder {
  color: var(--text-secondary, rgba(236, 239, 244, 0.50));
}
.modal-screen-feedback:focus {
  outline: none;
  border-color: rgba(76, 175, 80, 0.50);
  background: rgba(255, 255, 255, 0.06);
}
[data-theme="light"] .modal-screen-feedback {
  background: #FFFFFF;
  border-color: #D1D5DB;
  color: #0F172A;
}

/* ── IAP: benefits bullet list (RemoveAds + MoyakoPlus). */
.modal-screen-benefits {
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-2, 8px);
}
.modal-screen-benefit {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.10);
  border-radius: var(--radius-md, 10px);
  font-size: 13px;
  color: var(--text-primary, #ECEFF4);
  text-align: left;
}
.modal-screen-benefit-icon { font-size: 16px; line-height: 1; flex-shrink: 0; }
.modal-screen-benefit-text { line-height: 1.3; }
[data-theme="light"] .modal-screen-benefit {
  background: #FFFFFF;
  border-color: #D1D5DB;
  color: #0F172A;
}

/* ── MoyakoPlus: plan toggle (2 tiles, annual default). */
.modal-screen-plans {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2, 8px);
  width: 100%;
}
.modal-screen-plan {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 14px 12px;
  border-radius: var(--radius-md, 10px);
  background: rgba(255, 255, 255, 0.04);
  border: 2px solid rgba(255, 255, 255, 0.12);
  cursor: pointer;
  color: var(--text-primary, #ECEFF4);
  transition: transform 0.12s, background 0.15s, border-color 0.15s;
}
.modal-screen-plan:hover {
  background: rgba(255, 255, 255, 0.07);
  border-color: rgba(255, 255, 255, 0.22);
}
.modal-screen-plan.is-selected {
  background: linear-gradient(160deg, rgba(76, 175, 80, 0.18) 0%, rgba(46, 125, 50, 0.18) 100%);
  border-color: rgba(76, 175, 80, 0.65);
  box-shadow: 0 6px 14px rgba(76, 175, 80, 0.20);
}
.modal-screen-plan-badge {
  position: absolute;
  top: -8px;
  right: 8px;
  padding: 2px 8px;
  background: linear-gradient(135deg, #66BB6A 0%, #388E3C 100%);
  color: #FFFFFF;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  border-radius: 999px;
  box-shadow: 0 2px 6px rgba(76, 175, 80, 0.40);
}
.modal-screen-plan-label { font-size: 12px; font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase; color: var(--text-secondary, rgba(236, 239, 244, 0.65)); }
.modal-screen-plan-price { font-size: 22px; font-weight: 800; line-height: 1.1; }
.modal-screen-plan-cycle { font-size: 11px; color: var(--text-secondary, rgba(236, 239, 244, 0.65)); }
[data-theme="light"] .modal-screen-plan {
  background: #FFFFFF;
  border-color: #D1D5DB;
  color: #0F172A;
}
[data-theme="light"] .modal-screen-plan.is-selected {
  background: linear-gradient(160deg, rgba(76, 175, 80, 0.12) 0%, rgba(46, 125, 50, 0.10) 100%);
  border-color: #4CAF50;
}
[data-theme="light"] .modal-screen-plan-label,
[data-theme="light"] .modal-screen-plan-cycle { color: #6B7280; }

/* ── Fine print (auto-renewal disclosure, Apple-required). */
.modal-screen-fine {
  margin: 0;
  font-size: 10px;
  line-height: 1.4;
  color: var(--text-secondary, rgba(236, 239, 244, 0.55));
  text-align: center;
  padding: 0 var(--space-2, 8px);
}
[data-theme="light"] .modal-screen-fine { color: #6B7280; }

/* ── Settings Plus row — Sprint A7i entry surface inside the
 * SettingsModal panel. v449aq (2026-05-21): elegant treatment per
 * Owner "settings design too odd, could be something elegant". Dark
 * canonical surface + small gold star icon + gold "Upgrade" pill on
 * the right. Reads as a premium SaaS upsell (Notion / Linear) not a
 * loud sales banner. */
.sm-plus-row {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 16px;                 /* v451: taller / airier product card */
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(251, 192, 45, 0.25);
  border-radius: var(--radius-lg, 14px);
  color: var(--text-primary, #ECEFF4);
  cursor: pointer;
  text-align: left;
  font: inherit;
  transition: background 0.15s, border-color 0.15s, transform 0.12s;
}
.sm-plus-row:hover {
  background: rgba(251, 192, 45, 0.06);
  border-color: rgba(251, 192, 45, 0.45);
  transform: translateY(-1px);
}
/* v449az (2026-05-21): settings tile redesigned per Owner reference —
 * sparkle decorative icon (not full game icon), inline benefits with
 * bullet separator (single line, scannable). Refined elegant look. */
.sm-plus-row__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-size: 26px;
  line-height: 1;
  width: 36px;
  height: 36px;
  filter: drop-shadow(0 0 6px rgba(251, 192, 45, 0.45));
}
.sm-plus-row__body { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
/* v451: title row — name + a small type label (Subscription / Lifetime). */
.sm-plus-row__titlerow { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.sm-plus-row__type {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-tertiary, #7C8AA5);
}
[data-theme="light"] .sm-plus-row__type { color: #94A3B8; }
/* v449bf (2026-05-21): diamond-polished gradient on the Moyako+
 * wordmark per Owner "could be replaced with diamond polished
 * Moyako +". Silver-to-white shimmer on "Moyako" + gold-to-light-gold
 * shimmer on "+", subtle text shadow for depth. */
.sm-plus-row__title {
  font-size: 15px;
  font-weight: 800;
  letter-spacing: 0.01em;
  background: linear-gradient(180deg, #FFFFFF 0%, #E5EAF2 50%, #B8C0CC 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35));
}
.sm-plus-row__plus {
  display: inline-block;
  font-weight: 900;
  font-size: 17px;
  line-height: 1;
  margin-left: 2px;
  background: linear-gradient(180deg, #FFE082 0%, #FBC02D 50%, #F9A825 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  filter: drop-shadow(0 0 4px rgba(251, 192, 45, 0.55)) drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35));
}
[data-theme="light"] .sm-plus-row__title {
  background: linear-gradient(180deg, #0F172A 0%, #1F2937 50%, #4A5568 100%);
  -webkit-background-clip: text;
  background-clip: text;
  filter: none;
}
[data-theme="light"] .sm-plus-row__plus {
  background: linear-gradient(180deg, #F9A825 0%, #C77800 100%);
  -webkit-background-clip: text;
  background-clip: text;
  filter: drop-shadow(0 0 3px rgba(249, 168, 37, 0.40));
}
.sm-plus-row__benefits-inline {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-secondary, rgba(236, 239, 244, 0.60));
  line-height: 1.3;
}
.sm-plus-row__dot {
  color: #FBC02D;
  margin: 0 4px;
  font-weight: 700;
}
/* v449bc (2026-05-21): Ad-Free Premium row uses moyako master icon
 * inside a rounded tile (matches Owner reference). Lifetime chip is
 * a small gold-outlined pill next to the subtitle. */
.sm-plus-row__icon--adfree {
  width: 36px;
  height: 36px;
  font-size: 0;
  background: linear-gradient(160deg, #2F3648 0%, #1E2535 100%);
  border: 1px solid rgba(251, 192, 45, 0.30);
  border-radius: var(--radius-md, 10px);
  padding: 3px;
  filter: none;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.30);
}
.sm-plus-row__icon--adfree img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 7px;
}
.sm-plus-row__lifetime-chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  margin-left: 6px;
  background: transparent;
  border: 1px solid rgba(251, 192, 45, 0.45);
  border-radius: 999px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #FBC02D;
}
body.has-removeads .sm-adfree-group,
body.has-plus     .sm-adfree-group { display: none; }   /* hide when already ad-free */

/* v449be (2026-05-21): per-variant IAP filter. Each game's body sets
 * `data-iap-offers="plus,adfree"` (or any subset). Tiles marked with
 * `data-iap-only="X"` only render when X is in that list. Default
 * (no body attr) → show all tiles (template / dev pages). */
body[data-iap-offers]:not([data-iap-offers~="plus"])   [data-iap-only="plus"],
body[data-iap-offers]:not([data-iap-offers~="adfree"]) [data-iap-only="adfree"] {
  display: none;
}
[data-theme="light"] .sm-plus-row__benefits-inline { color: #6B7280; }
[data-theme="light"] .sm-plus-row__lifetime-chip { color: #C77800; border-color: rgba(249, 168, 37, 0.55); }
/* v451c: compact single-row Moyako+ promo (Owner 2026-05-27 "single row") —
   tighter vertical padding than the 16px product card; one line: icon · Moyako+ ·
   Subscribe. Only the Moyako+ row carries --compact; the ad-free card is unchanged. */
.sm-plus-row--compact { padding: 10px 16px; }
.sm-plus-row__cta {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  /* Title Case, not UPPERCASE (Owner: "only initial capital at Subscribe" +
     project Title-Case rule) — applies to Subscribe + Upgrade CTAs alike. */
  text-transform: none;
  padding: 6px 12px;
  border-radius: 999px;
  background: linear-gradient(135deg, #FBC02D 0%, #F9A825 100%);
  color: #1B1F2A;
  box-shadow: 0 2px 6px rgba(251, 192, 45, 0.30);
}
[data-theme="light"] .sm-plus-row {
  background: #FFFFFF;
  border-color: rgba(251, 192, 45, 0.40);
  color: #0F172A;
}
[data-theme="light"] .sm-plus-row__sub { color: #6B7280; }
body.has-plus .sm-plus-group { display: none; }   /* hide upsell when active */

/* ── Subscriber status — "★ Moyako+" badge under the settings/sound
 * rail when the user has Plus active. Per Owner "this could be a
 * badge for the subscribers ... can that be below the settings/sound
 * icon". Renders below the .cap-settings-anchor + below the topbar
 * sound/gear icons.
 *
 * The badge is injected by top-bar.js when body.has-plus is set; this
 * rule positions it. */
body.has-plus .topbar {
  position: relative;
}
.moyako-plus-subscriber-badge {
  display: none;
}
body.has-plus .moyako-plus-subscriber-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 1px 6px 1px 4px;
  background: linear-gradient(180deg, #2A3142 0%, #1C2233 100%);
  color: #FFFFFF;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.02em;
  border-radius: 999px;
  border: 1px solid rgba(251, 192, 45, 0.50);
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.30),
    0 0 4px rgba(251, 192, 45, 0.15);
  pointer-events: none;
  white-space: nowrap;
}
.moyako-plus-subscriber-badge .moyako-plus-subscriber-badge__star {
  color: #FBC02D;
  font-size: 8px;
  line-height: 1;
}
/* v449bb (2026-05-21): tiny per Owner "tiny to fit in there" — badge
 * sits just below the sound/gear icons in the topbar (right-aligned)
 * without overflowing into the page area. */
body.has-plus .topbar .moyako-plus-subscriber-badge {
  position: absolute;
  top: calc(100% - 2px);
  right: 12px;
  z-index: 6;
}
/* Inside a .cap-settings-host card (results-card, picker difficulty-
 * card), drop the badge under the icons rail. v442: the host needs its
 * own positioning context, or the absolutely-positioned badge anchors to
 * a wrong ancestor and floats over the SCORE cell (Owner: "Moyako+
 * floating around"). Mirrors the body.has-plus .topbar { position:relative }
 * fix above. */
body.has-plus .cap-settings-host { position: relative; }
body.has-plus .cap-settings-host .moyako-plus-subscriber-badge {
  position: absolute;
  top: 48px;
  right: 12px;
  z-index: 6;
}

/* ── PlusBadge — inline "★ Moyako+" pill for results-score + picker.
 * v449av (2026-05-21): per Owner reference image — dark navy
 * background, gold star, white "Moyako+" text (mixed case, not caps).
 * Reads as a premium game-style badge (gaming app convention). */
.moyako-plus-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 12px 4px 8px;
  background: linear-gradient(180deg, #2A3142 0%, #1C2233 100%);
  color: #FFFFFF;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.01em;
  border-radius: 999px;
  border: 1px solid rgba(251, 192, 45, 0.45);
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 2px 6px rgba(0, 0, 0, 0.35),
    0 0 8px rgba(251, 192, 45, 0.18);
  transition: border-color 0.15s, transform 0.12s, box-shadow 0.15s;
}
.moyako-plus-badge:hover {
  border-color: rgba(251, 192, 45, 0.75);
  transform: translateY(-1px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    0 4px 10px rgba(0, 0, 0, 0.40),
    0 0 14px rgba(251, 192, 45, 0.30);
}
.moyako-plus-badge-icon {
  font-size: 12px;
  line-height: 1;
  color: #FBC02D;
  filter: drop-shadow(0 0 4px rgba(251, 192, 45, 0.60));
}
[data-theme="light"] .moyako-plus-badge {
  /* Keep the dark navy pill in light theme too — it's a brand mark,
   * not a surface that should adapt. */
  background: linear-gradient(180deg, #2A3142 0%, #1C2233 100%);
  color: #FFFFFF;
  border-color: rgba(249, 168, 37, 0.55);
}

/* Landscape phone — compact mode so 6-button-style flows fit the
 * ~360–400 px tall viewport. v449z (2026-05-21): max-width matches
 * portrait/desktop (380 px) per Owner "don't widen that match" —
 * card stays the same shape across viewports, only inner content
 * compacts. */
@media (orientation: landscape) and (max-height: 500px) {
  .modal-screen-card {
    padding: var(--space-3, 12px);
    gap: var(--space-2, 8px);
  }
  .modal-screen-icon { width: 52px; height: 52px; font-size: 28px; }
  .modal-screen-title { font-size: 18px; }
  .modal-screen-body  { font-size: 12px; line-height: 1.4; }
  .modal-screen-reward { padding: 4px 10px; font-size: 12px; }
  .modal-screen-card.cap-settings-host { padding-top: 44px; }
  /* RateUs sub-elements compact too. */
  .modal-screen-sentiment-btn { padding: 6px 4px; gap: 2px; }
  .modal-screen-sentiment-btn .modal-screen-sentiment-emoji { font-size: 22px; }
  .modal-screen-sentiment-btn .modal-screen-sentiment-label { font-size: 11px; }
  .modal-screen-sentiment-btn .modal-screen-sentiment-hint  { font-size: 9px; }
  .modal-screen-store { min-height: 36px; padding: 6px 10px; font-size: 12px; }
  .modal-screen-store-icon { font-size: 16px; }
  .modal-screen-feedback { min-height: 60px; font-size: 12px; }
  /* IAP elements compact for landscape phone. v449ak: benefits flow
   * into 2-col grid so the card fits the ~375 px viewport without
   * scrolling. Hero icon + body drop a notch too.
   * v449an (2026-05-21): pane variant of MoyakoPlus uses picker /
   * results scale — only the overall benefit/plan sizing compacts. */
  .modal-screen-benefits { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
  .modal-screen-benefit { padding: 5px 8px; font-size: 11px; gap: 6px; }
  .modal-screen-benefit-icon { font-size: 13px; }
  .modal-screen-plan { padding: 8px 6px; }
  .modal-screen-plan-label { font-size: 10px; }
  .modal-screen-plan-price { font-size: 16px; }
  .modal-screen-plan-cycle { font-size: 9px; }
  .modal-screen-plan-badge { font-size: 9px; padding: 1px 6px; top: -7px; }
  .modal-screen-fine { font-size: 9px; padding: 0; }
  /* Pane brand card — icons match picker/results landscape (52 px). */
  .modal-screen-pane > .modal-screen-card--brand .modal-screen-icon {
    width: 52px; height: 52px;
  }
  .modal-screen-pane > .modal-screen-card { padding: var(--space-3, 12px); gap: var(--space-2, 8px); }
}

/* Toast — also tile-styled (pill with inset highlight + brand halo). */
.moyako-toast {
  position: fixed; left: 50%; bottom: 28px; transform: translateX(-50%);
  z-index: 10001;
  display: inline-flex; align-items: center; gap: 8px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, rgba(0, 0, 0, 0) 50%),
    linear-gradient(165deg, #2F3648 0%, #1C2233 100%);
  color: #ECEFF4;
  padding: 11px 20px; border-radius: 999px;
  font-size: 13px; font-weight: 600;
  border: 1px solid rgba(255, 255, 255, 0.10);
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.06) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.20) inset,
    0 12px 28px rgba(0, 0, 0, 0.45);
  animation: moyakoToastIn 0.26s cubic-bezier(0.22, 1.2, 0.36, 1);
  max-width: calc(100vw - 40px);
}
.moyako-toast.is-out { animation: moyakoToastOut 0.25s ease-out forwards; }
@keyframes moyakoToastIn  { from { transform: translate(-50%, 12px); opacity: 0; } to { transform: translate(-50%, 0); opacity: 1; } }
@keyframes moyakoToastOut { from { opacity: 1; } to { transform: translate(-50%, 12px); opacity: 0; } }
[data-theme="light"] .moyako-toast {
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.02) 0%, rgba(0, 0, 0, 0) 50%),
    #FFFFFF;
  color: #0F172A;
  border-color: #D1D5DB;
  box-shadow:
    0 1px 0 0 rgba(255, 255, 255, 0.6) inset,
    0 -1px 0 0 rgba(0, 0, 0, 0.04) inset,
    0 12px 28px rgba(0, 0, 0, 0.18);
}
/* v309 — checkmark on selected tier. Pseudo-element so we don't need
 * to rewire button content. ::after positions top-right of the button.
 * v449 (2026-05-20): default color reuses currentColor so the per-tier
 * overrides below tint the ✓ to match the border + dot. Avoids the
 * green checkmark sitting inside an orange-bordered Medium pill. */
[data-moyako-v2] .difficulty.is-selected::after {
  content: '✓';
  position: absolute;
  top: 6px;
  right: 8px;
  color: currentColor;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
}
[data-moyako-v2] .difficulty.is-selected[data-tier="beginner"]::after { color: #A5D6A7; }
[data-moyako-v2] .difficulty.is-selected[data-tier="easy"]::after     { color: #81C784; }
[data-moyako-v2] .difficulty.is-selected[data-tier="medium"]::after   { color: #FFB74D; }
[data-moyako-v2] .difficulty.is-selected[data-tier="hard"]::after     { color: #EF5350; }
[data-moyako-v2] .difficulty.is-selected[data-tier="expert"]::after   { color: #9C27B0; }
[data-moyako-v2] .difficulty.is-selected[data-tier="auto"]::after     { color: #29B6F6; }
[data-moyako-v2] .difficulty { position: relative; }
[data-moyako-v2] .difficulty-emoji { font-size: 20px; line-height: 1; }

/* v304 (2026-05-11): difficulty dot indicator.
 * Replaces tier emojis (🐣 🙂 🧠 🔥 🏆 🎯) with colored dots that match
 * the .difficulty-badge palette used on game-tile badges (games.html).
 * Same visual language picker-tier ↔ game-card-badge for clarity.
 *
 * Beginner / Easy / Medium / Hard / Expert / Auto get distinct hues.
 * Existing 3 badge colors extended with: Beginner (lighter green),
 * Expert (purple), Auto (cyan).
 */
[data-moyako-v2] .difficulty-dot {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  flex: 0 0 12px;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.08);
}
[data-moyako-v2] [data-tier="beginner"] .difficulty-dot { background: #A5D6A7; }
[data-moyako-v2] [data-tier="easy"]     .difficulty-dot { background: #81C784; }
[data-moyako-v2] [data-tier="medium"]   .difficulty-dot { background: #FFB74D; }
[data-moyako-v2] [data-tier="hard"]     .difficulty-dot { background: #EF5350; }
[data-moyako-v2] [data-tier="expert"]   .difficulty-dot { background: #9C27B0; }
[data-moyako-v2] [data-tier="auto"]     .difficulty-dot { background: #29B6F6; }
[data-theme="light"] [data-moyako-v2] .difficulty-dot {
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.08);
}
[data-moyako-v2] .difficulty-label { flex: 1; font-weight: 500; }
[data-moyako-v2] .difficulty-hint {
  font-size: var(--font-size-small); color: var(--text-tertiary);
}
[data-moyako-v2] .difficulty.is-selected .difficulty-hint::before {
  content: '✓ '; color: var(--success); font-weight: 700;
}

/* ============================================================
 * Solid-3D inputs (v171, 2026-05-08, DESIGN-RULES §3.18)
 * ------------------------------------------------------------
 * Form inputs use the same tile aesthetic as containers — surface
 * bg with INSET shadow for press-in depth, theme-aware border, and
 * a smooth focus highlight. Beats per-page input rules via the
 * [data-moyako-v2] selector specificity.
 * Per user 2026-05-08: "input fields also solid-3d style add to
 * the design rules as well".
 * ============================================================ */
[data-moyako-v2] input[type="text"],
[data-moyako-v2] input[type="email"],
[data-moyako-v2] input[type="password"],
[data-moyako-v2] input[type="number"],
[data-moyako-v2] input[type="search"],
[data-moyako-v2] input[type="tel"],
[data-moyako-v2] input[type="url"],
[data-moyako-v2] textarea,
[data-moyako-v2] select,
[data-moyako-v2] .form-container input[type="text"],
[data-moyako-v2] .form-container input[type="email"],
[data-moyako-v2] .form-container input[type="password"],
[data-moyako-v2] .form-container select {
  background: var(--bg-surface, var(--card-bg)) !important;
  border: 1px solid var(--border, var(--border-color)) !important;
  border-radius: 10px !important;
  color: var(--text-primary, var(--text-light)) !important;
  padding: 12px 14px !important;
  font-size: 15px !important;
  font-family: inherit !important;
  width: 100% !important;
  box-sizing: border-box !important;
  /* 3D inset — recessed top edge (dark), bright bottom edge (raised
   * border highlight), soft outer shadow for surface depth. */
  box-shadow:
    inset 0 1px 2px rgba(0, 0, 0, 0.30),
    inset 0 -1px 0 rgba(255, 255, 255, 0.04),
    0 1px 0 rgba(255, 255, 255, 0.03) !important;
  transition: border-color 0.15s ease, box-shadow 0.15s ease !important;
}

[data-moyako-v2] input:focus,
[data-moyako-v2] textarea:focus,
[data-moyako-v2] select:focus {
  outline: none !important;
  border-color: var(--action, #4CAF50) !important;
  box-shadow:
    inset 0 1px 2px rgba(0, 0, 0, 0.30),
    inset 0 -1px 0 rgba(255, 255, 255, 0.06),
    0 0 0 3px rgba(76, 175, 80, 0.18) !important;
}

[data-moyako-v2] input::placeholder,
[data-moyako-v2] textarea::placeholder {
  color: var(--text-muted, var(--text-tertiary));
  opacity: 0.75;
}

/* Light theme — silver-gradient surface, dark text, soft top
 * highlight for the raised look. */
html[data-theme="light"] [data-moyako-v2] input[type="text"],
html[data-theme="light"] [data-moyako-v2] input[type="email"],
html[data-theme="light"] [data-moyako-v2] input[type="password"],
html[data-theme="light"] [data-moyako-v2] input[type="number"],
html[data-theme="light"] [data-moyako-v2] input[type="search"],
html[data-theme="light"] [data-moyako-v2] input[type="tel"],
html[data-theme="light"] [data-moyako-v2] input[type="url"],
html[data-theme="light"] [data-moyako-v2] textarea,
html[data-theme="light"] [data-moyako-v2] select {
  background: linear-gradient(180deg, #FFFFFF 0%, #F2F5FB 100%) !important;
  border-color: rgba(15, 23, 42, 0.18) !important;
  color: #0F172A !important;
  box-shadow:
    inset 0 1px 2px rgba(15, 23, 42, 0.08),
    inset 0 -1px 0 rgba(255, 255, 255, 0.95),
    0 1px 0 rgba(255, 255, 255, 0.50) !important;
}

/* ── Ad slots: hide when no ad has loaded into .moyako-ad-slot-content.
   Prevents the bare "Ad" label from showing in an empty slot.
   :has() coverage ~94% (Chrome 105+, FF 103+, Safari 15.4+). ── */
.moyako-ad-slot:not(:has(.moyako-ad-slot-content > *)) {
  display: none !important;
}
