/* ==========================================================================
   EL3AB core: Night Arcade, a modern mobile-game UI kit.
   A quiet ink stage; flat ink tiles for lists; white game cards with a lip;
   flat colour buttons with a lip the finger pushes down; a floating HUD and
   a floating dock. See tokens.css.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* Hidden means gone, whatever display a class gives the element. */
[hidden] { display: none !important; }

html, body {
  width: 100%; height: 100%;
  overflow: hidden;
  background-color: var(--stage);
  background-image: var(--stage-pattern);
  background-size: 56px 56px;
  color: var(--text-primary);
  font-family: var(--font-ar);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
  touch-action: manipulation;
  caret-color: var(--gold);
  font-synthesis: none;
}

/* Two soft pools of light: violet from above, a warm one low in the corner. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(90% 46% at 50% -6%, var(--stage-glow), transparent 72%),
    radial-gradient(70% 38% at 100% 104%, var(--stage-glow-2), transparent 70%);
}

:root { --dir: 1; }
[dir="rtl"] { --dir: -1; }

body, input, button, textarea, select { font-variant-numeric: tabular-nums; }
input, button, textarea, select { font-family: inherit; }
button { color: inherit; }

::selection { background: var(--btn-purple-2); color: #fff; }

:focus { outline: none; }
:focus-visible {
  outline: 3px solid rgba(255, 255, 255, 0.85);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

/* Line icons (utility glyphs) and sticker icons (game art). */
.i { display: inline-block; flex-shrink: 0; vertical-align: middle; }
[dir="rtl"] .i-dir { transform: scaleX(-1); }
.i-coin, .i-crown { color: var(--gold); }
.i-gem { color: var(--purple); }
.i-online { color: var(--win); }
.i-offline { color: var(--text-dim); }
.gi { display: inline-block; flex-shrink: 0; object-fit: contain; vertical-align: middle; pointer-events: none; -webkit-user-drag: none; }

/* Display type: the voice of the game. */
.display, .screen-title, .section-title, .btn, .chip, .tab-item, .hud-name, .hud-stat, .empty-title, .ribbon {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: 0;
}

#game {
  position: fixed;
  inset: 0;
  z-index: 1;
  overflow: hidden;
}

/* The container never changes size: each scene sets its own box (below), so
   a game starting or ending never resizes the screen that is leaving. */
#scene-container {
  position: absolute;
  inset: 0;
}

#hud:empty { display: none; }

/* The first thing on screen, before any script: a layer of its own over the
   HUD and the dock. scene.js lifts it once the first scene has painted with
   its fonts in, so the scene, the HUD and the dock appear together under it. */
#boot-splash {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  background-color: var(--stage);
  background-image:
    radial-gradient(90% 46% at 50% -6%, var(--stage-glow), transparent 72%),
    radial-gradient(70% 38% at 100% 104%, var(--stage-glow-2), transparent 70%),
    var(--stage-pattern);
  background-size: 100% 100%, 100% 100%, 56px 56px;
  background-repeat: no-repeat, no-repeat, repeat;
  pointer-events: none;
}
#boot-splash .boot-crown { width: 132px; height: 132px; object-fit: contain; }
#boot-splash .boot-bar { width: 200px; height: 12px; border-radius: 6px; background: var(--back-700); overflow: hidden; }
#boot-splash .boot-bar > span {
  display: block; height: 100%; border-radius: inherit; background: var(--gold);
  transform-origin: 0 50%;
  animation: bootFill 2.4s var(--ease-out) both;
}
[dir="rtl"] #boot-splash .boot-bar > span { transform-origin: 100% 50%; }
@keyframes bootFill { from { transform: scaleX(0.08); } to { transform: scaleX(0.88); } }

/* ── The HUD: floating capsules, no bar ─────────────────────────────────── */
#hud {
  position: fixed;
  top: 0; inset-inline: 0;
  height: calc(var(--hud-height) + var(--safe-top));
  padding-top: var(--safe-top);
  padding-inline: var(--hud-padding-x);
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 8px;
  background: linear-gradient(180deg, var(--stage) 55%, rgba(20, 18, 26, 0));
  /* On the scene's own clock: scene.js sets these for each transition. */
  transition: transform var(--chrome-dur, var(--dur-normal)) var(--chrome-ease, var(--ease-out)) var(--chrome-delay, 0ms);
}
#hud.hidden { transform: translateY(calc(-100% - 12px)); }

/* How far into the level the player is (0..1), set by hud.js on the profile
   key: the bar and the badge's ring both read it, and it tweens, so the ring
   fills smoothly. On a level up hud.js fills it to 1, drops it to 0 with
   .xp-snap (no tween) and fills it again: it never runs backwards. */
@property --hud-xp { syntax: '<number>'; inherits: true; initial-value: 0; }
.hud-profile {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  position: relative;
  min-height: 48px;
  border: none;
  background: none;
  color: inherit;
  padding: 0;
  text-align: start;
  transition: --hud-xp 600ms var(--ease-out);
}
.hud-profile.xp-snap { transition: none; }
.hud-avatar {
  position: relative;
  width: var(--hud-avatar-size);
  height: var(--hud-avatar-size);
  border-radius: 15px;
  background: var(--btn-purple-2);
  box-shadow: 0 3px 0 var(--btn-purple-edge);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: #fff;
}
.hud-avatar > :first-child { border-radius: inherit; overflow: hidden; }
.hud-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
.hud-level-badge {
  position: absolute;
  bottom: -7px;
  inset-inline-end: -7px;
  min-width: 24px; height: 24px;
  padding: 0 5px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 12px;
  background: var(--gold);
  box-shadow: 0 0 0 3px var(--stage);
  color: var(--gold-ink);
  font-family: var(--font-display);
  font-size: 13px;
  line-height: 1;
  z-index: 2;
}
.hud-who { display: flex; flex-direction: column; line-height: 1.1; min-width: 0; }
.hud-name {
  font-size: 15px;
  color: var(--text-primary);
  max-width: 26vw;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hud-level { font-size: 12px; font-weight: 700; color: var(--gold-soft); }
.hud-xp { display: block; width: 68px; height: 6px; margin-top: 5px; border-radius: 3px; background: var(--back-600); overflow: hidden; }
.hud-xp > span { display: block; height: 100%; width: 100%; border-radius: inherit; background: var(--sky); transform: scaleX(var(--hud-xp, 0)); transform-origin: 0 50%; }
[dir="rtl"] .hud-xp > span { transform-origin: 100% 50%; }

.hud-stats {
  display: flex;
  align-items: center;
  gap: var(--hud-stat-gap);
  margin-inline-start: auto;
}

/* The purse: an ink capsule, the coin at its start, a green plus at its end. */
.hud-stat {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 36px;
  min-width: 74px;
  padding-inline: 4px 32px;
  border-radius: 18px;
  background: var(--back-700);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 3px 0 var(--back-950);
  font-size: var(--hud-stat-font);
  color: #fff;
  cursor: pointer;
  border: none;
}
.hud-stat .icon {
  display: flex;
  margin-inline-start: -2px;
}
.hud-stat .icon .gi { width: 28px; height: 28px; }
.hud-stat .hud-plus {
  position: absolute;
  inset-inline-end: 4px;
  top: 50%;
  transform: translateY(-50%);
  width: 26px; height: 26px;
  border-radius: 13px;
  background: var(--btn-green-2);
  box-shadow: 0 2px 0 var(--btn-green-edge);
  display: flex; align-items: center; justify-content: center;
  color: #fff;
}
.hud-stat.is-gaining { animation: purseGain 560ms var(--ease-pop); }
.hud-stat.is-gaining .icon { animation: coinSpin 600ms var(--ease-out); }
@keyframes purseGain { 0% { scale: 1; } 30% { scale: 1.16; } 100% { scale: 1; } }
@keyframes coinSpin { from { transform: rotateY(0); } to { transform: rotateY(360deg); } }

.hud-actions { display: flex; align-items: center; gap: 6px; }

/* Phones: the purses keep their plus keys; the avatar with its level badge
   stands for the player and the name moves to the profile. The badge is the
   XP meter: a sky ring fills around it from the top, toward the reading end,
   a white star riding its head. */
@media (max-width: 400px) {
  #hud { gap: 6px; padding-inline: 10px; }
  .hud-who { display: none; }
  .hud-stat { min-width: 0; padding-inline-end: 30px; }
  .hud-stat .icon .gi { width: 24px; height: 24px; }
  .hud-level-badge {
    min-width: 30px; height: 30px;
    padding: 0 2px;
    bottom: -10px;
    inset-inline-end: -10px;
    border: 3px solid transparent;
    border-radius: 15px;
    background:
      linear-gradient(var(--gold), var(--gold)) padding-box,
      conic-gradient(var(--sky) calc(var(--hud-xp) * 1turn), var(--back-600) 0) border-box;
    box-shadow: 0 0 0 2px var(--stage);
  }
  [dir="rtl"] .hud-level-badge {
    background:
      linear-gradient(var(--gold), var(--gold)) padding-box,
      conic-gradient(var(--back-600) calc((1 - var(--hud-xp)) * 1turn), var(--sky) 0) border-box;
  }
  /* The ring's head: on the ring line at the top, turned about the badge's middle. */
  .hud-level-badge::after {
    content: '';
    position: absolute;
    top: -6.5px;
    inset-inline-start: 50%;
    width: 10px; height: 10px;
    margin-inline-start: -5px;
    transform-origin: 50% 18.5px;
    rotate: calc(var(--hud-xp) * var(--dir) * 1turn);
    background: var(--card);
    clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%);
    pointer-events: none;
  }
}

.hud-btn {
  width: var(--hud-btn-size);
  height: var(--hud-btn-size);
  border-radius: 15px;
  background: var(--back-700);
  border: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 3px 0 var(--back-950);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  position: relative;
  color: var(--text-primary);
  padding: 0;
}
.hud-btn.is-pressed { translate: 0 2px; box-shadow: 0 1px 0 var(--back-950); }

.hud-badge {
  position: absolute;
  top: -6px;
  inset-inline-end: -6px;
  min-width: var(--hud-badge-size);
  height: var(--hud-badge-size);
  line-height: var(--hud-badge-size);
  background: var(--red);
  box-shadow: 0 0 0 3px var(--stage);
  color: #fff;
  font-family: var(--font-display);
  font-size: var(--hud-badge-font);
  border-radius: var(--r-full);
  text-align: center;
  padding: 0 5px;
  display: none;
}
.hud-badge:not(:empty) { display: block; animation: popIn 420ms var(--ease-pop); }
.hud-btn.is-ringing .gi, .hud-btn.is-ringing .i { animation: bellRing 900ms ease-out; transform-origin: 50% 12%; }
@keyframes bellRing { 0%, 100% { rotate: 0deg; } 15% { rotate: 20deg; } 30% { rotate: -16deg; } 45% { rotate: 12deg; } 60% { rotate: -8deg; } 75% { rotate: 4deg; } }

#overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 7, 12, 0);
  /* The dim and the blur come and go together, never in one frame. */
  transition: background-color var(--dur-normal) var(--ease-out), backdrop-filter var(--dur-normal) var(--ease-out), -webkit-backdrop-filter var(--dur-normal) var(--ease-out);
}
#overlay.active { pointer-events: all; background: rgba(8, 7, 12, 0.72); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }

/* ── Toasts: a white pill that drops in ─────────────────────────────────── */
#toast-container {
  position: fixed;
  top: calc(var(--hud-height) + var(--safe-top) + 8px);
  inset-inline: var(--gutter);
  z-index: 300;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  pointer-events: none;
}
.toast {
  position: relative;
  max-width: 440px;
  background: var(--card);
  color: var(--ink);
  border-radius: 20px;
  padding: 12px 18px;
  font-size: var(--fs-base);
  font-weight: 700;
  box-shadow: 0 4px 0 var(--card-edge), 0 16px 30px -8px rgba(0, 0, 0, 0.6);
  animation: toastIn 520ms var(--ease-pop);
  pointer-events: all;
  touch-action: none;   /* a swipe up flicks it away (ui.js) */
}
/* Its sticker pops in a beat after the pill lands; a reward's coin turns over once. */
.toast .gi { animation: toastSticker 380ms var(--ease-pop) 120ms both; }
.toast.reward .gi-coin { animation: toastSticker 380ms var(--ease-pop) 120ms both, toastCoin 620ms var(--ease-out) 320ms; }
.toast.is-dragging { animation: none; }
.toast.out { animation: toastOut 240ms var(--ease-in) forwards; pointer-events: none; }
.toast.reward { background: var(--gold); color: var(--gold-ink); box-shadow: 0 4px 0 var(--btn-gold-edge), 0 0 30px rgba(255, 197, 49, 0.45); }
.toast.error { background: var(--btn-red-2); color: #fff; box-shadow: 0 4px 0 var(--btn-red-edge), 0 16px 30px -8px rgba(0, 0, 0, 0.6); }
@keyframes toastIn { 0% { opacity: 0; transform: translateY(-36px) scale(0.85); } 60% { opacity: 1; transform: translateY(4px) scale(1.03); } 100% { transform: none; } }
@keyframes toastOut { to { opacity: 0; transform: translateY(-30px) rotate(calc(-2deg * var(--dir))) scale(0.92); } }
@keyframes toastSticker { 0% { scale: 0.4; } 60% { scale: 1.15; } 100% { scale: 1; } }
@keyframes toastCoin { from { transform: rotateY(0deg); } to { transform: rotateY(360deg); } }

/* ── An achievement unlocked: a gold banner drops from the top (hud.js) ──── */
.ach-pop {
  position: fixed;
  top: calc(var(--safe-top) + 10px);
  inset-inline: 12px;
  z-index: 350;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 520px;
  margin-inline: auto;
  padding: 10px 14px;
  border-radius: 22px;
  background: var(--gold);
  box-shadow: 0 5px 0 var(--btn-gold-edge), 0 18px 34px -8px rgba(0, 0, 0, 0.6);
  pointer-events: none;
}
.ach-pop-badge { display: flex; }
.ach-pop-badge .gi { animation: wiggle 1.2s ease-in-out 700ms infinite; }
.ach-pop-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.ach-pop-kind { font-family: var(--font-display); font-size: 13px; color: var(--gold-ink); opacity: 0.8; }
.ach-pop-name { font-family: var(--font-display); font-size: 19px; color: var(--gold-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ach-pop-reward { display: inline-flex; align-items: center; gap: 2px; padding-block: 2px; padding-inline: 4px 10px; border-radius: 16px; background: rgba(0, 0, 0, 0.12); font-family: var(--font-display); font-size: 18px; color: var(--gold-ink); }

/* ── A new level: the crown, the gold star stamped on it, rays (hud.js) ──── */
.lvl-up {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 24px;
  overflow: hidden;
  text-align: center;
  background: rgba(8, 7, 12, 0.86);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  animation: fadeIn 240ms ease-out;
}
.lvl-stage { position: relative; width: 260px; height: 240px; display: flex; align-items: center; justify-content: center; }
.lvl-crown { position: relative; z-index: 1; display: flex; animation: slamIn 640ms var(--ease-out) both; }
.lvl-star {
  position: absolute;
  z-index: 2;
  bottom: 4px;
  width: 92px; height: 92px;
  display: flex; align-items: center; justify-content: center;
  clip-path: polygon(50% 0, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
  background: var(--gold);
}
.lvl-star .num { font-family: var(--font-display); font-size: 36px; color: var(--gold-ink); margin-top: 8px; }
.lvl-title { font-family: var(--font-display); font-size: 40px; color: var(--gold); animation: bounceIn 520ms var(--ease-out) 420ms both; }
.lvl-sub { font-family: var(--font-display); font-size: 18px; color: var(--text-secondary); animation: bounceIn 520ms var(--ease-out) 560ms both; }
.lvl-ok { margin-top: 18px; min-width: 200px; animation: bounceIn 520ms var(--ease-out) 700ms both; }
@media (max-height: 640px) {
  .lvl-stage { width: 220px; height: 196px; }
  .lvl-stage .gi-crown-logo { width: 100px; height: 100px; }
  .lvl-title { font-size: 34px; }
}

/* ── The dock: floats above the bottom edge; play stands up out of it ───── */
.tab-bar {
  position: fixed;
  bottom: calc(10px + var(--safe-bottom));
  inset-inline: 12px;
  max-width: 540px;
  margin-inline: auto;
  height: 68px;
  padding-inline: 6px;
  border-radius: 26px;
  /* A flat fill, no backdrop blur: a blur behind the dock is drawn again on
     every frame a scene slides under it, for almost nothing to see. */
  background: rgba(29, 26, 37, 0.97);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 4px 0 var(--back-950), 0 18px 36px -10px rgba(0, 0, 0, 0.7);
  display: flex;
  align-items: stretch;
  justify-content: space-around;
  z-index: 100;
  transition:
    transform var(--chrome-dur, var(--dur-normal)) var(--chrome-ease, var(--ease-out)) var(--chrome-delay, 0ms),
    visibility 0s linear var(--chrome-delay, 0ms);
}
.tab-bar.hidden {
  transform: translateY(calc(100% + 64px + var(--safe-bottom)));
  visibility: hidden;
  transition:
    transform var(--chrome-dur, var(--dur-normal)) var(--chrome-ease, var(--ease-in)) var(--chrome-delay, 0ms),
    visibility 0s linear calc(var(--chrome-delay, 0ms) + var(--chrome-dur, var(--dur-normal)));
}

.tab-item {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--tab-item-gap);
  padding: 4px 2px;
  color: var(--text-muted);
  font-size: var(--tab-label-font);
  cursor: pointer;
  min-width: var(--tab-item-min-size);
  min-height: var(--tab-item-min-size);
  background: none;
  border: none;
  border-radius: 20px;
  transition: color var(--dur-fast);
}
.tab-item .tab-icon {
  width: var(--tab-icon-size); height: var(--tab-icon-size);
  display: flex; align-items: center; justify-content: center;
  transition: transform 300ms var(--ease-pop), filter 200ms, opacity 200ms;
  filter: grayscale(0.85);
  opacity: 0.55;
}
.tab-icon img, .tab-icon svg { width: 100%; height: 100%; object-fit: contain; }
.tab-item.active { color: var(--text-primary); }
/* The icon of the tab just chosen jumps into the socket (hud.js, on 'translate'
   and 'scale', so it adds to this resting pose). */
.tab-item.active .tab-icon { transform: translateY(-2px) scale(1.14); filter: none; opacity: 1; }

/* The socket: an ink tile under the active tab that springs from tab to tab,
   stretching as it travels (hud.js sets --sock-x / --sock-w, measured from the
   dock's start edge). Play has none: its own key stands out of the dock. */
.tab-ind {
  position: absolute;
  top: 7px;
  inset-inline-start: 0;
  width: var(--sock-w, 60px);
  height: 54px;
  border-radius: 18px;
  background: var(--back-700);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 3px 0 var(--back-950);
  translate: var(--sock-x, 0px) 0;
  transition: translate 420ms var(--ease-spring), opacity 150ms var(--ease-out);
  pointer-events: none;
}
.tab-ind.is-off { opacity: 0; }
.tab-ind.no-tween { transition: none; }

/* Play is the heart of the game: a big green key standing out of the dock. */
.tab-item[data-world="play"] { justify-content: flex-end; padding-bottom: 6px; }
.tab-item[data-world="play"] .tab-icon {
  position: absolute;
  top: -20px;
  width: 58px; height: 58px;
  border-radius: 20px;
  background: var(--btn-green-2);
  box-shadow: 0 5px 0 var(--btn-green-edge), 0 0 0 5px rgba(29, 26, 37, 0.97), 0 14px 24px -6px rgba(0, 0, 0, 0.6);
  padding: 8px;
  filter: none;
  opacity: 1;
}
.tab-item[data-world="play"] .tab-icon .gi { filter: drop-shadow(0 2px 0 rgba(0, 0, 0, 0.18)); }
.tab-item[data-world="play"].active .tab-icon { transform: none; }
/* .is-stamping: a pop is running on the icon (hud.js popIcon); one scale animation at a time. */
.tab-item[data-world="play"].active .tab-icon:not(.is-stamping) { animation: playBreathe 2.6s ease-in-out infinite; }
@keyframes playBreathe { 0%, 100% { scale: 1; } 50% { scale: 1.05; } }
/* Ambient loops rest with the idle director (core/idle.js): paused while it is
   off (a transition, a hidden page), gone in lite mode (a weak phone). */
html.idle-off .tab-item[data-world="play"].active .tab-icon,
html.idle-off .empty .deck-back,
html.idle-off .mode-icon.is-live .gi { animation-play-state: paused; }
html.idle-lite .tab-item[data-world="play"].active .tab-icon,
html.idle-lite .empty .deck-back,
html.idle-lite .mode-icon.is-live .gi { animation: none; }
.tab-item[data-world="play"].active span:last-child { color: var(--btn-green-1); }
.tab-item[data-world="play"]:not(.active) .tab-icon { background: var(--back-500); box-shadow: 0 5px 0 var(--back-950), 0 0 0 5px rgba(29, 26, 37, 0.97); }

/* ── Scenes, and how they change (scene.js) ────────────────────────────────
   A scene's box is set once, when it mounts: a chrome scene stops short of
   the HUD and the dock; a table (a game, the signed-out screens) takes the
   whole phone. The motion itself is Web Animations in scene.js. */
.scene {
  --scene-top: calc(var(--hud-height) + var(--safe-top));
  --scene-bottom: calc(var(--tab-height) + var(--safe-bottom));
  position: absolute;
  inset-inline: 0;
  top: var(--scene-top);
  bottom: var(--scene-bottom);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.scene.is-table { --scene-top: 0px; --scene-bottom: 0px; }
.scene::-webkit-scrollbar { display: none; }

/* A scene that moves carries the stage, so nothing ever shows through it. It
   is laid out against the screen, not the scene's box (exactly what body and
   body::before paint), so putting it on and taking it off changes no pixel. */
.scene.is-moving {
  background-color: var(--stage);
  background-image:
    radial-gradient(90% 46% at 50% -6%, var(--stage-glow), transparent 72%),
    radial-gradient(70% 38% at 100% 104%, var(--stage-glow-2), transparent 70%),
    var(--stage-pattern);
  background-size: 100vw 100vh, 100vw 100vh, 56px 56px;
  background-size: 100vw 100dvh, 100vw 100dvh, 56px 56px;
  background-position: 0 calc(-1 * var(--scene-top));
  background-repeat: no-repeat, no-repeat, repeat;
}
.scene-ghost { pointer-events: none; overflow: hidden; }
/* A table keeps its size when the HUD and the dock come back over it (a result
   drawn on the board itself): the card and its keys stay between the bars.
   --chrome-top/--chrome-bottom are set by scene.js. */
.scene.is-table .bgr-stage {
  margin-block: var(--chrome-top, 0px) var(--chrome-bottom, 0px);
  max-height: calc(100% - var(--chrome-top, 0px) - var(--chrome-bottom, 0px));
}
/* A ghost copied from a scene that unmounted: frozen on the frame it left on. */
.scene-frozen *, .scene-frozen *::before, .scene-frozen *::after { animation: none !important; transition: none !important; }

/* The strips above and below a chrome scene while a table comes or goes:
   they darken with the screen they belong to, or settle into the stage. */
.scene-veil {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  pointer-events: none;
}
.scene-veil > i { display: block; flex: none; background: var(--veil, #000); }
.scene-veil.is-ground > i {
  background-color: var(--stage);
  background-image:
    radial-gradient(90% 46% at 50% -6%, var(--stage-glow), transparent 72%),
    radial-gradient(70% 38% at 100% 104%, var(--stage-glow-2), transparent 70%),
    var(--stage-pattern);
  background-size: 100vw 100vh, 100vw 100vh, 56px 56px;
  background-size: 100vw 100dvh, 100vw 100dvh, 56px 56px;
  background-repeat: no-repeat, no-repeat, repeat;
  background-attachment: fixed;
}

/* ── Touch: everything squashes under the finger and springs back ────────
   feel.js toggles these. 'scale'/'translate' compose with any transform. */
.is-pressed { scale: 0.95; transition: scale 70ms ease-out, translate 70ms ease-out, box-shadow 70ms ease-out, filter 70ms; filter: brightness(0.96); }
.deck-card.is-pressed { scale: 0.97; translate: 0 3px; }
.is-released { animation: pressPop 380ms var(--ease-pop); }
@keyframes pressPop { 0% { scale: 0.95; } 50% { scale: 1.05; } 75% { scale: 0.99; } 100% { scale: 1; } }

/* Content is dealt in when a list first paints (feel.js staggers it). Each
   card is in view almost at once and the deal is in its movement, so a list
   never sits empty while it deals, nor a pushed screen arrive blank. */
.dealt-auto { animation: dealIn 480ms var(--ease-pop) var(--deal-delay, 0ms) both; }
@keyframes dealIn { 0% { opacity: 0; transform: translateY(18px) scale(0.94); } 20% { opacity: 1; } 100% { opacity: 1; transform: none; } }

.is-counting { display: inline-block; scale: 1.12; color: var(--gold-soft); transition: scale 120ms; }
.is-landed { display: inline-block; animation: numberLand 440ms var(--ease-pop); }
@keyframes numberLand { 0% { scale: 1.35; } 100% { scale: 1; } }

/* ── Screen furniture ───────────────────────────────────────────────────── */
.screen {
  position: relative;
  width: 100%;
  max-width: 560px;
  margin-inline: auto;
  padding: 8px var(--gutter) 28px;
}

/* A screen header: round back key, a big title, actions at the end. */
.screen-head {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 56px;
  margin-bottom: 14px;
}
.screen-head-text { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; text-align: center; }
.screen-head-actions { display: flex; align-items: center; gap: var(--s-2); }
.screen-head > [data-back], .screen-head > .icon-btn:first-child { flex-shrink: 0; }
.screen-head:has(> [data-back]):not(:has(.screen-head-actions)) .screen-head-text { margin-inline-end: 56px; }

.screen-title {
  font-size: var(--fs-xl);
  line-height: 1.15;
  color: var(--text-primary);
  text-wrap: balance;
}
.screen-head .screen-title {
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.screen-sub { margin-top: 6px; font-size: var(--fs-sm); font-weight: 600; color: var(--text-secondary); }

/* A ribbon is now a flat tag. */
.ribbon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px 18px;
  background: var(--btn-red-2);
  border-radius: 14px;
  box-shadow: 0 4px 0 var(--btn-red-edge);
  color: #fff;
  font-size: var(--fs-lg);
  text-shadow: var(--text-outline);
}
.ribbon.gold { background: var(--btn-gold-2); box-shadow: 0 4px 0 var(--btn-gold-edge); color: var(--gold-ink); text-shadow: none; }
.ribbon.green { background: var(--btn-green-2); box-shadow: 0 4px 0 var(--btn-green-edge); }
.ribbon.blue { background: var(--btn-blue-2); box-shadow: 0 4px 0 var(--btn-blue-edge); }

.section-title {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 24px 0 10px;
  font-size: var(--fs-md);
  color: var(--text-primary);
}
.section-title .link { margin-inline-start: auto; font-size: var(--fs-sm); color: var(--sky); }

/* Round keys for icons; the back key is the same, with a bold arrow. */
.icon-btn {
  width: 46px; height: 46px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 16px;
  background: var(--back-700);
  border: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 4px 0 var(--back-950);
  color: var(--text-primary);
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
}
.icon-btn.is-pressed { translate: 0 3px; box-shadow: 0 1px 0 var(--back-950); }
[data-back].icon-btn .i { stroke-width: 2.8; }

/* ── Profile avatar ─────────────────────────────────────────────────────── */
.profile-avatar-wrap { position: relative; width: 96px; height: 96px; margin: 0 auto; cursor: pointer; }
.profile-avatar {
  width: 96px; height: 96px;
  border-radius: 30px;
  background: var(--btn-purple-2);
  box-shadow: 0 5px 0 var(--btn-purple-edge);
  display: flex; align-items: center; justify-content: center;
  color: #fff;
  overflow: hidden;
}
.profile-avatar-edit {
  position: absolute;
  bottom: -6px; inset-inline-end: -6px;
  width: 34px; height: 34px;
  border-radius: 12px;
  background: var(--btn-green-2);
  box-shadow: 0 0 0 3px var(--stage), 0 3px 0 var(--btn-green-edge);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
}

/* ── Panels: flat ink, a step up from the stage ─────────────────────────── */
.card, .tray {
  position: relative;
  background: var(--back-800);
  border-radius: var(--r-xl);
  padding: var(--s-4);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 4px 0 var(--back-950);
}

/* ── Game cards: white, with a lip ──────────────────────────────────────── */
.deck-card {
  position: relative;
  background: var(--card);
  color: var(--ink);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  isolation: isolate;
}
.deck-card { --suit: var(--ink); }
.deck-card.ink-red { --suit: var(--red-ink); }
.deck-card .index {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  color: var(--suit);
  font-family: var(--font-display);
  line-height: 1;
}
.deck-card .index.top { top: 13px; inset-inline-start: 13px; }
.deck-card .index.bottom { bottom: 13px; inset-inline-end: 13px; transform: rotate(180deg); }
button.deck-card, .deck-card[role="button"] { border: none; cursor: pointer; font: inherit; text-align: inherit; }
.deck-card.is-pressed { box-shadow: 0 1px 0 var(--card-edge), 0 6px 14px -6px rgba(0, 0, 0, 0.5); }

/* Locked or not yet: the card lies face down, showing the violet back. */
.deck-back,
.deck-card.face-down {
  background-color: var(--btn-purple-2);
  background-image: var(--back-pattern);
  background-size: 18px 18px;
  color: #fff;
}
.deck-card.face-down { box-shadow: 0 5px 0 var(--btn-purple-edge), 0 14px 28px -10px rgba(0, 0, 0, 0.55); }

/* Spent: a punched corner. */
.deck-card.spent {
  -webkit-mask: radial-gradient(circle 12px at calc(100% - 18px) 18px, transparent 98%, #000 100%);
          mask: radial-gradient(circle 12px at calc(100% - 18px) 18px, transparent 98%, #000 100%);
}
[dir="rtl"] .deck-card.spent {
  -webkit-mask: radial-gradient(circle 12px at 18px 18px, transparent 98%, #000 100%);
          mask: radial-gradient(circle 12px at 18px 18px, transparent 98%, #000 100%);
}

/* ── Buttons: flat colour on a solid lip ────────────────────────────────── */
button,
[role="button"],
input[type="button"],
input[type="submit"] { min-block-size: var(--tap-min, 44px); }

a { color: var(--sky); text-underline-offset: 3px; }
a:not(.btn):not([class*="tab"]) { display: inline-flex; align-items: center; min-block-size: var(--tap-min, 44px); }

.icon-btn, .hud-btn, #emote-inline-toggle, .bgg-action-btn, #back-btn { min-inline-size: var(--tap-min, 44px); }

.btn {
  --b1: var(--btn-wood-1); --b2: var(--btn-wood-2); --be: var(--btn-wood-edge); --bt: #fff;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 22px;
  min-height: var(--btn-min-height);
  border: none;
  border-radius: var(--r-btn);
  background: linear-gradient(180deg, var(--b1), var(--b2));
  color: var(--bt);
  font-size: var(--fs-md);
  line-height: 1.1;
  text-shadow: var(--text-outline);
  cursor: pointer;
  box-shadow: 0 var(--btn-lip) 0 var(--be);
  transition: filter var(--dur-fast);
  -webkit-user-select: none;
  isolation: isolate;
}
.btn .i { stroke-width: 2.6; }
.btn.is-pressed { translate: 0 4px; box-shadow: 0 1px 0 var(--be); }
.btn:disabled, .btn[aria-disabled="true"] { filter: grayscale(0.9) brightness(0.75); cursor: default; }

.btn-primary, .btn-game { --b1: var(--btn-green-1); --b2: var(--btn-green-2); --be: var(--btn-green-edge); }
/* Small keys carry 13-14px text, which needs 4.5:1: a deeper green. */
.btn-sm.btn-primary, .btn-sm.btn-game { --b1: var(--btn-green-deep); --b2: var(--btn-green-deep); --be: var(--btn-green-deep-edge); }
.btn-card, .btn-blue { --b1: var(--btn-blue-1); --b2: var(--btn-blue-2); --be: var(--btn-blue-edge); }
.btn-gold { --b1: var(--btn-gold-1); --b2: var(--btn-gold-2); --be: var(--btn-gold-edge); --bt: var(--gold-ink); text-shadow: none; }
.btn-danger, .btn-red { --b1: var(--btn-red-1); --b2: var(--btn-red-2); --be: var(--btn-red-edge); }
.btn-purple { --b1: var(--btn-purple-1); --b2: var(--btn-purple-2); --be: var(--btn-purple-edge); }
.btn-orange { --b1: var(--btn-orange-1); --b2: var(--btn-orange-2); --be: var(--btn-orange-edge); }
.btn-white { --b1: var(--btn-white-1); --b2: var(--btn-white-2); --be: var(--btn-white-edge); --bt: var(--ink); text-shadow: none; }
.btn-secondary, .btn-wood { --b1: var(--btn-wood-1); --b2: var(--btn-wood-2); --be: var(--btn-wood-edge); --bt: var(--text-primary); }
.btn-ghost {
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--back-line-strong);
  color: var(--text-secondary);
  text-shadow: none;
}
.btn-primary .gi-refresh, .btn-primary .gi-check, .btn-primary .gi-plus, .btn-primary .gi-up,
.btn-primary .gi-puzzle, .btn-primary .gi-friends, .btn-primary .gi-team { filter: brightness(0) invert(1); }   /* green stickers vanish on a green key */
.btn-ghost.is-pressed { translate: 0 1px; box-shadow: inset 0 0 0 2px var(--back-line-strong); }

/* The big calls to play catch the light now and then, one at a time across
   the screen: feel.js gives .btn-xl, .btn-lg.btn-primary and .btn-lg.btn-gold
   data-idle="glint" (unless a screen set data-idle itself) and the idle
   director (core/idle.js) sweeps a light band across one of them every few
   seconds, never in game mode, a transition or lite mode. */

.btn-block { width: 100%; }
.btn-lg { min-height: 62px; font-size: var(--fs-lg); padding-inline: 26px; border-radius: 20px; }
.btn-xl { min-height: 70px; font-size: var(--fs-xl); padding-inline: 30px; border-radius: 22px; --btn-lip: 6px; }
.btn-sm { min-height: 40px; font-size: var(--fs-sm); padding-inline: 14px; border-radius: 14px; --btn-lip: 3px; }

/* ── Game-mode buttons and the room hero (ui.modeButton / ui.roomHero) ── */
.mode-btn {
  justify-content: flex-start;
  gap: 14px;
  min-height: 84px;
  padding: 10px 14px;
  border-radius: 24px;
  text-align: start;
}
.mode-btn + .mode-btn { margin-top: 14px; }
.mode-icon {
  width: 60px; height: 60px;
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.2);
}
.mode-icon.is-live .gi { animation: idleBob 2.4s ease-in-out infinite; }
.mode-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.mode-title { font-family: var(--font-display); font-size: var(--fs-lg); line-height: 1.1; }
.mode-sub { font-family: var(--font-ar); font-weight: 600; font-size: var(--fs-xs); opacity: 0.9; text-shadow: none; line-height: 1.3; }
.mode-chev { flex-shrink: 0; opacity: 0.85; }

.room-hero { display: flex; justify-content: center; margin: 4px 0 22px; }
.room-hero-card { width: min(62vw, 240px); padding: 0; overflow: hidden; }
.room-hero-art { display: block; margin: 8px 8px 0; height: calc(min(62vw, 240px) * 0.62); border-radius: 16px; overflow: hidden; }
.room-hero-art .game-art { width: 100%; height: 100%; display: block; }
.room-hero-name { display: block; padding: 8px 0 10px; text-align: center; font-family: var(--font-display); font-size: var(--fs-lg); color: var(--ink); }
.room-hero-card:not(:has(.room-hero-name)) .room-hero-art { margin-bottom: 8px; }

/* ── Inputs ─────────────────────────────────────────────────────────────── */
.input {
  width: 100%;
  padding: 12px 16px;
  background: var(--back-700);
  border: 2px solid transparent;
  border-radius: var(--r-input);
  color: var(--text-primary);
  font-family: var(--font-ar);
  font-size: 17px;
  font-weight: 600;
  outline: none;
  transition: border-color var(--dur-fast), background var(--dur-fast);
  min-height: 54px;
  user-select: text;
}
.input:focus { border-color: var(--btn-purple-1); background: var(--back-600); }
.input::placeholder { color: var(--text-dim); }

/* ── Chips: pills; the chosen one is white ──────────────────────────────── */
.chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  padding: 2px 2px 8px;
}
.chips::-webkit-scrollbar { display: none; }
.chip {
  position: relative;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;           /* the 44px tap floor */
  padding: 0 16px;
  border-radius: var(--r-full);
  border: none;
  background: var(--back-700);
  box-shadow: 0 3px 0 var(--back-950);
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  cursor: pointer;
}
.chip.is-pressed { translate: 0 2px; box-shadow: 0 1px 0 var(--back-950); }
.chip.active, .chip[aria-pressed="true"], .chip[aria-selected="true"] {
  background: var(--card);
  box-shadow: 0 3px 0 var(--card-edge);
  color: var(--ink);
}
.chip.active *, .chip[aria-pressed="true"] *, .chip[aria-selected="true"] * { font-weight: inherit; }

/* ── Rows: each one its own ink tile ────────────────────────────────────── */
.rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.rows:empty { display: none; }
.row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 64px;
  padding: 10px 14px;
  border-radius: 20px;
  background: var(--back-800);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 3px 0 var(--back-950);
}
.row-main { flex: 1; min-width: 0; }
.row-title { font-size: var(--fs-base); font-weight: 700; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row-sub { font-size: var(--fs-sm); font-weight: 500; color: var(--text-muted); }

.avatar {
  width: 44px; height: 44px;
  border-radius: 15px;
  background: var(--btn-purple-2);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
  font-family: var(--font-display);
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }

/* Count badges anywhere: a red bead ringed in the stage colour. */
.badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; padding: 0 6px;
  border-radius: 11px;
  background: var(--red);
  box-shadow: 0 0 0 3px var(--stage);
  color: #fff;
  font-family: var(--font-display);
  font-size: 13px;
  line-height: 1;
}

/* ── Empty states: one big quiet mark and one honest line ───────────────── */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 12px;
  padding: 40px 24px;
  color: var(--text-secondary);
}
.empty .deck-back {
  width: 92px; height: 92px;
  border-radius: 30px;
  background: var(--back-700);
  background-image: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), 0 5px 0 var(--back-950);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-muted);
  animation: float 3.4s ease-in-out infinite;
}
.empty .deck-back .i { width: 40px; height: 40px; }
.empty-title { font-size: var(--fs-lg); color: var(--text-primary); }
.empty-sub { font-size: var(--fs-sm); font-weight: 500; max-width: 34ch; color: var(--text-muted); }

/* ── Utility ────────────────────────────────────────────────────────────── */
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.gap-2 { gap: var(--s-2); }
.gap-3 { gap: var(--s-3); }
.gap-4 { gap: var(--s-4); }
.text-center { text-align: center; }
.text-gold { color: var(--gold); }
.text-secondary { color: var(--text-secondary); }
.w-full { width: 100%; }
.p-4 { padding: var(--s-4); }
.mt-4 { margin-top: var(--s-4); }
.num { font-variant-numeric: tabular-nums; direction: ltr; unicode-bidi: isolate; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* A shade lighter than a row, with an edge: a loading list reads as rows to
   come, never as an empty screen. */
.skeleton {
  background: linear-gradient(90deg, var(--back-700) 25%, var(--back-600) 50%, var(--back-700) 75%);
  background-size: 200% 100%;
  box-shadow: inset 0 0 0 1px var(--back-line-strong);
  animation: shimmer 1.4s infinite linear;
  border-radius: var(--r-md);
}
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* Rows still to come lie face down (ui.loadingRows): ink card backs the size
   of the rows, the diamond lattice inset on each, breathing in turn. When the
   rows arrive they are dealt in, or turned over in place (ui.revealRows). */
.skeleton.sk-row {
  position: relative;
  overflow: hidden;
  border-radius: 20px;
  background: var(--back-800);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 3px 0 var(--back-950);
  animation: none;
}
.skeleton.sk-row::before {
  content: '';
  position: absolute;
  inset: 6px;
  border-radius: 15px;
  background-image: var(--back-pattern);
  background-size: 18px 18px;
  opacity: 0.22;
  animation: skBack 1.4s ease-in-out calc(var(--sk-i, 0) * 140ms) infinite alternate;
}
@keyframes skBack { from { opacity: 0.12; } to { opacity: 0.32; } }
html.idle-lite .skeleton.sk-row::before { animation: none; }

/* A whole screen waiting (ui.dealingLoader): three violet backs dealt in from
   the reading side, fanned, gathered, and off again, in a loop. Silent. */
.deal-loader {
  position: relative;
  display: grid;
  place-items: center;
  width: 104px;
  height: 80px;
  margin: 48px auto;
}
.deal-loader > i {
  grid-area: 1 / 1;
  width: 38px;
  height: 54px;
  border-radius: 9px;
  background-color: var(--btn-purple-2);
  background-image: var(--back-pattern);
  background-size: 10px 10px;
  box-shadow: inset 0 0 0 2px var(--btn-purple-1), 0 3px 0 var(--btn-purple-edge);
  animation: dealLoop 1.2s var(--ease-in-out) infinite both;
}
.deal-loader > i:nth-child(1) { --fan: -16deg; --fx: -16px; }
.deal-loader > i:nth-child(2) { --fan: 0deg; --fx: 0px; animation-delay: 70ms; }
.deal-loader > i:nth-child(3) { --fan: 16deg; --fx: 16px; animation-delay: 140ms; }
.deal-loader.is-sm { width: 40px; height: 30px; margin: 0; }
.deal-loader.is-sm > i { width: 15px; height: 21px; border-radius: 4px; background-size: 6px 6px; box-shadow: inset 0 0 0 1.5px var(--btn-purple-1), 0 2px 0 var(--btn-purple-edge); }
.deal-loader.is-sm > i:nth-child(1) { --fx: -7px; }
.deal-loader.is-sm > i:nth-child(3) { --fx: 7px; }
@keyframes dealLoop {
  0% { transform: translateX(calc(var(--dir) * -56px)) rotate(calc(var(--dir) * -24deg)); opacity: 0; }
  20% { transform: none; opacity: 1; }
  42%, 62% { transform: translateX(var(--fx)) rotate(var(--fan)); opacity: 1; }
  80% { transform: none; opacity: 1; }
  100% { transform: translateX(calc(var(--dir) * 56px)) rotate(calc(var(--dir) * 24deg)); opacity: 0; }
}

/* ── Motion vocabulary (names kept: scene code calls them) ──────────────── */
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
@keyframes float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -6px; } }
@keyframes glow { 0%, 100% { box-shadow: 0 0 0 0 rgba(255, 197, 49, 0); } 50% { box-shadow: 0 0 22px 4px rgba(255, 197, 49, 0.5); } }
@keyframes shake { 0%,100% { transform: translateX(0); } 20% { transform: translateX(-6px); } 40% { transform: translateX(6px); } 60% { transform: translateX(-3px); } 80% { transform: translateX(3px); } }
@keyframes popIn { 0% { transform: scale(0.4); opacity: 0; } 70% { transform: scale(1.1); opacity: 1; } 100% { transform: scale(1); } }
@keyframes slamIn { 0% { transform: scale(2.2) rotate(-10deg); opacity: 0; } 55% { transform: scale(0.9) rotate(2deg); opacity: 1; } 75% { transform: scale(1.06); } 100% { transform: none; } }
@keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
@keyframes slideUpBounce { 0% { transform: translateY(100%); } 70% { transform: translateY(-4%); } 100% { transform: translateY(0); } }
@keyframes bounceIn { 0% { transform: scale(0.5); opacity: 0; } 60% { transform: scale(1.08); opacity: 1; } 80% { transform: scale(0.97); } 100% { transform: scale(1); } }
@keyframes breatheGlow { 0%, 100% { box-shadow: 0 0 0 0 rgba(255, 197, 49, 0); } 50% { box-shadow: 0 0 18px 3px rgba(255, 197, 49, 0.45); } }
@keyframes bgGradientMove { 0%, 100% { background-position: 0 0; } }
@keyframes numberPop { 0% { transform: scale(1); } 50% { transform: scale(1.25); } 100% { transform: scale(1); } }
@keyframes ringExpand { 0% { transform: scale(0.5); opacity: 1; } 100% { transform: scale(2.4); opacity: 0; } }
@keyframes wobble { 0%,100% { transform: rotate(0); } 25% { transform: rotate(-4deg); } 75% { transform: rotate(4deg); } }
@keyframes shimmerBorder { 0%, 100% { border-color: rgba(255, 197, 49, 0.3); } 50% { border-color: rgba(255, 197, 49, 0.9); } }
@keyframes fadeIn { from { opacity: 0; } }
/* A dealt card is in view almost at once; the deal is in its flight, so a
   screen that deals its cards never arrives empty. */
@keyframes deal { 0% { opacity: 0; transform: translateY(-40px) rotate(var(--deal-rot, -8deg)) scale(0.9); } 20% { opacity: 1; } }
@keyframes flipIn { 0% { transform: perspective(900px) rotateY(90deg) scale(0.9); } 60% { transform: perspective(900px) rotateY(-10deg) scale(1.04); } 100% { transform: perspective(900px) rotateY(0) scale(1); } }
@keyframes wiggle { 0%, 82%, 100% { rotate: 0deg; } 86% { rotate: -9deg; } 90% { rotate: 8deg; } 94% { rotate: -5deg; } 97% { rotate: 3deg; } }
@keyframes idleBob { 0%, 100% { translate: 0 0; } 50% { translate: 0 -4px; } }

.pulse { animation: pulse 1.6s ease-in-out infinite; }
.float { animation: float 3.2s ease-in-out infinite; }
.shake { animation: shake 0.34s ease; }
.pop-in { animation: popIn 420ms var(--ease-out); }
.slam-in { animation: slamIn 560ms var(--ease-out); }
.bounce-in { animation: bounceIn 520ms var(--ease-out); }
.slide-up { animation: slideUp var(--dur-normal) var(--ease-out); }
.slide-up-bounce { animation: slideUpBounce 460ms var(--ease-deal); }
.breathe-glow { animation: breatheGlow 2.2s ease-in-out infinite; }
.wobble { animation: wobble 0.5s ease; }
.wiggle { animation: wiggle 3s ease-in-out infinite; }
.number-pop { animation: numberPop 300ms var(--ease-out); }
.dealt { animation: deal 520ms var(--ease-pop) both; }
.flip-in { animation: flipIn 640ms var(--ease-deal) both; }

::-webkit-scrollbar { width: 4px; height: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.15); border-radius: 2px; }

/* Reduced motion: everything arrives at once. Delays go too, or content with
   a staggered entrance would sit invisible for its delay. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    transition-delay: 0s !important;
  }
  /* Scenes never tween: a scene's stage has to line up with the body's in the
     very frame it is put on (the 1ms above would show it unregistered first). */
  .scene, .scene-veil, .scene-veil > i { transition: none !important; }
  /* A loader that ran once would end off screen: it stands still, fanned. */
  .deal-loader > i { animation: none !important; transform: translateX(var(--fx)) rotate(var(--fan)); }
}

/* Native app mode */
/* Shells that wrap the page in their own SafeArea. A shell that draws edge to
   edge sends its insets instead (native-bridge.js: html.native-insets, and
   --safe-bottom already lifts the dock clear of the system bar). */
html.native-app:not(.native-insets) .tab-bar { padding-bottom: calc(var(--safe-bottom) + 8px); }
