/* Luxury casino theme. No frameworks, no webfonts, no external requests (the CSP blocks
   them), so every material here is built from gradients, borders and shadows. */
:root {
  --card-w: 46px;
  --card-h: 68px;
  /* Deep, warm-tinted blacks rather than blue-grey: warmth is what separates a casino
     floor from a dashboard. */
  --bg: #06090c;
  --bg-deep: #040607;
  --bg-raised: #0c1217;
  --panel: #10181d;
  --panel-hi: #151f26;
  --bg-input: #080c0f;

  /* Crimson is the accent (the variable names still say gold, which keeps every rule below working). Three stops so highlights, fills and hairlines stay coherent. */
  --gold: #2893e0;
  --gold-bright: #5ab2f2;
  --gold-dim: #1a5e8f;
  --gold-line: rgba(40, 147, 224, .25);
  --gold-glow: rgba(40, 147, 224, .42);

  /* Dark wine for surfaces that should read as a table, and a bright green for wins. */
  --felt: #245375;
  --felt-hi: #275372;
  --win: #35d9a0;
  --danger: #ff8a4c;
  --warn: #e0a94e;

  --text: #f2ede3;
  --text-dim: #a2988a;
  --text-faint: #6e6558;

  --line: #1a2730;
  --line-soft: #131c22;

  --radius: 12px;
  --radius-sm: 7px;
  --mono: ui-monospace, "SFMono-Regular", "Cascadia Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --serif: "Hoefler Text", Baskerville, "Palatino Linotype", Georgia, "Times New Roman", serif;
  --shadow: 0 14px 40px rgba(0, 0, 0, .55);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  color: var(--text);
  font: 14px/1.55 var(--sans);
  -webkit-font-smoothing: antialiased;
  background:
    radial-gradient(1200px 620px at 50% -12%, rgba(40, 147, 224, .07), transparent 62%),
    radial-gradient(900px 500px at 88% 8%, rgba(32, 84, 121, .10), transparent 60%),
    var(--bg);
  background-attachment: fixed;
}

a { color: var(--gold); text-decoration: none; }
a:hover { color: var(--gold-bright); }
h1, h2, h3 { margin: 0 0 12px; font-weight: 600; letter-spacing: -0.01em; }
h1 { font-size: 22px; font-family: var(--serif); }
h2 { font-size: 17px; font-family: var(--serif); letter-spacing: .01em; }
h3 {
  font-size: 11px; color: var(--gold); font-family: var(--sans);
  text-transform: uppercase; letter-spacing: .16em; font-weight: 600;
}
code, .mono { font-family: var(--mono); font-size: 12px; }

/* ---------------------------------------------------------------- header */
/* The header is sticky, so every pixel of it is a pixel taken off every game for the
   whole session. It used to be 190px tall — nearly a quarter of an 833px screen — because
   a dozen game buttons and seven account buttons all wrapped inside one flex row, giving
   three stacked bands. It is one band now: the row never wraps, the game nav scrolls
   sideways instead of wrapping, and the account buttons live in a menu. */
header.top {
  display: flex; align-items: center; gap: 12px;
  padding: 6px 18px;
  background: linear-gradient(180deg, #0d1419 0%, #0a0f12 100%);
  border-bottom: 1px solid var(--gold-line);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .03) inset, 0 10px 30px rgba(0, 0, 0, .45);
  position: sticky; top: 0; z-index: 40;
  flex-wrap: nowrap;
}
.brand {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--serif); font-weight: 600; font-size: 17px;
  letter-spacing: .02em; color: var(--gold-bright);
  flex: 0 0 auto;
}
.brand .chip {
  width: 20px; height: 20px; border-radius: 50%;
  background: conic-gradient(from 210deg, var(--gold-bright), var(--gold-dim), var(--gold-bright));
  box-shadow: 0 0 14px var(--gold-glow), inset 0 0 6px rgba(0, 0, 0, .5);
}
.spacer { flex: 1 1 auto; }

/* The game nav is the one part that genuinely cannot fit on a phone, so it scrolls rather
   than wraps: a second line here costs every game 30px forever, a sideways scroll costs
   nothing until you need it. `min-width: 0` is what actually lets it shrink — a flex item
   defaults to min-width:auto and would otherwise push the bar wider than the screen. */
header.top nav#navGames {
  flex: 0 1 auto; min-width: 0;
  flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -ms-overflow-style: none;
  scroll-snap-type: x proximity;
}
header.top nav#navGames::-webkit-scrollbar { display: none; }
header.top nav#navGames button { flex: 0 0 auto; scroll-snap-align: start; }

.balance {
  display: flex; align-items: baseline; gap: 7px;
  background: linear-gradient(180deg, #0c1216, #070b0e);
  border: 1px solid var(--gold-line);
  border-radius: 999px;
  padding: 4px 12px;
  flex: 0 0 auto;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .04);
}
.balance .label {
  font-size: 9px; color: var(--text-faint);
  text-transform: uppercase; letter-spacing: .14em;
}
.balance .value {
  font-family: var(--mono); font-size: 14px; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--gold-bright);
}
.balance .value.flash { color: var(--win); }
.balance .value.flash-down { color: var(--danger); }

/* ------------------------------------------------------------ header menu */
.menu { position: relative; flex: 0 0 auto; }
.menu-trigger { display: flex; align-items: center; gap: 7px; }
/* Three bars drawn in the border so the trigger reads as a menu without an icon font. */
.menu-trigger::before {
  content: ''; width: 13px; height: 9px;
  border-top: 2px solid currentColor; border-bottom: 2px solid currentColor;
  box-shadow: inset 0 0 0 1px transparent;
  background: linear-gradient(currentColor, currentColor) center/100% 2px no-repeat;
}
.menu-trigger[aria-expanded="true"] { background: rgba(255, 255, 255, .06); border-color: var(--line); }

.menu-panel {
  position: absolute; top: calc(100% + 7px); right: 0; z-index: 50;
  min-width: 190px;
  display: flex; flex-direction: column; gap: 2px;
  padding: 6px;
  background: linear-gradient(180deg, #0f161b, #0a0f12);
  border: 1px solid var(--gold-line); border-radius: var(--radius-sm);
  box-shadow: 0 18px 40px rgba(0, 0, 0, .6), inset 0 1px 0 rgba(255, 255, 255, .04);
}
.menu-item {
  display: block; width: 100%; text-align: left;
  background: transparent; border-color: transparent; color: var(--text-dim);
  padding: 7px 10px; font-size: 13px; font-weight: 500;
  text-decoration: none;
}
.menu-item:hover { background: rgba(255, 255, 255, .05); border-color: transparent; color: var(--text); }
.menu-sep { height: 1px; margin: 4px 2px; background: var(--line); }

/* --------------------------------------------------------------- buttons */
button, .btn {
  font: inherit; font-weight: 600;
  color: var(--text);
  background: linear-gradient(180deg, var(--panel-hi), var(--panel));
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 9px 15px;
  cursor: pointer;
  transition: border-color .15s, background .15s, transform .08s, box-shadow .15s;
}
button:hover:not(:disabled), .btn:hover {
  border-color: var(--gold-line); background: linear-gradient(180deg, #1a2730, #121b21);
}
button:active:not(:disabled) { transform: translateY(1px); }
button:disabled { opacity: .4; cursor: not-allowed; }

button.primary {
  background: linear-gradient(180deg, var(--gold-bright) 0%, var(--gold) 55%, #3388c4 100%);
  border-color: #1b557e;
  color: #1a1305;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .35);
  box-shadow: 0 6px 18px rgba(40, 147, 224, .20), inset 0 1px 0 rgba(255, 255, 255, .5);
}
button.primary:hover:not(:disabled) {
  background: linear-gradient(180deg, #5ab2f2 0%, var(--gold-bright) 55%, var(--gold) 100%);
  box-shadow: 0 8px 26px rgba(40, 147, 224, .34), inset 0 1px 0 rgba(255, 255, 255, .6);
}
button.danger { background: transparent; border-color: #273b4a; color: var(--danger); }
button.danger:hover:not(:disabled) { background: #141e26; border-color: #6b3743; }
button.ghost { background: transparent; border-color: transparent; }
button.ghost:hover:not(:disabled) { background: rgba(255, 255, 255, .04); border-color: var(--line); }
button.big { padding: 13px 18px; width: 100%; font-size: 15px; letter-spacing: .02em; }
button.tiny { padding: 5px 10px; font-size: 12px; font-weight: 500; }

/* ---------------------------------------------------------------- inputs */
label.field { display: block; margin-bottom: 14px; }
label.field > span {
  display: block; margin-bottom: 6px;
  font-size: 10px; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: .14em; font-weight: 600;
}
input, select {
  font: inherit; width: 100%;
  color: var(--text);
  background: var(--bg-input);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 11px;
  transition: border-color .15s, box-shadow .15s;
}
input.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
input:focus, select:focus {
  outline: none; border-color: var(--gold-dim);
  box-shadow: 0 0 0 3px rgba(40, 147, 224, .10);
}
input[type=range] { padding: 0; accent-color: var(--gold); }
.input-row { display: flex; gap: 6px; }
.input-row input { flex: 1 1 auto; }

/* ---------------------------------------------------------------- layout */
.wrap { max-width: 1280px; margin: 0 auto; padding: 20px 16px; }
.tabs { display: flex; gap: 6px; margin-bottom: 18px; flex-wrap: wrap; }
.tabs button { background: transparent; border-color: transparent; color: var(--text-dim); }
.tabs button.on {
  background: linear-gradient(180deg, var(--panel-hi), var(--panel));
  border-color: var(--gold-line); color: var(--gold-bright);
}

.layout { display: grid; grid-template-columns: 330px 1fr; gap: 18px; align-items: start; }
/* A panel with nothing in it is not a panel, it is a rectangle. The picker has no bet
   controls, so both would otherwise sit beside it as empty boxes. */
.panel:empty { display: none; }
.panel {
  background: linear-gradient(180deg, var(--panel) 0%, #0d1317 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px;
  box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, .035);
}
.panel + .panel { margin-top: 18px; }
.stage {
  position: relative;
  background:
    radial-gradient(760px 340px at 50% -10%, rgba(40, 147, 224, .055), transparent 66%),
    linear-gradient(180deg, #0c1217 0%, #070a0d 100%);
  border: 1px solid var(--gold-line);
  border-radius: var(--radius);
  padding: 22px;
  min-height: 330px;
  display: flex; flex-direction: column; justify-content: center;
  box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, .04);
}

/* ----------------------------------------------------------------- stats */
.stat-row {
  display: flex; justify-content: space-between; gap: 10px;
  padding: 6px 0; font-size: 13px;
  border-bottom: 1px solid var(--line-soft);
}
.stat-row:last-child { border-bottom: none; }
.stat-row .k { color: var(--text-dim); }
.stat-row .v { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; }
.stat-card {
  background: linear-gradient(180deg, #0d1318, #090d10);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: 12px 14px;
}
.stat-card .k {
  font-size: 9px; color: var(--text-faint);
  text-transform: uppercase; letter-spacing: .14em;
}
.stat-card .v {
  font-family: var(--mono); font-size: 18px; font-weight: 600;
  margin-top: 4px; font-variant-numeric: tabular-nums;
}
.pos { color: var(--win); }
.neg { color: var(--danger); }
.hint { font-size: 12px; color: var(--text-faint); margin: 8px 0 0; line-height: 1.6; }

/* ------------------------------------------------------------------ dice */
/* ---------------------------------------------------------------- Кости ---
   Two dice on a felt table. The canvas draws the dice and the table; everything here
   frames it and prints the numbers underneath. */
.bones-board {
  position: relative;
  width: 100%;
  border-radius: 12px;
  overflow: hidden;
  background: #0b0e0c;
  box-shadow: inset 0 2px 14px rgba(0, 0, 0, .6);
}
.dice-canvas { display: block; width: 100%; }

/* The fallback, for a browser with no WebGL. Two big glyph dice. The game is identical;
   only the tumble is missing, and saying so is better than showing an empty box. */
.bones-flat {
  display: flex; justify-content: center; gap: 18px;
  padding: 26px 0 8px;
  font-size: 84px; line-height: 1;
  color: var(--gold-bright);
  text-shadow: 0 0 30px var(--gold-glow);
}

/* The call: eleven totals, 2 through 12. Touch targets are 44px because a phone is the
   target and a 28px button is a miss waiting to happen. */
.bones-call {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(44px, 1fr));
  gap: 6px;
  margin: 6px 0 10px;
}
.bones-num {
  min-height: 44px;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
/* Inside the call. The two ends are brighter, so a range reads as a span with edges
   rather than as an undifferentiated block. */
.bones-num.on {
  background: linear-gradient(180deg, hsl(118 26% 26%), hsl(118 24% 18%));
  border-color: hsl(118 30% 38%);
  color: var(--text);
}
.bones-num.edge {
  border-color: var(--gold);
  box-shadow: 0 0 0 1px var(--gold-glow);
}
.bones-callout {
  font-family: var(--mono);
  font-size: 18px;
  color: var(--gold-bright);
  font-variant-numeric: tabular-nums;
}

/* A multiplier the house cannot pay in full is struck through in the panel, never hidden:
   the smaller true number is shown, and the reason sits beside it. */
.capped { color: var(--warn); }
.bones-hint.warn { color: var(--warn); }

.bones-verdict {
  text-align: center;
  padding: 14px 0 6px;
  font-family: var(--mono);
  font-size: 15px;
  color: var(--text-dim);
}
.bones-verdict.win { color: var(--win); }
.bones-verdict.lose { color: var(--danger); }

/* The whole board, priced, so the offer is legible before it is taken. */
.bones-paytable {
  display: grid;
  gap: 2px;
  margin-top: 10px;
  font-size: 12px;
}
.bones-th, .bones-tr {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 8px;
  padding: 4px 10px;
  border-radius: 5px;
}
.bones-th {
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: .06em;
  font-size: 10px;
}
.bones-tr:nth-child(odd) { background: rgba(255, 255, 255, .025); }
.bones-tr .dim { color: var(--text-faint); }

/* ----------------------------------------------------------------- limbo */
.limbo-readout { text-align: center; padding: 42px 0; }
.limbo-readout .mult {
  font-family: var(--mono); font-size: 68px; font-weight: 700; line-height: 1;
  color: var(--gold-bright); text-shadow: 0 0 40px var(--gold-glow);
  font-variant-numeric: tabular-nums;
}

/* ----------------------------------------------------------------- mines */
.mine-grid {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 9px;
  max-width: 420px; margin: 10px auto; width: 100%;
}
.tile {
  aspect-ratio: 1; border-radius: var(--radius-sm);
  background: linear-gradient(180deg, #151f26, #0d1318);
  border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  font-size: 24px; cursor: pointer; user-select: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
  transition: transform .1s, background .15s, border-color .15s;
}
.tile:hover:not(.done):not(:disabled) {
  background: linear-gradient(180deg, #1b2831, #111a20);
  border-color: var(--gold-line); transform: translateY(-2px);
}
.tile.gem {
  background: linear-gradient(180deg, var(--felt-hi), var(--felt));
  border-color: #255374; color: var(--win);
  box-shadow: 0 0 18px rgba(53, 217, 160, .18);
}
.tile.mine {
  background: linear-gradient(180deg, #162733, #0e1a22);
  border-color: #27465c; color: var(--danger);
}
.tile.dim { opacity: .3; }
.tile:disabled { cursor: default; }

/* ----------------------------------------------------------------- crash */
.crash-stage { position: relative; flex: 1; display: flex; flex-direction: column; }
.crash-mult {
  text-align: center; padding: 30px 0 8px;
  font-family: var(--mono); font-size: 70px; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--gold-bright); text-shadow: 0 0 44px var(--gold-glow);
}
.crash-mult.running { color: var(--win); text-shadow: 0 0 44px rgba(53, 217, 160, .45); }
.crash-mult.busted { color: var(--danger); text-shadow: none; }
.crash-status { text-align: center; color: var(--text-dim); min-height: 22px; }
.crash-curve { width: 100%; height: 140px; margin-top: 10px; }
.crash-hist { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 16px; }
.crash-hist .pill {
  font-family: var(--mono); font-size: 11px; padding: 4px 9px; border-radius: 999px;
  background: #0d1318; border: 1px solid var(--line); color: var(--text-dim);
}
.crash-hist .pill.hi { color: var(--gold-bright); border-color: var(--gold-line); }
.crash-hist .pill.lo { color: var(--danger); border-color: #273b4a; }

/* ---------------------------------------------------------------- tables */
table.grid { width: 100%; border-collapse: collapse; font-size: 13px; }
table.grid th {
  text-align: left; font-size: 9px; color: var(--text-faint);
  text-transform: uppercase; letter-spacing: .14em; font-weight: 600;
  padding: 8px 9px; border-bottom: 1px solid var(--line);
}
table.grid td {
  padding: 8px 9px; border-bottom: 1px solid var(--line-soft);
  font-family: var(--mono); font-size: 12px; font-variant-numeric: tabular-nums;
}
table.grid td.name { font-family: var(--sans); }
table.grid tr:last-child td { border-bottom: none; }
table.grid tbody tr:hover td { background: rgba(255, 255, 255, .02); }
.scroll-y { max-height: 360px; overflow-y: auto; }
.table-wrap { overflow-x: auto; }

/* ----------------------------------------------------------------- modal */
.modal-back {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(2, 3, 4, .82);
  backdrop-filter: blur(3px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 44px 16px; overflow-y: auto;
}
.modal {
  width: 100%; max-width: 580px;
  background: linear-gradient(180deg, #10181e 0%, #0a0f12 100%);
  border: 1px solid var(--gold-line);
  border-radius: var(--radius); padding: 22px;
  box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, .05);
}
.modal header { display: flex; align-items: center; margin-bottom: 16px; }
.modal header h2 { margin: 0; flex: 1; color: var(--gold-bright); }
.modal-tabs { display: flex; gap: 4px; margin-bottom: 16px; border-bottom: 1px solid var(--line); }
.modal-tabs button {
  border: none; background: transparent; color: var(--text-dim);
  border-bottom: 2px solid transparent; border-radius: 0; padding: 9px 12px;
}
.modal-tabs button.on { color: var(--gold-bright); border-bottom-color: var(--gold); }

/* ----------------------------------------------------------------- toast */
#toasts {
  position: fixed; bottom: 20px; right: 20px; z-index: 90;
  display: flex; flex-direction: column; gap: 9px;
}
.toast {
  background: linear-gradient(180deg, #121a20, #0c1217);
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: var(--radius-sm); padding: 11px 16px;
  max-width: 350px; font-size: 13px;
  box-shadow: var(--shadow);
  animation: slidein .18s cubic-bezier(.22, 1, .36, 1);
}
.toast.bad { border-left-color: var(--danger); }
.toast.warn { border-left-color: var(--warn); }
@keyframes slidein { from { opacity: 0; transform: translateX(18px); } }

/* --------------------------------------------------------------- helpers */
.addr {
  font-family: var(--mono); font-size: 12px; word-break: break-all;
  background: var(--bg-input); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: 10px 12px; color: var(--gold-bright);
}
.row { display: flex; gap: 8px; align-items: center; }
.row.wrap { flex-wrap: wrap; }
.muted { color: var(--text-dim); }
.faint { color: var(--text-faint); }
.tag {
  font-size: 9px; padding: 3px 8px; border-radius: 999px;
  background: #0d1318; border: 1px solid var(--line); color: var(--text-dim);
  text-transform: uppercase; letter-spacing: .12em;
}
.tag.ok { color: var(--win); border-color: #255374; }
.tag.bad { color: var(--danger); border-color: #273b4a; }
.tag.warn { color: var(--warn); border-color: #265373; }
.banner {
  background: linear-gradient(180deg, #285371, #0c171f);
  border: 1px solid #265373; color: var(--warn);
  border-radius: var(--radius-sm); padding: 10px 14px; font-size: 12px; margin-bottom: 16px;
}
.lang-select { width: auto; padding: 4px 7px; font-size: 12px; flex: 0 0 auto; }
.hide { display: none !important; }

/* ----------------------------------------------------------------- slots */
/* The cabinet: a dark inset well with a gold bezel, sitting on felt. */
/* The machine is an object in a room, not a picture of one.
 *
 * The drums were already turning in 3D inside a cabinet that was flat, which is an odd
 * combination: the parts had depth and the thing containing them did not. So the cabinet
 * is a solid now — a front, a top and two sides, tipped back a few degrees so you are
 * looking slightly down at it the way you would at a machine on a floor.
 *
 * The tilt is deliberately small. Past about eight degrees the symbols start to shear and
 * the paylines stop reading, and a slot you cannot read is worse than a flat one.
 */
/* The room.
 *
 * This is the answer to "the background doesn't sell the 3D", and the cause was simple:
 * there was no background. The stage set a perspective and nothing else, so a carefully
 * lit cabinet sat on flat page colour with nothing behind it, nothing under it, and no
 * shadow where it met the ground. An object with no room around it reads as a sticker no
 * matter how good its own shading is — the eye judges depth from context before it judges
 * it from shading.
 *
 * Three layers, all painted here rather than in extra elements: a pool of light on the
 * back wall behind the machine, a floor that meets the wall on a horizon just below the
 * cabinet's base, and a vignette that closes the corners down.
 *
 * `--tilt-x` / `--tilt-y` come from the parallax module and are zero until it starts, so
 * every offset below reads as 0 on its own.
 */
.slot-stage {
  position: relative;
  /* A near eye point. This is the single number that decides how three-dimensional the
     whole thing looks: far away and a box is a rectangle, close up and you can see round
     it. 820px against a 660px cabinet is close enough to see the sides. */
  perspective: 820px;
  perspective-origin: calc(50% + var(--tilt-y, 0) * 0.6%) 28%;
  padding: 34px 0 54px;
  isolation: isolate;
  background:
    /* the vignette, closing the corners */
    radial-gradient(130% 100% at 50% 34%,
      rgba(0, 0, 0,0) 44%, rgba(0, 0, 0,.34) 76%, rgba(0, 0, 0,.62) 100%),
    /* the pool of light thrown on the wall behind the machine */
    radial-gradient(64% 46% at calc(50% + var(--tilt-y, 0) * 1.4%) 30%,
      rgba(32, 84, 121, .40) 0%, rgba(31, 84, 122, .20) 45%, rgba(0, 0, 0,0) 72%),
    /* the floor, and the horizon where it meets the wall */
    linear-gradient(180deg,
      #070a0c 0%, #090e12 52%, #0e1419 62%, #141e26 64%, #0c1115 100%);
  border-radius: 14px;
}

/* The contact shadow: the one thing that says the cabinet is standing on the floor rather
   than hovering over a picture of one. It sits under the machine, in front of the floor
   gradient and behind the cabinet. */
.slot-stage::after {
  content: '';
  position: absolute;
  left: 50%; bottom: 30px;
  width: min(74%, 520px); height: 42px;
  transform: translateX(-50%) translateX(calc(var(--tilt-y, 0) * -0.9px));
  background: radial-gradient(closest-side, rgba(0, 0, 0,.78), rgba(0, 0, 0,0));
  filter: blur(6px);
  pointer-events: none;
  z-index: 0;
}
.slot-cabinet {
  position: relative;
  max-width: 660px; margin: 0 auto; width: 100%;
  padding: 0 0 10px;
  border-radius: 18px 18px 12px 12px;
  transform-style: preserve-3d;
  /* The fixed 9-degree lean, plus wherever the viewer is. Each layer takes its own
     fraction of the same two properties, and that difference in rate between layers is
     what parallax is — the cabinet swings furthest, the glass a shade more, the wall
     behind it least. All of it reads as zero until the parallax module starts. */
  transform:
    rotateX(calc(9deg + var(--tilt-x, 0) * 1deg))
    rotateY(calc(var(--tilt-y, 0) * 1deg));
  background:
    repeating-linear-gradient(90deg,
      #141d24 0 14px, #0d151b 14px 28px),
    linear-gradient(180deg, #17222a, #090f14);
  border: 2px solid #1b6193;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0,.8) inset,
    0 30px 70px rgba(0, 0, 0,.7),
    inset 0 2px 0 rgba(255,255,255,.08);
}

/* The top and sides of the cabinet.
 *
 * These were hinged the wrong way round the first time: rotateX(90deg) about the top edge
 * swings a panel forward, not back, so the box was turned inside out and read as flat
 * because the faces you should see receding were coming at you instead. The top goes
 * back with -90, and each side goes back with a rotateY of the opposite sign to the edge
 * it hangs from. */
.cab-top {
  position: absolute; left: 0; right: 0; top: 0;
  height: 54px;
  transform-origin: 50% 0%;
  transform: rotateX(-90deg);
  border-radius: 18px 18px 0 0;
  background: linear-gradient(180deg, #215478 0%, #1b557e 55%, #1f547a 100%);
  box-shadow: inset 0 1px 0 rgba(90, 178, 242,.4);
}
.cab-side {
  position: absolute; top: 0; bottom: 0; width: 54px;
  background: linear-gradient(90deg, #235376, #140d06);
}
.cab-side.l { left: 0; transform-origin: 0% 50%; transform: rotateY(90deg); }
.cab-side.r { right: 0; transform-origin: 100% 50%; transform: rotateY(-90deg); }


/* ------------------------------------------------------------ the marquee */
.slot-marquee {
  position: relative;
  transform: translateZ(14px);
  padding: 12px 16px 10px;
  text-align: center;
  border-radius: 16px 16px 0 0;
  /* The arch. A slot machine is recognisable from its silhouette before anything
     printed on it is legible, and the curved top is the silhouette. */
  background:
    radial-gradient(120% 100% at 50% 100%, rgba(90, 178, 242,.16), transparent 60%),
    linear-gradient(180deg, #1f547a 0%, #255374 100%);
  border-bottom: 2px solid #1b6193;
  clip-path: ellipse(140% 100% at 50% 100%);
}
.slot-marquee .mq-top,
.slot-marquee .mq-sub {
  display: block;
  font: 700 10px/1.4 var(--sans, system-ui);
  letter-spacing: .34em;
  color: #3e95d4;
}
.slot-marquee .mq-main {
  display: block;
  font: 800 25px/1.15 var(--serif, Georgia, serif);
  letter-spacing: .07em;
  background: linear-gradient(180deg, #5ab2f2 0%, #58afed 48%, #1b6ba5 100%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  text-shadow: 0 2px 0 rgba(0, 0, 0,.45);
}

/* --------------------------------------------------------------- the body */
.slot-body {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 12px 12px;
  transform-style: preserve-3d;
}
/* The window the drums sit behind: a deep gold bezel around a dark recess. */
/* The GL cabinet (public/cabinet3d.js).
 *
 * Two canvases: the housing behind the drums, the glass in front of them. While the
 * class is on, the CSS cabinet's own paint — its wood, its bezel, its glass gradient — is
 * turned off and nothing else: every rule stays, so a context that fails to start leaves
 * the machine exactly as it was. The marquee text, the lever, the hubs, the payline and
 * the sparks are all still CSS, and sit above the housing canvas.
 */
.cab-gl {
  position: absolute; inset: 0;
  z-index: 1;
  pointer-events: none;
  display: block;
  border-radius: 18px 18px 12px 12px;
}
.cab-glass {
  position: absolute; inset: 0;
  z-index: 4;
  pointer-events: none;
  display: block;
  /* Adds light and never takes it: a symbol cannot be hidden by the glass. */
  mix-blend-mode: plus-lighter;
}
.slot-stage .slot-cabinet.cabgl {
  background: none;
  border-color: transparent;
  /* No box shadow: the GL case is a rounded pillow, and a rectangle's shadow under it
     would give the box away. The stage's contact shadow is the one it stands on. */
  box-shadow: none;
}
.slot-cabinet.cabgl .cab-top,
.slot-cabinet.cabgl .cab-side { display: none; }
.slot-cabinet.cabgl .slot-marquee,
.slot-cabinet.cabgl .slot-body,
.slot-cabinet.cabgl .slot-base { position: relative; z-index: 2; }
.slot-cabinet.cabgl .slot-marquee { background: none; box-shadow: none; border-bottom: none; clip-path: none; }
.slot-cabinet.cabgl .slot-window { background: none; box-shadow: none; border: none; border-radius: 28px; }
.slot-cabinet.cabgl .slot-base,
.slot-cabinet.cabgl .slot-tray { background: none; box-shadow: none; border-color: transparent; }
.slot-cabinet.cabgl .reel > .reel-glass { visibility: hidden; }
/* Room either side of the window for the LED strips and the thicker bezel, a deck under
   it deep enough for the button, and the marquee title lit in the theme's colours - the
   palette in slotthemes.js sets the custom properties on the cabinet. */
.slot-cabinet.cabgl { max-width: 720px; }
.slot-cabinet.cabgl .slot-body { padding: 14px 60px 12px; }
.slot-cabinet.cabgl .slot-body { position: relative; }
/* Out of the flow, hung off the case's right edge, so the window keeps the whole width
   between the paddings and the LEDs have their room. */
.slot-cabinet.cabgl .slot-lever {
  position: absolute; right: -28px; top: 18%; margin: 0;
  filter: drop-shadow(4px 8px 14px rgba(0, 0, 0, .75));
}
.slot-cabinet.cabgl .slot-base { min-height: 64px; margin: 0 16px; cursor: pointer; }
/* The reel canvas covers the window box; four px of padding puts its edge exactly on
   the bezel's inner lip, which frames the reels four px out. With the old nine the drums
   ran under the frame and the top row was cut by nothing visible. */
.slot-cabinet.cabgl .slot-window { padding: 4px; }
.slot-cabinet.cabgl .slot-marquee { padding: 16px 16px 14px; }
.slot-cabinet.cabgl .mq-main {
  background: linear-gradient(180deg, var(--mq-a, #5ab2f2) 0%, var(--mq-b, #58afed) 48%, var(--mq-c, #1b6ba5) 100%);
  -webkit-background-clip: text; background-clip: text;
  filter: drop-shadow(0 0 10px var(--mq-glow, rgba(90, 178, 242, .6)));
}
.slot-cabinet.cabgl .mq-top,
.slot-cabinet.cabgl .mq-sub { color: var(--mq-small, #3e95d4); }
/* A phone has no width to spare for LEDs: the window takes it, and the strips are
   simply not built when there is no room for them. */
@media (max-width: 560px) {
  .slot-cabinet.cabgl .slot-body { padding: 10px 26px 10px; }
  .slot-cabinet.cabgl .slot-lever { right: -14px; }
  .slot-cabinet.cabgl .slot-base { min-height: 52px; }
}

.slot-window {
  flex: 1 1 auto; min-width: 0;
  padding: 9px;
  border-radius: 10px;
  background: linear-gradient(180deg, #05080a, #030506);
  border: 3px solid transparent;
  background-clip: padding-box;
  box-shadow:
    0 0 0 3px #1b6193,
    0 0 0 4px #165683,
    0 0 0 6px rgba(90, 178, 242, .22),
    inset 0 10px 26px rgba(0, 0, 0,.9);
}
/* The drums stay on the face of the cabinet.
 *
 * Pushing them back in Z to sit inside a recess does not work: the cabinet's own painted
 * front is an element at z=0, so anything behind it is simply occluded by it and the
 * window goes black. CSS cannot punch a hole through a background. The depth here comes
 * from the bezel standing proud instead, which costs nothing and cannot hide the game.
 */
.slot-window {
  transform-style: preserve-3d;
  transform: translateZ(10px);
}
.slot-window > .reels { flex: 1 1 auto; min-width: 0; }
/* ---------------------------------------------------------------- the arm */
.slot-lever {
  flex: 0 0 auto;
  transform: translateZ(26px);
  width: 44px; height: 195px;
  cursor: pointer;
  /* Hangs off the side of the cabinet, the way the real one is bolted on. */
  margin-right: -14px;
}
.slot-lever svg { width: 100%; height: 100%; display: block; overflow: visible; }
/* The arm swings about its pivot, which is the circle at the bottom. */
.slot-lever .lever-arm {
  transform-origin: 24px 162px;
  transition: transform .18s cubic-bezier(.3, 1.6, .5, 1);
}
.slot-lever:hover .lever-arm { transform: rotate(6deg); }
.slot-lever.pulled .lever-arm { transform: rotate(74deg); }

/* --------------------------------------------------------------- the base */
.slot-base {
  margin: 0 12px;
  padding: 8px;
  border-radius: 8px;
  background: linear-gradient(180deg, #17222a, #090f14);
  border-top: 2px solid #1b6193;
}
/* The payout tray: a dark slot you would fish coins out of. */
.slot-tray {
  height: 16px;
  border-radius: 4px;
  background: linear-gradient(180deg, #020405 0%, #060a0d 60%, #0c141a 100%);
  box-shadow: inset 0 4px 10px rgba(0, 0, 0,.95), 0 1px 0 rgba(255,255,255,.05);
}

@media (max-width: 560px) {
  .slot-marquee .mq-main { font-size: 19px; }
  .slot-lever { width: 24px; height: 140px; margin-right: -10px; }
  .slot-body { gap: 6px; padding: 10px 8px 8px; }
}

.reels {
  position: relative;
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px;
  padding: 10px;
  border-radius: 10px;
  background: linear-gradient(180deg, #070b0e, #040608);
  box-shadow: inset 0 6px 18px rgba(0, 0, 0, .8);
}
/* ---------------------------------------------------------------- the drums
 *
 * Each reel is a real cylinder rather than a strip of squares: fifteen faces arranged
 * around an axis, turned with rotateX, seen through a window. The symbols above and
 * below the payline are the ones genuinely next to the result on that reel's strip, so
 * the drum is not a decoration over the maths -- it is the maths, drawn.
 *
 * --cell is the size of one symbol in pixels, set from JS after layout, because
 * translateZ cannot take a percentage. The radius that puts fifteen faces of that height
 * around a circle is (cell / 2) / tan(12deg), and 1 / (2 * tan 12) is 2.3523.
 */
.reel {
  position: relative;
  aspect-ratio: 1 / 3;
  overflow: hidden;
  border-radius: 8px;
  perspective: calc(var(--cell, 60px) * 2.15);
  perspective-origin: 50% 50%;
  background: linear-gradient(180deg, #05080a 0%, #0c1116 22%, #131a20 50%, #0c1116 78%, #05080a 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .05);
}
/* The mount holds the drum's axis at the window plane, so the front face sits flush
   with the glass instead of looming out of it. */
.drum-mount {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  transform: translateZ(calc(var(--cell, 60px) * -2.3523));
}
.drum {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(0deg);
}
.face {
  position: absolute;
  left: 0; right: 0;
  height: var(--cell, 60px);
  top: calc(50% - var(--cell, 60px) / 2);
  display: flex; align-items: center; justify-content: center;
  backface-visibility: hidden;
  transform:
    rotateX(calc(var(--i) * 24deg))
    translateZ(calc(var(--cell, 60px) * 2.3523));
  /* Flat, not graded.
   *
   * A gradient per face was the thing stopping this reading as a drum: every panel ran
   * light to dark on its own, so the eye saw a stack of tiles with repeating banding
   * rather than one surface curving away. On a real reel the panels are printed the same
   * and it is the cylinder that shades them — which is what --shade and the glass do
   * here, both of which vary with where the face actually sits on the barrel.
   *
   * The seam stays. Printed drums have one between panels, and without it the symbols
   * float on a blank band. */
  background: #11191f;
  border-bottom: 1px solid rgba(0, 0, 0, .45);
}
.face svg { width: 68%; height: 68%; display: block; }
/* Faces further round the barrel sit deeper in shadow, so the drum has a lit side. */
.face::before {
  content: ''; position: absolute; inset: 0;
  background: #000;
  opacity: var(--shade, 0);
  pointer-events: none;
}

/* A winning symbol. The glow has to sit on the face itself so it travels with the
   curve rather than floating flat in front of it. */
.face.win {
  background: linear-gradient(180deg, #285371 0%, #255374 50%, #1d1609 100%);
  box-shadow: inset 0 0 22px rgba(40, 147, 224, .35);
  animation: winpulse .85s ease-in-out infinite alternate;
}
@keyframes winpulse {
  from { filter: brightness(1); }
  to { filter: brightness(1.28); }
}

/* The glass: what makes a set of flat faces read as one solid barrel. The drum supplies
   the geometry, this supplies the light -- dark where the cylinder curves away at the
   top and bottom, a specular band across the middle, and shade down both sides. */
.reel-glass {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 5;
  /* In front of the drum, so it slides across it: a highlight that moved with the barrel
     would not read as a pane of glass over one. */
  transform: translateX(calc(var(--tilt-y, 0) * 0.55px));
  border-radius: 8px;
  background:
    linear-gradient(90deg, rgba(0, 0, 0,.55) 0%, rgba(0, 0, 0,0) 14%,
                           rgba(0, 0, 0,0) 86%, rgba(0, 0, 0,.55) 100%),
    linear-gradient(180deg,
      rgba(0, 0, 0,.99) 0%, rgba(0, 0, 0,.90) 5%, rgba(0, 0, 0,.58) 13%, rgba(0, 0, 0,.20) 25%,
      rgba(255,255,255,.06) 38%, rgba(255,255,255,.13) 45%, rgba(255,255,255,.28) 50%,
      rgba(255,255,255,.11) 55%, rgba(255,255,255,.04) 62%,
      rgba(0, 0, 0,.20) 75%, rgba(0, 0, 0,.58) 87%, rgba(0, 0, 0,.90) 95%, rgba(0, 0, 0,.99) 100%);
  box-shadow: inset 0 0 18px rgba(0, 0, 0, .7);
}
/* The axle the drums are keyed to.
 *
 * A tall ellipse down each side read as a blade rather than a wheel, so this is what is
 * actually visible on the real machine: a narrow dark divider between the reels, with the
 * end of the shaft showing at the centre line. The roundness comes from the drum itself;
 * the hub only has to say there is a shaft through it.
 */
.reel-cap {
  position: absolute; top: 0; bottom: 0; width: 6px;
  z-index: 6; pointer-events: none;
  background: linear-gradient(90deg, rgba(0, 0, 0,.92), rgba(28,34,40,.95) 50%, rgba(0, 0, 0,.92));
}
.reel-cap.l { left: -3px; }
.reel-cap.r { right: -3px; }
/* The shaft end: a machined boss on the centre line, lit from the top left like
   everything else on the cabinet. */
.reel-cap::after {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 12px; height: 12px; margin: -6px 0 0 -6px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 34% 30%, #f2f7fb, #94a3b0 42%, #47535e 68%, #10161b);
  box-shadow: 0 0 5px rgba(0, 0, 0,.9), inset 0 0 0 1px rgba(0, 0, 0,.5);
}
/* A slot cut across the boss, so it reads as turned metal rather than a bead. */
.reel-cap::before {
  content: ''; position: absolute; left: 50%; top: 50%;
  width: 12px; height: 2px; margin: -1px 0 0 -6px;
  z-index: 1;
  background: rgba(10,14,18,.75);
  border-radius: 1px;
}
.reel.spinning .reel-cap::before { animation: hubturn .62s linear infinite; }
@keyframes hubturn {
  from { transform: rotate(0deg); }
  to   { transform: rotate(-360deg); }
}

/* Spinning and landing are rotations of the drum, not slides of a strip. Down, because
   that is the way a reel turns. */
.reel.spinning .drum { animation: drumspin .62s linear infinite; }
@keyframes drumspin {
  from { transform: rotateX(0deg); }
  to   { transform: rotateX(-360deg); }
}
.reel.spinning .face { filter: blur(1.1px) brightness(.82); }
/* Coming in from above and settling with a little bounce, the way a real drum does when
   the brake bites. Same direction as the spin, so it does not look like it reverses. */
.reel.landing .drum { animation: drumland .42s cubic-bezier(.17, 1.3, .45, 1); }
@keyframes drumland {
  from { transform: rotateX(48deg); }
  to   { transform: rotateX(0deg); }
}

/* The valve: the wheel on the side of the cabinet that turns while the drums do. It is
   dressing, and it is the part that says this is a machine and not a table. */

/* The WebGL reels.
 *
 * They sit where the DOM drums are and take their place when the context is available;
 * the DOM ones stay in the markup as the fallback and are simply hidden, so nothing has
 * to be rebuilt if the renderer cannot start. */
.slot-gl {
  position: absolute; inset: 0;
  z-index: 3;
  pointer-events: none;
  display: block;
}
/* In WebGL mode the CSS drum is replaced by the canvas — but `visibility` inherits, so
 * hiding the whole reel took the glass, the dividers and the hubs down with it. Every
 * specular-glass, side-vignette and axle rule above was therefore only ever visible on the
 * fallback path nobody sees, which is why the barrels read as bare tubes with black voids
 * between them. Only the drum itself is hidden; the parts that sit *in front* of it, which
 * is what the canvas cannot draw, stay.
 */
.slot-window.gl .reel { visibility: hidden; }
/* A wider window. The reel box's shape decides the drum cell's: three rows fill the
   window's height whatever it is, so a taller window makes a narrower drum and a
   symbol that fills only two thirds of its row. At 1 : 2.4 the cell is nearly square on
   the drum and the symbol takes almost all of it. */
.slot-window.gl .reel { aspect-ratio: 1 / 2.4; }
/* What stands where a WebGL table should be when the browser will not give one: a
   sentence, not a flat imitation of the table. Inside the slot window it covers the
   window; elsewhere it is the block the renderer would have been. */
.gl-notice {
  display: flex; align-items: center; justify-content: center; text-align: center;
  min-height: 160px; padding: 24px;
  color: #59b2f2; font: 600 14px/1.5 var(--sans, system-ui);
  background: rgba(0, 0, 0, .55); border-radius: 10px;
}
.slot-window > .gl-notice { position: absolute; inset: 0; z-index: 9; min-height: 0; }
.slot-window.gl .reel > .reel-glass,
.slot-window.gl .reel > .reel-cap { visibility: visible; }

/* A rendered symbol fills its face: it is a photograph with its own black ground, not
   line art that needs a margin. */
.face .sym-img {
  width: 100%; height: 100%;
  display: block;
  object-fit: cover;
}

/* The effects canvas. Above the drums and their glass, below nothing: it is the top of
   the machine and it never takes a click. */
.slot-fx {
  position: absolute; inset: 0;
  z-index: 8;
  pointer-events: none;
}

/* SVG overlay that draws the winning paylines across the reels. */
.payline-layer { position: absolute; inset: 10px; pointer-events: none; overflow: visible; }
.payline-layer path {
  fill: none; stroke: var(--gold); stroke-width: 2.5; stroke-linecap: round;
  stroke-linejoin: round; opacity: .85;
  filter: drop-shadow(0 0 6px var(--gold-glow));
  stroke-dasharray: 700; stroke-dashoffset: 700;
  animation: drawline .55s ease-out forwards;
}
@keyframes drawline { to { stroke-dashoffset: 0; } }

.slot-banner {
  text-align: center; font-family: var(--mono); font-size: 24px;
  min-height: 34px; padding: 10px 0 4px; letter-spacing: .02em;
  font-variant-numeric: tabular-nums; color: var(--text-dim);
}
.slot-banner.win { color: var(--win); text-shadow: 0 0 26px rgba(53, 217, 160, .4); }
.slot-banner.free {
  color: var(--gold-bright); text-shadow: 0 0 26px var(--gold-glow);
  animation: freepulse 1s ease-in-out infinite alternate;
}
@keyframes freepulse { from { opacity: .72; } to { opacity: 1; } }

/* Big-win curtain, shown only above a threshold so it keeps its impact. */
.bigwin {
  position: absolute; inset: 0; z-index: 5;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; border-radius: var(--radius);
  background: radial-gradient(circle at 50% 45%, rgba(40, 147, 224, .22), rgba(4, 6, 7, .93) 68%);
  animation: bigwinIn .35s ease-out;
}
.bigwin .label {
  font-family: var(--serif); font-size: 15px; letter-spacing: .34em;
  text-transform: uppercase; color: var(--gold);
}
.bigwin .amount {
  font-family: var(--mono); font-size: 52px; font-weight: 700; color: var(--gold-bright);
  text-shadow: 0 0 44px var(--gold-glow); font-variant-numeric: tabular-nums;
}
@keyframes bigwinIn { from { opacity: 0; transform: scale(.94); } }

.paytable-grid {
  display: grid; grid-template-columns: auto repeat(3, 1fr);
  gap: 7px 12px; font-size: 12px; align-items: center;
}
.paytable-grid .sym { display: flex; align-items: center; gap: 6px; }
.paytable-grid .sym svg { width: 28px; height: 28px; }
.paytable-grid .n {
  font-family: var(--mono); text-align: right; color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------ card games */
.felt {
  background:
    radial-gradient(620px 260px at 50% 0%, rgba(40, 147, 224, .07), transparent 70%),
    linear-gradient(180deg, var(--felt-hi), var(--felt));
  border: 1px solid var(--gold-line);
  border-radius: 12px; padding: 16px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), 0 14px 38px rgba(0, 0, 0, .5);
}
.hand { display: flex; flex-wrap: wrap; gap: 7px; justify-content: center; margin: 14px 0 4px; }
.card {
  width: var(--card-w); height: var(--card-h); border-radius: 8px; padding: 0;
  background: linear-gradient(170deg, #63b1e8 0%, #e8e2d5 100%);
  color: #0c1216;
  border: 1px solid #b8ae99;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  font-family: var(--serif); font-weight: 600; font-size: 19px; line-height: 1.05;
  cursor: pointer; user-select: none;
  box-shadow: 0 5px 14px rgba(0, 0, 0, .45), inset 0 1px 0 rgba(255, 255, 255, .8);
  transition: transform .12s cubic-bezier(.22, 1, .36, 1), box-shadow .12s, opacity .12s;
}
.card:hover:not(:disabled) {
  transform: translateY(-7px);
  box-shadow: 0 12px 24px rgba(0, 0, 0, .55), 0 0 0 1px var(--gold);
}
.card:disabled { cursor: default; }
.card.red { color: #1672b3; }
.card.selected {
  transform: translateY(-12px);
  box-shadow: 0 0 0 2px var(--gold), 0 14px 26px rgba(0, 0, 0, .55);
}
.card.muted { opacity: .32; }
.card .suit { font-size: 21px; }

/* The held hand, on an arc.
   Only the player's own hand gets this; the talon and the revealed bot hands stay
   flat. Each card pivots about a point well below itself, so a per-card rotation swings
   the whole row round one shared centre: that is the invisible circle, with no
   translate maths. 3deg per card is 13.5deg at the ends of ten, which reads as held with
   the rank corners still legible. Cards tuck behind the next by a few pixels the way a
   held hand overlaps, and never wrap, because an arc across two rows is nonsense.
   --i and --n arrive from app.js via setProperty (CSP blocks style attributes, not the
   CSSOM). Specificity is deliberate: .card:hover and .card.selected assign transform
   directly and would overwrite the rotation, so the base rule is .hand.hand--held .card,
   which outranks .selected and ties :hover:not(:disabled), and it sits after both so the
   tie goes its way; hover and selected here set --lift rather than transform. */
.hand--held { flex-wrap: nowrap; gap: 0; padding: 12px 6px 4px; }
.hand.hand--held .card {
  --off: calc(var(--i, 0) - (var(--n, 1) - 1) / 2);
  --rot: calc(var(--off) * 3deg);
  --lift: 0px;
  margin-left: -8px;
  transform-origin: 50% 140%;
  transform: rotate(var(--rot)) translateY(var(--lift));
  z-index: calc(var(--i, 0) + 1);
}
.hand.hand--held .card:first-child { margin-left: 0; }
.hand.hand--held .card:hover:not(:disabled) { --lift: -10px; z-index: 60; }
.hand.hand--held .card.selected { --lift: -14px; z-index: 61; }
@media (max-width: 560px) {
  /* Ten cards at 38px effective is wider than a phone; tuck them tighter. */
  .hand.hand--held .card { margin-left: -14px; }
}
.card.back {
  background: repeating-linear-gradient(45deg, #1a2b46 0 5px, #16243b 5px 10px);
  border-color: var(--line); color: transparent;
}
.trick-area {
  display: flex; gap: 14px; justify-content: center; align-items: flex-start;
  min-height: 108px; margin: 6px 0 10px;
}
.trick-slot { text-align: center; }
.trick-slot .who {
  font-size: 9px; color: var(--text-faint); text-transform: uppercase;
  letter-spacing: .14em; margin-bottom: 6px;
}
.trick-slot.winner .card { box-shadow: 0 0 0 2px var(--gold), 0 0 22px var(--gold-glow); }
.empty-slot {
  width: var(--card-w); height: var(--card-h); border-radius: 8px;
  border: 1px dashed rgba(40, 147, 224, .25);
  background: rgba(0, 0, 0, .18);
}
.trump-pick { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; }
.trump-pick button { font-size: 20px; min-width: 56px; padding: 10px 12px; }
.trump-pick button.on { border-color: var(--gold); color: var(--gold-bright); }
.score-row {
  display: flex; gap: 18px; justify-content: center;
  font-family: var(--mono); font-size: 13px; color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.score-row b { color: var(--gold-bright); }

/* ------------------------------------------------------------ responsive */
@media (max-width: 900px) {
  .layout { grid-template-columns: 1fr; }
  .crash-mult, .limbo-readout .mult { font-size: 48px; }
  /* The glyph dice, when WebGL is unavailable; the canvas scales itself. */
  .bones-flat { font-size: 60px; }
  .bigwin .amount { font-size: 38px; }
  header.top { gap: 8px; padding: 5px 12px; }
  /* On a phone the brand word goes before the bar is allowed to take a second line. */
  .brand #siteName { display: none; }
  .card { font-size: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; animation-iteration-count: 1 !important; }
}


/* ------------------------------------------------- slots: After Dark theme */
/* Only the cabinet dressing changes with the theme. The reel maths, paytable and
   published RTP are identical, so the skin is never a different game. */
body.afterdark .slot-cabinet {
  background:
    radial-gradient(520px 200px at 50% 0%, rgba(255, 79, 163, .16), transparent 70%),
    linear-gradient(180deg, #24123a 0%, #180c28 60%, #0d0616 100%);
  border-color: rgba(255, 79, 163, .30);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, .6) inset,
    0 18px 48px rgba(0, 0, 0, .6),
    inset 0 1px 0 rgba(255, 255, 255, .07);
}
body.afterdark .reels { background: linear-gradient(180deg, #0f0718, #070310); }
body.afterdark .face { background: #1a1026; }
body.afterdark .face.win {
  border-color: #ff4fa3;
  box-shadow: 0 0 22px rgba(255, 79, 163, .45), inset 0 0 18px rgba(255, 79, 163, .20);
}
body.afterdark .payline-layer path {
  stroke: #ff4fa3; filter: drop-shadow(0 0 6px rgba(255, 79, 163, .6));
}
body.afterdark .slot-banner.free { color: #ffa8d8; text-shadow: 0 0 26px rgba(255, 79, 163, .5); }
body.afterdark .bigwin {
  background: radial-gradient(circle at 50% 45%, rgba(255, 79, 163, .26), rgba(8, 4, 12, .93) 68%);
}
body.afterdark .bigwin .label { color: #ff8ecb; }
body.afterdark .bigwin .amount { color: #ffd9ec; text-shadow: 0 0 44px rgba(255, 79, 163, .55); }

/* ---------------------------------------------------------------- puzzle */
/* The artwork sits underneath and never moves; the grid on top is a set of covers that
   are removed as pieces are uncovered. Slicing an image into tiles would misalign the
   moment the container resized, so nothing is sliced. */
.puzzle-frame {
  position: relative; width: 100%; max-width: 440px; margin: 0 auto;
  aspect-ratio: 1; border-radius: 12px; overflow: hidden;
  border: 1px solid var(--gold-line);
  box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, .06);
}
.puzzle-frame.wide { max-width: 560px; aspect-ratio: 4 / 3; }
.puzzle-frame.widest { max-width: 620px; aspect-ratio: 5 / 3; }
.puzzle-art { position: absolute; inset: 0; }
.puzzle-art svg { width: 100%; height: 100%; display: block; }
/* Pieces are SVG paths, not boxes, so the cut can interlock. */
.puzzle-pieces { position: absolute; inset: 0; width: 100%; height: 100%; }
.puzzle-pieces .piece {
  fill: #10191f;
  stroke: #06080a;
  stroke-width: 2;
  cursor: pointer;
  transition: opacity .35s ease, fill .2s;
}
.puzzle-pieces .piece:hover { fill: #1b2832; }
.puzzle-pieces .piece.open { opacity: 0; pointer-events: none; }
.puzzle-pieces .piece.cracked {
  fill: #3a1620; stroke: #2c5a7b; stroke-width: 3;
  cursor: default; pointer-events: none;
}
.puzzle-status {
  text-align: center; font-family: var(--mono); font-size: 20px;
  padding: 8px 0 10px; color: var(--gold-bright);
  font-variant-numeric: tabular-nums;
}
.puzzle-status.lost { color: var(--danger); }
.puzzle-status.won { color: var(--win); }
.ladder-list { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.ladder-list span {
  font-family: var(--mono); font-size: 11px; padding: 3px 7px; border-radius: 999px;
  background: #0d1318; border: 1px solid var(--line-soft); color: var(--text-dim);
}
.ladder-list span.done { color: var(--win); border-color: #255374; }
.ladder-list span.now { color: var(--gold-bright); border-color: var(--gold); }

/* ----------------------------------------------------------------- sound */
.sound-toggle {
  font-size: 15px; line-height: 1; padding: 6px 10px; min-width: 36px;
  color: var(--gold); border: 1px solid var(--gold-line); background: var(--bg-input);
}
.sound-toggle.muted { color: var(--text-faint); border-color: var(--line); }
.sound-toggle.music-off { color: var(--text-dim); }

/* ----------------------------------------------- slots: Golden Ring theme */
/* Khokhloma lacquerware: black ground, red and gold. Cosmetic only, like every
   other theme; the reel strips, paytable and RTP are shared. */
body.russian .slot-cabinet {
  background:
    radial-gradient(520px 200px at 50% 0%, rgba(90, 178, 242, .14), transparent 70%),
    linear-gradient(180deg, #2a0f0a 0%, #1a0906 60%, #0d0402 100%);
  border-color: rgba(46, 154, 231, .34);
}
body.russian .reels { background: linear-gradient(180deg, #120604, #1f547a); }
body.russian .face { background: #1f1009; }
body.russian .face.win {
  border-color: #35a1ef;
  box-shadow: 0 0 22px rgba(53, 161, 239, .45), inset 0 0 18px rgba(23, 129, 204, .22);
}
body.russian .payline-layer path {
  stroke: #35a1ef; filter: drop-shadow(0 0 6px rgba(53, 161, 239, .6));
}
body.russian .bigwin {
  background: radial-gradient(circle at 50% 45%, rgba(53, 161, 239, .26), rgba(26, 85, 127, .93) 68%);
}
body.russian .bigwin .label { color: #35a1ef; }
body.russian .bigwin .amount { color: #5ab2f2; text-shadow: 0 0 44px rgba(53, 161, 239, .55); }

/* Knife and Smoke: lamplight in a grey room. Almost no colour, which is the point. */
body.noir .slot-cabinet {
  background:
    radial-gradient(520px 210px at 50% 0%, rgba(214, 226, 240, .10), transparent 72%),
    linear-gradient(180deg, #16191e 0%, #0e1115 62%, #06080a 100%);
  border-color: rgba(158, 172, 188, .26);
}
body.noir .reels { background: linear-gradient(180deg, #0c0e12, #05070a); }
body.noir .face { background: #14181e; }
body.noir .face.win {
  border-color: #cfd8e4;
  box-shadow: 0 0 22px rgba(207, 216, 228, .32), inset 0 0 18px rgba(255, 122, 60, .16);
}
body.noir .payline-layer path {
  stroke: #cfd8e4; filter: drop-shadow(0 0 6px rgba(207, 216, 228, .5));
}
body.noir .slot-banner.free { color: #5ab2f2; text-shadow: 0 0 22px rgba(255, 122, 60, .4); }
body.noir .bigwin {
  background: radial-gradient(circle at 50% 45%, rgba(207, 216, 228, .2), rgba(5, 7, 10, .94) 68%);
}
body.noir .bigwin .label { color: #cfd8e4; }
body.noir .bigwin .amount { color: #f2f6fb; text-shadow: 0 0 44px rgba(207, 216, 228, .45); }

/* The Couch: a night sky seen through a consulting-room window. */
body.couch .slot-cabinet {
  background:
    radial-gradient(520px 210px at 50% 0%, rgba(143, 166, 232, .16), transparent 72%),
    linear-gradient(180deg, #131a33 0%, #0b1022 60%, #05070f 100%);
  border-color: rgba(143, 166, 232, .3);
}
body.couch .reels { background: linear-gradient(180deg, #0a0e1c, #05070f); }
body.couch .face { background: #121728; }
body.couch .face.win {
  border-color: #8fa6e8;
  box-shadow: 0 0 22px rgba(143, 166, 232, .42), inset 0 0 18px rgba(90, 178, 242, .18);
}
body.couch .payline-layer path {
  stroke: #8fa6e8; filter: drop-shadow(0 0 6px rgba(143, 166, 232, .6));
}
body.couch .slot-banner.free { color: #5ab2f2; text-shadow: 0 0 22px rgba(90, 178, 242, .4); }
body.couch .bigwin {
  background: radial-gradient(circle at 50% 45%, rgba(143, 166, 232, .24), rgba(5, 7, 15, .93) 68%);
}
body.couch .bigwin .label { color: #8fa6e8; }
body.couch .bigwin .amount { color: #e8eeff; text-shadow: 0 0 44px rgba(143, 166, 232, .55); }

/* ------------------------------------------------------------ site token */
.phrase-box {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px;
  background: var(--bg-input); border: 1px solid var(--gold-line);
  border-radius: var(--radius-sm); padding: 12px; margin: 10px 0;
}
.phrase-box span {
  font-family: var(--mono); font-size: 12px; color: var(--gold-bright);
  padding: 4px 6px; border-radius: 4px; background: rgba(40, 147, 224, .07);
}
.phrase-box span b { color: var(--text-faint); font-weight: 400; margin-right: 5px; }
.verify-bar {
  height: 6px; border-radius: 3px; background: var(--bg-input);
  border: 1px solid var(--line); overflow: hidden; margin: 10px 0;
}
.verify-bar i { display: block; height: 100%; background: var(--gold); width: 0; transition: width .1s; }
.verify-result { font-size: 13px; padding: 10px 12px; border-radius: var(--radius-sm); margin-top: 10px; }
.verify-result.ok { background: #2a526f; border: 1px solid #255374; color: var(--win); }
.verify-result.bad { background: #12202a; border: 1px solid #295270; color: var(--danger); }

/* ---------------------------------------------------------------- arcade */
.cabinets { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 14px; }
.cabinet {
  background: linear-gradient(180deg, #1b1a28 0%, #101018 100%);
  border: 1px solid #2f2d44; border-radius: var(--radius);
  padding: 16px; cursor: pointer;
  transition: border-color .15s, transform .12s, box-shadow .15s;
}
.cabinet:hover {
  border-color: #5fd6ff; transform: translateY(-3px);
  box-shadow: 0 10px 30px rgba(95, 214, 255, .14);
}
.cabinet h4 {
  margin: 0 0 6px; font-family: var(--serif); font-size: 16px; color: #8fe3ff;
}
.cabinet .blurb { font-size: 12px; color: var(--text-dim); min-height: 32px; }
.cabinet .marquee {
  height: 54px; border-radius: 6px; margin-bottom: 10px;
  background: linear-gradient(180deg, #24243a, #0e0e18);
  border: 1px solid #343252;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--mono); font-size: 22px; color: #5fd6ff;
  text-shadow: 0 0 14px rgba(95, 214, 255, .55);
}
.cabinet .board { margin-top: 10px; font-size: 11px; }
.cabinet .board div { display: flex; justify-content: space-between; color: var(--text-faint); }
.arcade-screen {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.arcade-screen canvas {
  max-width: 100%; height: auto; border-radius: 10px;
  border: 1px solid #2f2d44; background: #0a1420;
  box-shadow: 0 16px 44px rgba(0, 0, 0, .6);
  touch-action: none;
}
.arcade-hud {
  display: flex; gap: 18px; font-family: var(--mono); font-size: 13px;
  color: var(--text-dim); font-variant-numeric: tabular-nums;
}
.arcade-hud b { color: #8fe3ff; }
.arcade-controls { font-size: 12px; color: var(--text-faint); text-align: center; }
/* What the billiards table has to say when it is drawn in WebGL, where there is no text on
   the canvas: the last shot, and the score. */
.arcade-caption {
  font-family: var(--mono); font-size: 12px; color: #59b2f2; text-align: center;
  min-height: 16px; font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ radio */
.stations {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px;
}
.stations button {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  text-align: left;
  padding: 10px 12px;
  line-height: 1.35;
}
.stations button strong { font-size: 13px; letter-spacing: .02em; }
.stations button span { font-size: 11px; opacity: .65; font-weight: 400; }
.stations button.on {
  border-color: var(--gold);
  box-shadow: inset 0 0 0 1px var(--gold);
}

/* ----------------------------------------------------------------- matches */
.challenges { display: flex; flex-direction: column; gap: 6px; }
.challenge {
  display: grid;
  /* minmax(0, 1fr) rather than 1fr: a plain 1fr will not shrink below its content, so a
     long username pushes the stake and the button off a narrow screen. */
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.challenge > div:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.challenge .hint { display: inline; }

@media (max-width: 420px) {
  /* At phone width the stake and the button get their own line rather than squeezing the
     names into nothing. */
  .challenge { grid-template-columns: minmax(0, 1fr) auto; }
  .challenge button { grid-column: 1 / -1; }
}

.match-screen { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.match-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: min(100%, 520px);
  font-size: 13px;
  color: var(--text-dim);
}
.match-bar .mono { color: var(--text); letter-spacing: .04em; }

.board-host { width: min(100%, 520px); }
.board-host:has(.tron) { width: min(100%, 768px); }
.tron { display: flex; flex-direction: column; align-items: center; gap: 6px; width: 100%; }
.tron-arena {
  width: 100%; max-width: 768px; aspect-ratio: 3 / 2; display: block;
  background: #050508; border: 1px solid #1c2a3a; border-radius: 6px; touch-action: none;
}
.tron-status { min-height: 1.4em; color: #bfe6ff; }
.chessboard {
  position: relative;
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  aspect-ratio: 1;
  width: 100%;
  border: 1px solid var(--gold-line);
  border-radius: 6px;
  overflow: hidden;
  user-select: none;
}
.chessboard .sq {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.chessboard .sq.light { background: #2a2f38; }
.chessboard .sq.dark { background: #1b1f26; }
.chessboard .sq.last { box-shadow: inset 0 0 0 100px rgba(40, 147, 224, .09); }
.chessboard .sq.selected { box-shadow: inset 0 0 0 2px var(--gold); }
.chessboard .sq.target::after,
.chessboard .sq.capture::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}
.chessboard .sq.target::after {
  width: 26%; height: 26%;
  background: rgba(40, 147, 224, .45);
}
.chessboard .sq.capture::after {
  width: 84%; height: 84%;
  border: 3px solid rgba(40, 147, 224, .55);
}
/* One atlas, six pieces across by two rows: white on top, black beneath, in the order
   k q r b n p. The column comes from the piece class and the row from the colour class,
   so a square never needs to know anything but which two classes to put on a span.

   These replaced Unicode glyphs, which were whatever the player's font happened to
   have — a colour emoji on some phones, a box on Linux — and which mixed the outlined
   white series with the filled black one while the comment here claimed otherwise. */
.chessboard .pc {
  width: 92%; height: 92%;
  background-image: url('/textures/chess-pieces.png');
  background-size: 600% 200%;
  background-repeat: no-repeat;
  pointer-events: none;
}
.chessboard .pc.white { background-position-y: 0; }
.chessboard .pc.black { background-position-y: 100%; }
.chessboard .pc-k { background-position-x: 0%; }
.chessboard .pc-q { background-position-x: 20%; }
.chessboard .pc-r { background-position-x: 40%; }
.chessboard .pc-b { background-position-x: 60%; }
.chessboard .pc-n { background-position-x: 80%; }
.chessboard .pc-p { background-position-x: 100%; }

/* The promotion picker uses the same sprites, as buttons rather than spans. */
.promo .pc {
  width: clamp(30px, 9vw, 52px); aspect-ratio: 1;
  background-image: url('/textures/chess-pieces.png');
  background-size: 600% 200%;
  background-repeat: no-repeat;
  pointer-events: auto;
}
.chessboard .rank, .chessboard .file {
  position: absolute;
  font-size: 9px;
  font-style: normal;
  opacity: .4;
  pointer-events: none;
}
.chessboard .rank { top: 2px; left: 3px; }
.chessboard .file { bottom: 1px; right: 3px; }

.chessboard .promo {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  justify-content: center;
  gap: 6px;
  padding: 8px;
  background: rgba(10, 13, 18, .94);
  border-top: 1px solid var(--gold-line);
}
.chessboard .promo button { padding: 4px 10px; font-size: 26px; line-height: 1; }
.chessboard .promo .promo-cancel { font-size: 18px; opacity: .6; }

.movelist {
  max-height: 220px;
  overflow-y: auto;
  margin-top: 10px;
  font-size: 12px;
  font-family: ui-monospace, monospace;
}
.movelist > div {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr) minmax(0, 1fr);
  gap: 4px;
  padding: 2px 0;
}
.movelist > div > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ------------------------------------------------------------- sea battle */
.seabattle { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.sea-title { margin: 0; font-size: 13px; letter-spacing: .06em; text-transform: uppercase; color: var(--text-dim); }
.sea-pair { display: flex; flex-wrap: wrap; gap: 18px; justify-content: center; }
.sea-side { display: flex; flex-direction: column; align-items: center; gap: 6px; }

.sea-grid {
  display: grid;
  grid-template-columns: 14px repeat(10, 1fr);
  gap: 1px;
  width: min(100%, 320px);
}
.sea-grid .coord {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 8px;
  font-style: normal;
  opacity: .45;
}
.sea-cell {
  aspect-ratio: 1;
  background: #161b22;
  border: 1px solid #232a33;
  border-radius: 2px;
}
.sea-grid.live .sea-cell:not(.hit):not(.miss) { cursor: crosshair; }
.sea-grid.live .sea-cell:not(.hit):not(.miss):hover { background: #22303f; }
.sea-cell.ship { background: #3a4756; border-color: #4d5c6d; }
.sea-cell.sunk { background: #2a465a; border-color: #7a3a3a; }
.sea-cell.miss { background: #12161c; }
.sea-cell.miss::after {
  content: '';
  display: block;
  margin: 40% auto;
  width: 20%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(255, 255, 255, .28);
}
.sea-cell.hit { background: #7a2f2f; border-color: #a04141; }
.sea-cell.hit::after { content: '\00d7'; display: block; text-align: center; font-size: 12px; line-height: 1; color: #5ab2f2; }

.sea-left { font-size: 11px; color: var(--text-dim); letter-spacing: .06em; }
.fleet-row { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.ship-chip { display: flex; gap: 2px; padding: 5px 6px; }
.ship-chip .seg { width: 8px; height: 8px; background: #4d5c6d; border-radius: 1px; }
.ship-chip.on { border-color: var(--gold); }
.ship-chip.on .seg { background: var(--gold); }

/* ---------------------------------------------------------------- balda */
.balda { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.balda-grid {
  display: grid;
  grid-template-columns: repeat(var(--n, 5), 1fr);
  gap: 4px;
  width: min(100%, 340px);
}
.balda-cell {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 600 clamp(16px, 5.5vw, 26px)/1 ui-monospace, monospace;
  background: #161b22;
  border: 1px solid #232a33;
  border-radius: 4px;
  color: var(--text-dim);
}
.balda-cell.filled { background: #2a323d; border-color: #3a4552; color: var(--text); }
.balda-cell.open { border-color: var(--gold-line); cursor: pointer; }
.balda-cell.open:hover { background: #22303f; }
.balda-cell.picked { border-color: var(--gold); box-shadow: inset 0 0 0 1px var(--gold); }

.balda-controls {
  display: flex; flex-wrap: wrap; gap: 6px; justify-content: center;
  max-width: min(100%, 340px);
}
.balda-letter { width: 48px; flex: 0 0 auto; text-align: center; text-transform: uppercase; }
.balda-word { flex: 1 1 140px; min-width: 0; }

.balda-scores { display: flex; align-items: baseline; gap: 10px; font-size: 13px; }
.balda-scores .v { font: 600 20px/1 ui-monospace, monospace; }

.balda-usedwrap { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; justify-content: center; max-width: min(100%, 340px); }
.balda-used { display: flex; flex-wrap: wrap; gap: 4px; }
.balda-used .chip {
  font-size: 11px;
  padding: 2px 6px;
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--text-dim);
}

/* ---------------------------------------------------------------- durak */
.durak { display: flex; flex-direction: column; align-items: center; gap: 10px; width: 100%; }
.durak-seats { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.durak-seat {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 6px 12px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel); min-width: 62px;
}
.durak-seat.defending { border-color: var(--gold); }
.durak-seat .count { font: 600 18px/1 ui-monospace, monospace; }

.durak-deck { display: flex; align-items: center; gap: 10px; font-size: 11px; color: var(--text-dim); }
.durak-table {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;
  min-height: 92px; align-items: center;
  padding: 10px; border: 1px dashed var(--line); border-radius: 10px;
  width: min(100%, 520px);
}
.durak-table .pair { position: relative; width: 46px; height: 68px; }
.durak-table .pair .card { position: absolute; inset: 0; }
.durak-table .pair .beat { transform: translate(9px, 11px) rotate(8deg); }

.durak-hand { display: flex; flex-wrap: wrap; gap: 5px; justify-content: center; max-width: min(100%, 560px); }
.durak-status { min-height: 16px; }
.durak-actions { gap: 6px; }

.card {
  width: var(--card-w); height: var(--card-h); border-radius: 5px;
  background: #deacb2; border: 1px solid #b9b0a0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  font-family: ui-monospace, monospace; line-height: 1; user-select: none;
}
.card .r { font-size: 17px; font-weight: 700; }
.card .s { font-size: 15px; }
.card.red { color: #2277b3; }
.card.black { color: #14161a; }
.card.dim { opacity: .45; }
.card.playable { cursor: pointer; box-shadow: 0 0 0 1px var(--gold-line); }
.card.playable:hover { transform: translateY(-4px); }
.card.picked { box-shadow: 0 0 0 2px var(--gold); transform: translateY(-6px); }
.card.trump-card { transform: rotate(90deg) scale(.8); }

/* ---------------------------------------------------------------- poker */
.poker { display: flex; flex-direction: column; align-items: center; gap: 10px; width: 100%; }
.poker-head { display: flex; gap: 14px; align-items: baseline; font-size: 11px; color: var(--text-dim); }
.poker-head .v { font: 600 15px/1 ui-monospace, monospace; color: var(--gold); }

.poker-seats { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.poker-seat {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 6px 12px; min-width: 74px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
}
.poker-seat.acting { border-color: var(--gold); box-shadow: 0 0 12px rgba(40, 147, 224,.25); }
.poker-seat.out { opacity: .4; }
.poker-seat .chips { font: 600 16px/1 ui-monospace, monospace; }
.poker-seat .bet { font-size: 11px; color: var(--gold); }

.poker-board { display: flex; gap: 6px; justify-content: center; }
.poker-hole { display: flex; gap: 6px; justify-content: center; }
.poker-showdown { display: flex; flex-direction: column; gap: 4px; align-items: center; font-size: 12px; }
.poker-showdown .row { align-items: center; gap: 6px; }
.poker-actions { flex-wrap: wrap; gap: 6px; justify-content: center; max-width: min(100%, 560px); }
.poker-slider { width: 130px; }

.card.back {
  background: repeating-linear-gradient(45deg, #2a3242 0 4px, #222a38 4px 8px);
  border-color: #3a4457;
}
.card.small { width: 32px; height: 46px; }
.card.small .r { font-size: 12px; }
.card.small .s { font-size: 11px; }

/* ------------------------------------------------------- touch and phones */
/*
 * Hover on a touch screen is a state you get stuck in.
 *
 * Nothing on this site is hover-gated — every hover rule is decoration and the click
 * underneath always works — but a tap on a phone leaves the hovered look painted on until
 * something else is tapped, so a played card stays lifted and a tile stays lit. These are
 * the rules whose effect is visible enough to matter, switched off where there is no
 * pointer to hover with. `hover: none` rather than `pointer: coarse`: a laptop with a
 * touchscreen has both, and it should keep its hover states.
 */
@media (hover: none) {
  .tile:hover:not(.done):not(:disabled) { transform: none; }
  .card:hover:not(:disabled) { transform: none; }
  .card.playable:hover { transform: none; }
  .cabinet:hover { transform: none; box-shadow: none; }
  .slot-lever:hover .lever-arm { transform: none; }
  .puzzle-pieces .piece:hover { fill: inherit; }
  .sea-grid.live .sea-cell:not(.hit):not(.miss):hover { background: inherit; }
  .balda-cell.open:hover { background: inherit; }
  table.grid tbody tr:hover td { background: transparent; }
  /* A crosshair means nothing without a cursor to shape. */
  .sea-grid.live .sea-cell:not(.hit):not(.miss) { cursor: default; }
}

/*
 * A fixed background attachment forces a repaint of the whole page on every scroll frame,
 * and on iOS Safari it is one of the reliable ways to make a page feel slow. The look is
 * worth nothing on a screen this size.
 */
@media (max-width: 900px) {
  body { background-attachment: scroll; }
}

/*
 * On a phone the games come first.
 *
 * `.layout` collapses to one column below 900px, and the bet panel is first in source
 * order, so the thing you came to play was below the controls for it — you scrolled past
 * the stake field to reach the game. Order is a visual concern, not a structural one, so
 * the markup keeps its reading order and the grid reverses it.
 */
@media (max-width: 900px) {
  .layout { display: flex; flex-direction: column; }
  .layout > .stage-col { order: 1; }
  .layout > .controls-col { order: 2; }
}

/*
 * Tap targets. A sea-battle cell lands at about 29px on a 390px screen against a 44px
 * minimum, which is why it is easy to fire at the wrong square. The grid keeps its shape
 * and gets a floor instead, scrolling sideways in its own box if it has to rather than
 * pushing the page wide.
 */
@media (max-width: 560px) {
  .sea-pair { overflow-x: auto; }
  .sea-grid { grid-template-columns: 16px repeat(10, minmax(30px, 1fr)); }
  .sea-grid .coord { font-size: 10px; }
}

/*
 * The notch. `viewport-fit=cover` lets the page use the full screen, which then needs the
 * insets honoured by hand or the sticky header sits under the status bar.
 */
@supports (padding: env(safe-area-inset-top)) {
  header.top {
    padding-left: max(18px, env(safe-area-inset-left));
    padding-right: max(18px, env(safe-area-inset-right));
  }
  .wrap {
    padding-left: max(16px, env(safe-area-inset-left));
    padding-right: max(16px, env(safe-area-inset-right));
    padding-bottom: max(20px, env(safe-area-inset-bottom));
  }
}

/* ------------------------------------------------------------ the card table ---
   The felt and the trick, drawn in WebGL. Your hand is DOM, below it — see the note at
   the top of cards3d.js for why a card you must pick for money stays a real button. */
.card-stage {
  width: 100%;
  border-radius: 12px;
  overflow: hidden;
  margin-bottom: 10px;
  background: #0a0e0b;
  box-shadow: inset 0 2px 14px rgba(0, 0, 0, .6);
}
.cards-canvas { display: block; width: 100%; }

/* ------------------------------------------------- the Мины gem and bomb ---

   Two halves of one atlas (public/textures/mine-icons.png, gem at x=0, bomb at the
   right), placed by background-position so both tiles share a single request.

   They are ray traced by tools/generate-materials.js rather than drawn as glyphs: the
   old ◆ and ✕ were whatever the player's font happened to have, which on a phone could
   be a coloured emoji and on Linux frequently a box. These are the same object on every
   machine, lit by the same key direction as the dice and the cards. */
.mine-icon {
  display: block;
  width: 78%; aspect-ratio: 1;
  background-image: url('/textures/mine-icons.png');
  background-size: 200% 100%;
  background-repeat: no-repeat;
  /* The tile already animates its own transform on hover; this rides along. */
  pointer-events: none;
}
.mine-icon.gem { background-position: 0 0; }
.mine-icon.bomb { background-position: 100% 0; }

/* A found gem lands rather than appears: the scale settles, so a run of safe picks
   reads as a sequence of events instead of the board silently changing colour. */
.tile.gem .mine-icon { animation: mine-pop .28s cubic-bezier(.2, 1.5, .4, 1); }
.tile.mine .mine-icon { animation: mine-shake .34s ease-out; }

@keyframes mine-pop {
  from { transform: scale(.3); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}
@keyframes mine-shake {
  0% { transform: scale(1.35) rotate(-7deg); opacity: 0; }
  40% { opacity: 1; }
  60% { transform: scale(1) rotate(4deg); }
  100% { transform: scale(1) rotate(0); }
}

/* Motion is a nicety, not the information: the icon itself says what happened. */
@media (prefers-reduced-motion: reduce) {
  .tile.gem .mine-icon, .tile.mine .mine-icon { animation: none; }
}

/* Test machine: ocean. */
body.ocean .slot-cabinet {
  background:
    radial-gradient(520px 210px at 50% 0%, rgba(79, 209, 232, .16), transparent 72%),
    linear-gradient(180deg, #0f2d47 0%, #08192a 60%, #040c15 100%);
  border-color: rgba(79, 209, 232, .3);
}
body.ocean .reels { background: linear-gradient(180deg, #08192a, #040c15); }
body.ocean .face { background: #0e2a42; }
body.ocean .face.win {
  border-color: #4fd1e8;
  box-shadow: 0 0 22px rgba(79, 209, 232, .42), inset 0 0 18px rgba(79, 209, 232, .18);
}
body.ocean .payline-layer path { stroke: #4fd1e8; filter: drop-shadow(0 0 6px rgba(79, 209, 232, .6)); }
body.ocean .slot-banner.free { color: #4fd1e8; text-shadow: 0 0 22px rgba(79, 209, 232, .4); }
body.ocean .bigwin {
  background: radial-gradient(circle at 50% 45%, rgba(79, 209, 232, .24), rgba(4, 6, 8, .93) 68%);
}
body.ocean .bigwin .label { color: #4fd1e8; }
body.ocean .bigwin .amount { color: #e6fbff; text-shadow: 0 0 44px rgba(79, 209, 232, .55); }

/* Test machine: jungle. */
body.jungle .slot-cabinet {
  background:
    radial-gradient(520px 210px at 50% 0%, rgba(74, 222, 128, .16), transparent 72%),
    linear-gradient(180deg, #10301c 0%, #08190f 60%, #040d07 100%);
  border-color: rgba(74, 222, 128, .3);
}
body.jungle .reels { background: linear-gradient(180deg, #08190f, #040d07); }
body.jungle .face { background: #10301c; }
body.jungle .face.win {
  border-color: #4ade80;
  box-shadow: 0 0 22px rgba(74, 222, 128, .42), inset 0 0 18px rgba(74, 222, 128, .18);
}
body.jungle .payline-layer path { stroke: #4ade80; filter: drop-shadow(0 0 6px rgba(74, 222, 128, .6)); }
body.jungle .slot-banner.free { color: #4ade80; text-shadow: 0 0 22px rgba(74, 222, 128, .4); }
body.jungle .bigwin {
  background: radial-gradient(circle at 50% 45%, rgba(74, 222, 128, .24), rgba(4, 6, 8, .93) 68%);
}
body.jungle .bigwin .label { color: #4ade80; }
body.jungle .bigwin .amount { color: #eafff0; text-shadow: 0 0 44px rgba(74, 222, 128, .55); }

/* Test machine: volcano. */
body.volcano .slot-cabinet {
  background:
    radial-gradient(520px 210px at 50% 0%, rgba(255, 106, 43, .16), transparent 72%),
    linear-gradient(180deg, #2e120a 0%, #170704 60%, #0a0302 100%);
  border-color: rgba(255, 106, 43, .3);
}
body.volcano .reels { background: linear-gradient(180deg, #170704, #0a0302); }
body.volcano .face { background: #2e120a; }
body.volcano .face.win {
  border-color: #ff6a2b;
  box-shadow: 0 0 22px rgba(255, 106, 43, .42), inset 0 0 18px rgba(255, 106, 43, .18);
}
body.volcano .payline-layer path { stroke: #ff6a2b; filter: drop-shadow(0 0 6px rgba(255, 106, 43, .6)); }
body.volcano .slot-banner.free { color: #ff6a2b; text-shadow: 0 0 22px rgba(255, 106, 43, .4); }
body.volcano .bigwin {
  background: radial-gradient(circle at 50% 45%, rgba(255, 106, 43, .24), rgba(4, 6, 8, .93) 68%);
}
body.volcano .bigwin .label { color: #ff6a2b; }
body.volcano .bigwin .amount { color: #fff0e6; text-shadow: 0 0 44px rgba(255, 106, 43, .55); }

.bet-state{display:inline-block;margin-left:8px;padding:1px 8px;border-radius:999px;font-size:11px;font-weight:700;background:#222;color:#ddd}
.bet-state.open{background:#7a5a0d;color:#ffe08a}
.bet-state.won{background:#0f5a2a;color:#9dffc0}
.bet-state.lost{background:#6a0d12;color:#ffb3b0}
.bet-state.void{background:#333;color:#ccc}
