/* KOLOPOLY styles. Kijitech playbook: mobile first, self hosted fonts, one
   type scale, warm ink and cream and gold, money always exact.
   The game board is landscape first: on a wide screen the square board sits
   left with a rail of players, wallet and actions on the right. Portrait
   phones run one view only: the full board in a square, full-bleed stage
   (KP10). */
@font-face {
  font-family: 'Aeonik Pro';
  src: url('/fonts/aeonik-pro-bold.woff2') format('woff2'), url('/fonts/aeonik-pro-bold.woff') format('woff');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Figtree';
  src: url('/fonts/figtree-variable.woff2') format('woff2');
  font-weight: 300 900; font-style: normal; font-display: swap;
}
:root {
  --ink: #2b2018;
  --ink-soft: #5c4c3d;
  --cream: #f5efe2;
  --paper: #fffaf0;
  --card: #fffdf7;
  --gold: #ffc857;
  --gold-deep: #e8a90c;
  --green: #1d9e6c;
  --green-deep: #0f7a50;
  --red: #d64545;
  --red-deep: #b02e2e;
  --line: #e3d7c3;
  --board-green: #2e6b46;
  --fs-display: 34px; --fs-headline: 26px; --fs-title: 20px;
  --fs-lead: 16px; --fs-body: 14px; --fs-caption: 12px; --fs-meta: 11px;
  --radius: 16px;
  --shadow: 0 10px 30px rgba(43, 32, 24, 0.16);
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --btn-h: 43px; --btn-h-s: 38px; --chip-h: 28px;
  --topbar-h: 59px;
  --rail-w: 344px;
}
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { margin: 0; padding: 0; }
body {
  font-family: 'Figtree', system-ui, sans-serif;
  font-size: var(--fs-body);
  color: var(--ink);
  background: var(--cream);
  min-height: 100dvh;
}
h1, h2, h3, .display, .headline, .title { font-family: 'Aeonik Pro', 'Figtree', sans-serif; margin: 0; }
.display { font-size: var(--fs-display); line-height: 1.05; }
.headline { font-size: var(--fs-headline); line-height: 1.12; }
.title { font-size: var(--fs-title); line-height: 1.2; }
.lead { font-size: var(--fs-lead); }
.caption { font-size: var(--fs-caption); color: var(--ink-soft); }
.meta { font-size: var(--fs-meta); color: var(--ink-soft); }
button { font-family: inherit; cursor: pointer; }
/* Taps must land the first time: no double-tap zoom delay or zoom on rapid
   dice taps, and no tap highlight flash on any control. */
button, .btn, .mode-pick, .slot, .chip, input, select, textarea { touch-action: manipulation; }
input, select { font-family: inherit; font-size: var(--fs-lead); color: var(--ink); }
.ic { vertical-align: -4px; flex: none; }

/* Header */
.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 10px;
  padding: 10px max(14px, env(safe-area-inset-right)) 10px max(14px, env(safe-area-inset-left)); height: var(--topbar-h);
  background: rgba(245, 239, 226, 0.94); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.wordmark { font-family: 'Aeonik Pro', sans-serif; font-size: 22px; letter-spacing: 0.5px; }
.wordmark .gold { color: var(--gold-deep); }
.topbar .spacer { flex: 1; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--paper); border: 1px solid var(--line); border-radius: 999px;
  padding: 3px 10px; min-height: var(--chip-h); font-size: 12px; font-weight: 700;
}
/* KP9: the room chip carries the running match clock while a timed game
   plays; under a minute left the time turns urgent. */
.chip .chip-time { font-variant-numeric: tabular-nums; font-weight: 800; }
.chip .chip-time.urgent { color: var(--red-deep); }

.iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 12px;
  background: var(--paper); border: 1px solid var(--line); color: var(--ink);
}
/* KP14: the one audio button's lit and silenced states. State 1 (sound
   + voice) wears the gold chip; state 3 (all off) drops muted, so the
   state reads at a glance even before the glyph registers. */
#muteBtn.on { background: #fdf0cd; border-color: var(--gold); color: var(--gold-deep); }
#muteBtn.off { color: var(--ink-soft); opacity: 0.62; }

/* Buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: none; border-radius: 14px; padding: 10px 14px; min-height: var(--btn-h);
  font-size: 15px; font-weight: 800;
  background: linear-gradient(180deg, #ffd97a, var(--gold));
  color: var(--ink);
  box-shadow: 0 3px 0 var(--gold-deep), 0 6px 14px rgba(232, 169, 12, 0.25);
}
.btn:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--gold-deep); }
.btn:disabled { opacity: 0.45; box-shadow: none; transform: none; }
.btn.green { background: linear-gradient(180deg, #27b57c, var(--green)); color: #fff; box-shadow: 0 3px 0 var(--green-deep); }
.btn.red { background: linear-gradient(180deg, #e25858, var(--red)); color: #fff; box-shadow: 0 3px 0 var(--red-deep); }
.btn.ghost { background: var(--paper); color: var(--ink); border: 1px solid var(--line); box-shadow: none; }
.btn.dark { background: linear-gradient(180deg, #3d2d1e, var(--ink)); color: var(--cream); box-shadow: 0 3px 0 #120d09; }
.btn.small { padding: 7px 10px; min-height: var(--btn-h-s); font-size: 13px; border-radius: 11px; }
.btn.block { width: 100%; }
/* Button type pass (KP8 compact density, owner correction): primaries are
   43px tall with 15px labels, chips and pills 28px with 12px labels.
   Hit comfort comes from the 6px dead gaps between controls, not from
   inflating every control to desktop size. (KP9 later replaced the 38px
   text utilities with a 48px icon-over-label bar, and took the stage
   full-bleed; see the KP9 notes in the portrait block.) */
.sheet .btn, .modal-card .btn { font-size: 15px; }
@media (hover: hover) { .btn:hover { filter: brightness(1.05); } }

/* Landing */
.view { display: none; }
.view.active { display: block; }
.landing-hero {
  position: relative; overflow: hidden;
  padding: 26px 18px 8px; text-align: center;
}
.hero-board {
  position: absolute; inset: -40px -20px auto -20px; height: 250px; opacity: 0.16;
  display: grid; grid-template-columns: repeat(8, 1fr); gap: 5px; transform: rotate(-4deg);
  pointer-events: none;
}
.hero-board i { border-radius: 8px; background: linear-gradient(160deg, #3c8560, var(--board-green)); box-shadow: inset 0 2px 0 rgba(255,255,255,0.35); }
.hero-board i:nth-child(3n) { background: linear-gradient(160deg, #ffd97a, var(--gold)); }
.hero-board i:nth-child(4n) { background: linear-gradient(160deg, #e25858, var(--red)); }
.hero-board i:nth-child(5n) { background: linear-gradient(160deg, #5b97d6, #3f7fbf); }
.landing-hero .display { position: relative; font-size: 44px; letter-spacing: 1px; text-shadow: 0 3px 0 rgba(232, 169, 12, 0.3); }
.landing-hero .tagline { position: relative; margin-top: 6px; }
.token-rain { position: relative; display: flex; justify-content: center; gap: 12px; margin: 14px 0 2px; color: var(--ink-soft); }
.token-rain .tokart { filter: drop-shadow(0 4px 4px rgba(43, 32, 24, 0.28)); }
.join-card {
  position: relative;
  margin: 12px 14px 30px; background: var(--card); border: 1px solid var(--line);
  border-radius: 22px; padding: 18px; box-shadow: var(--shadow);
}
.join-card label { display: block; font-weight: 800; font-size: var(--fs-caption); letter-spacing: 0.4px; text-transform: uppercase; color: var(--ink-soft); margin: 14px 0 7px; }
.join-card label:first-child { margin-top: 0; }
.textin {
  width: 100%; padding: 13px 14px; border-radius: 13px;
  border: 1.5px solid var(--line); background: var(--paper);
}
.textin:focus { outline: 2px solid var(--gold); border-color: transparent; }
.token-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.token-pick {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 10px 4px; border-radius: 13px; border: 1.5px solid var(--line);
  background: var(--paper); font-size: 13px; font-weight: 700; color: var(--ink);
}
.token-pick .tokart { filter: drop-shadow(0 3px 3px rgba(43, 32, 24, 0.25)); }
.token-pick.sel { border-color: var(--gold-deep); background: #fff3d1; box-shadow: 0 0 0 2px var(--gold); }
.mode-row { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.mode-pick {
  padding: 11px; border-radius: 13px; border: 1.5px solid var(--line); background: var(--paper);
  text-align: left; color: var(--ink);
}
.mode-pick.sel { border-color: var(--gold-deep); background: #fff3d1; box-shadow: 0 0 0 2px var(--gold); }
.mode-pick .t { font-weight: 800; font-size: var(--fs-body); display: flex; gap: 6px; align-items: center; }
.mode-pick .d { font-size: var(--fs-caption); color: var(--ink-soft); margin-top: 2px; display: block; }
.toggle-row {
  display: flex; align-items: center; gap: 10px; padding: 10px 2px; border-bottom: 1px dashed var(--line);
}
.toggle-row:last-of-type { border-bottom: none; }
.toggle-row .grow { flex: 1; }
.toggle-row .t { font-weight: 700; }
.switch { position: relative; width: 46px; height: 27px; flex: none; }
.switch input { opacity: 0; width: 100%; height: 100%; position: absolute; margin: 0; }
.switch .track {
  position: absolute; inset: 0; border-radius: 999px; background: #d8cbb2; transition: background 0.15s;
}
.switch .track::after {
  content: ''; position: absolute; top: 3px; left: 3px; width: 21px; height: 21px; border-radius: 50%;
  background: #fff; transition: left 0.15s; box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.switch input:checked + .track { background: var(--green); }
.switch input:checked + .track::after { left: 22px; }
.join-row { display: flex; gap: 8px; }
.join-row .textin { text-transform: uppercase; letter-spacing: 3px; font-weight: 800; text-align: center; }
.banner {
  margin: 12px 14px 0; padding: 12px 14px; border-radius: 14px;
  background: #fff3d1; border: 1px solid var(--gold); font-weight: 600;
  display: none;
}
.banner.show { display: block; }
.invite-title { font-family: 'Aeonik Pro', sans-serif; font-size: var(--fs-title); }
.invite-row { display: flex; align-items: center; gap: 10px; margin: 8px 0 12px; }
.invite-row .grow { flex: 1; min-width: 0; }
.invite-token { flex: none; display: inline-flex; }
.invite-as { font-weight: 800; }
.welcome-title { font-family: 'Aeonik Pro', sans-serif; font-size: var(--fs-title); margin-top: 2px; }
.overline { font-size: var(--fs-meta); font-weight: 900; letter-spacing: 1.4px; color: var(--gold-deep); }
.divider { display: flex; align-items: center; gap: 10px; margin: 18px 0 4px; color: var(--ink-soft); font-size: var(--fs-caption); font-weight: 700; }
.divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: var(--line); }

/* Lobby */
.lobby-wrap { padding: 16px 14px 40px; max-width: 560px; margin: 0 auto; }
.code-hero { text-align: center; padding: 10px 0 4px; }
.code-big { font-family: 'Aeonik Pro', sans-serif; font-size: 52px; letter-spacing: 10px; color: var(--ink); }
.invite-actions { display: flex; gap: 8px; justify-content: center; margin: 10px 0; flex-wrap: wrap; }
.qr-box { display: flex; justify-content: center; padding: 8px 0; }
.qr-box svg { width: 148px; height: 148px; }
.slots { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin: 14px 0; }
.bot-prompt {
  margin: 14px 0 0; padding: 14px; border-radius: 16px;
  background: #fff3d1; border: 1px solid var(--gold);
}
.bot-prompt .bp-title { font-family: 'Aeonik Pro', sans-serif; font-weight: 700; font-size: var(--fs-lead); margin-bottom: 4px; }
.bot-prompt .btnrow { margin-top: 10px; }
.bot-tag {
  display: inline-block; padding: 0 5px; border-radius: 999px;
  background: var(--ink); color: var(--gold);
  font-size: 10px; font-weight: 800; letter-spacing: 0.6px; vertical-align: 1px;
}
.slot {
  display: flex; align-items: center; gap: 10px; padding: 11px 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: 15px; min-height: 58px;
}
.slot.empty { border-style: dashed; color: var(--ink-soft); justify-content: center; font-weight: 600; }
.slot .avatar {
  width: 38px; height: 38px; border-radius: 12px; display: flex; align-items: center; justify-content: center;
  color: #fff; flex: none; box-shadow: inset 0 -3px 0 rgba(0,0,0,0.18);
}
.slot .nm { font-weight: 800; }
.settings-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 2px; }

/* Game layout: portrait stacks stage over rail; landscape is stage + rail */
.game-stage { padding: 8px 8px 0; }
.game-rail { display: flex; flex-direction: column; padding-bottom: calc(var(--wallet-h, 190px) + 36px); }
.rail-top { display: flex; flex-direction: column; gap: 8px; padding: 8px 8px 4px; min-height: 0; }
.msgbar {
  background: linear-gradient(180deg, #3d2d1e, var(--ink)); color: var(--cream); border-radius: 16px;
  padding: 9px 13px; font-weight: 600; min-height: 38px; display: flex; align-items: center; gap: 8px;
  box-shadow: 0 4px 10px rgba(43, 32, 24, 0.18);
}
.players-strip { display: flex; gap: 7px; flex-wrap: wrap; padding: 1px; }
/* KP12: the player pill compacts to one line (name, then exact cash):
   a 22px avatar disc, 13px name, 13px bold cash, about 34px all in. */
.pchip {
  flex: 1 1 128px; display: flex; align-items: center; gap: 6px;
  background: var(--card); border: 1.5px solid var(--line); border-radius: 999px; padding: 4px 10px 4px 5px;
  box-shadow: 0 2px 6px rgba(43, 32, 24, 0.07); min-height: 34px;
}
.pchip.turn { border-color: var(--gold-deep); box-shadow: 0 0 0 2px var(--gold); }
.pchip.bankrupt { opacity: 0.45; }
.pchip .avatar { width: 22px; height: 22px; border-radius: 50%; color: #fff; display: flex; align-items: center; justify-content: center; flex: none; box-shadow: inset 0 -2px 0 rgba(0,0,0,0.2); }
.pchip .nm { font-weight: 700; font-size: 13px; }
.pchip .cash { font-weight: 800; font-size: 13px; color: var(--green-deep); }
.pchip.jailed .nm::after { content: ' · Kirikiri'; color: var(--red-deep); }

/* Board */
.board-outer { position: relative; width: 100%; max-width: 640px; margin: 0 auto; }
.board-wrap {
  --frame: clamp(7px, 1.8vw, 13px);
  position: relative; width: 100%; aspect-ratio: 1 / 1; overflow: hidden;
  border-radius: 22px; padding: var(--frame);
  background: linear-gradient(145deg, #5d4128, #3a2817 55%, #2b1c0f);
  box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, 0.28), inset 0 -3px 8px rgba(0, 0, 0, 0.45);
  touch-action: manipulation;
}
.board {
  position: absolute; inset: var(--frame); display: grid;
  grid-template-columns: 1.6fr repeat(9, 1fr) 1.6fr;
  grid-template-rows: 1.6fr repeat(9, 1fr) 1.6fr;
  transform-origin: 0 0; transition: transform 0.45s ease;
  border-radius: 10px; background: #fdf6df;
  box-shadow: inset 0 0 0 2px #20402c, inset 0 0 46px rgba(46, 107, 70, 0.22);
  container-type: inline-size;
}
.bspace {
  position: relative; border: 0.5px solid rgba(43, 32, 24, 0.35);
  background: linear-gradient(180deg, #fffef9, #fdf3da);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9), inset 0 -2px 4px rgba(92, 76, 61, 0.10);
  overflow: hidden; cursor: pointer;
  display: flex; flex-direction: column; align-items: center;
}
@media (hover: hover) { .bspace:hover { filter: brightness(1.05); } }
.bspace .band {
  width: 100%; height: 19%; flex: none;
  background: linear-gradient(180deg, color-mix(in srgb, var(--band) 68%, #ffffff), var(--band) 58%, color-mix(in srgb, var(--band) 78%, #000000));
  box-shadow: inset 0 -2px 3px rgba(0, 0, 0, 0.20), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
.bspace .tile-art { flex: 1 1 0; min-height: 0; width: 100%; display: flex; align-items: center; justify-content: center; padding-top: 1px; }
.bspace .tileart { height: 100%; width: auto; max-width: 94%; }
.bspace .nm {
  font-size: 9.5px; font-size: clamp(7.5px, 2.3cqw, 13px);
  font-weight: 800; line-height: 1.06; text-align: center;
  padding: 1px 1px 0; letter-spacing: -0.2px; flex: none;
}
.bspace .pr {
  font-size: 9px; font-size: clamp(7px, 2cqw, 11.5px);
  font-weight: 700; color: var(--ink-soft); padding-bottom: 2px; flex: none;
}
.bspace .pr .pr-full { display: none; }
@container (min-width: 620px) {
  .bspace .pr .pr-full { display: inline; }
  .bspace .pr .pr-compact { display: none; }
}
.bspace.corner { background: linear-gradient(180deg, #fdf6df, #f3e3bc); }
.bspace.corner .nm { font-size: 10.5px; font-size: clamp(8.5px, 2.8cqw, 16px); padding-top: 2px; }
.bspace.owned { box-shadow: inset 0 0 0 2.5px var(--own), inset 0 1px 0 rgba(255, 255, 255, 0.9); }
.bspace.mortgaged { filter: grayscale(0.9); opacity: 0.62; }
.bspace.mortgaged::after {
  content: 'MORTGAGED'; position: absolute; top: 44%; left: 50%; transform: translate(-50%, -50%) rotate(-24deg);
  font-size: 7.5px; font-weight: 900; color: var(--red-deep); letter-spacing: 0.4px;
}
.bspace .ownbar { position: absolute; left: 0; right: 0; bottom: 0; height: 4px; }
.bspace .pips { display: flex; gap: 1px; justify-content: center; align-items: flex-end; height: 22%; flex: none; width: 100%; }
.bspace .pips svg { height: 100%; width: auto; max-width: 36%; filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.3)); }
.bspace .potamt { font-family: 'Aeonik Pro', sans-serif; font-size: 10.5px; font-size: clamp(8px, 2.4cqw, 13px); color: var(--gold-deep); flex: none; }
.bspace.legal { box-shadow: inset 0 0 0 3px var(--gold-deep), inset 0 0 12px rgba(232, 169, 12, 0.55); }
.bspace.flash { animation: spaceflash 0.7s ease; }
@keyframes spaceflash { 0%, 100% { filter: none; } 50% { filter: brightness(1.5); } }
.btokens { position: absolute; inset: 0; pointer-events: none; }
.btoken {
  --ts: clamp(26px, 9.2cqw, 42px);
  position: absolute; width: var(--ts); height: var(--ts);
  margin: calc(var(--ts) / -2) 0 0 calc(var(--ts) / -2);
  transition: left 0.2s ease, top 0.2s ease; z-index: 5;
}
.btoken .tok-base {
  position: absolute; left: 50%; bottom: 1%; transform: translateX(-50%);
  width: 76%; height: 30%; border-radius: 50%; opacity: 0.92;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.35), inset 0 -2px 3px rgba(0, 0, 0, 0.30), inset 0 2px 2px rgba(255, 255, 255, 0.45);
}
.btoken .tok-in { position: absolute; inset: 0; display: block; filter: drop-shadow(0 3px 3px rgba(20, 12, 4, 0.35)); }
.btoken .tok-in svg { width: 100%; height: 100%; display: block; }
.btoken .tok-in.hop { animation: tokenhop 0.23s ease; }
@keyframes tokenhop {
  0% { transform: translateY(0) scale(1, 1); }
  30% { transform: translateY(-34%) scale(0.94, 1.07); }
  55% { transform: translateY(-44%) scale(1, 1); }
  82% { transform: translateY(0) scale(1.13, 0.83); }
  100% { transform: translateY(0) scale(1, 1); }
}
.bcenter {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  background: radial-gradient(circle at 50% 32%, #fffaf0, #f3e7c8 80%);
  border: 0.5px solid rgba(43, 32, 24, 0.35); text-align: center; padding: 6px;
}
.bcenter .skyline { position: absolute; left: 0; right: 0; bottom: 0; height: 48%; pointer-events: none; }
.bcenter .skyline svg { width: 100%; height: 100%; display: block; }
.bcenter > *:not(.skyline) { position: relative; z-index: 1; }
.bcenter .logo {
  font-family: 'Aeonik Pro', sans-serif; letter-spacing: 1px; transform: rotate(-3.5deg);
  font-size: 20px; font-size: clamp(16px, 6cqw, 34px);
  text-shadow: 0 2px 0 rgba(232, 169, 12, 0.35);
}
.bcenter .logo .gold { color: var(--gold-deep); }
.bcenter .meta { font-size: 11px; font-size: clamp(8px, 2.2cqw, 12px); }
.bcenter .pot { font-family: 'Aeonik Pro', sans-serif; color: var(--gold-deep); font-size: 15px; font-size: clamp(13px, 4.2cqw, 22px); }
.pot-visual { display: flex; flex-direction: column; align-items: center; }
.pot-notes { height: 15px; display: flex; justify-content: center; align-items: flex-end; gap: 1px; }
.pot-notes i {
  width: 15px; width: clamp(10px, 3.4cqw, 18px); height: 9px; height: clamp(6px, 2cqw, 11px);
  background: linear-gradient(180deg, #2fbf71, #1d9e6c);
  border: 1px solid #0f7a50; border-radius: 1.5px; display: block;
}
.pot-notes i:nth-child(odd) { transform: rotate(-7deg); }
.pot-notes i:nth-child(even) { transform: rotate(6deg); }
.pot-notes i:nth-child(3n) { transform: rotate(-2deg) translateY(-2px); }
.pot-bowl {
  width: 56px; width: clamp(40px, 13cqw, 64px); height: 21px; height: clamp(15px, 5cqw, 24px);
  background: linear-gradient(180deg, #8a5a33, #5d3a1c);
  clip-path: polygon(0 0, 100% 0, 86% 100%, 14% 100%);
}
.pot-bowl span { display: block; height: 5px; background: linear-gradient(90deg, var(--gold-deep), var(--gold), var(--gold-deep)); }
.dice-row { display: flex; gap: 7px; }
.die {
  width: 31px; width: clamp(27px, 8.6cqw, 42px); height: 31px; height: clamp(27px, 8.6cqw, 42px);
  border-radius: 22%; padding: 13%;
  background: linear-gradient(145deg, #ffffff, #e9e0cb);
  border: 1px solid #c9b992;
  box-shadow: inset 0 -3px 6px rgba(43, 32, 24, 0.20), inset 0 2px 3px rgba(255, 255, 255, 0.95), 0 3px 6px rgba(43, 32, 24, 0.28);
  display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr);
}
.die i {
  width: 64%; aspect-ratio: 1; border-radius: 50%; place-self: center; visibility: hidden;
  background: radial-gradient(circle at 35% 32%, #57432c, #1d130a 72%);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.6);
}
.die i.on { visibility: visible; }
.dice-row.tumble .die { animation: dicetumble 0.5s cubic-bezier(0.2, 0.7, 0.3, 1.2); }
.dice-row.tumble .die:nth-child(2) { animation-delay: 0.07s; }
@keyframes dicetumble {
  0% { transform: rotate(0deg) translateY(0) scale(1); }
  30% { transform: rotate(24deg) translateY(-7px) scale(1.08); }
  60% { transform: rotate(-16deg) translateY(1px) scale(0.98); }
  100% { transform: rotate(0deg) translateY(0) scale(1); }
}
.deck-row { display: flex; gap: 7px; }
.deck-pile {
  font-size: 9.5px; font-size: clamp(8px, 2.3cqw, 11px); font-weight: 900; letter-spacing: 0.4px;
  padding: 5px 8px; padding: clamp(3px, 0.9cqw, 6px) clamp(5px, 1.6cqw, 10px);
  border-radius: 7px; color: #fff; border: 1px solid rgba(255, 255, 255, 0.45);
  box-shadow: 2px 2px 0 rgba(43, 32, 24, 0.35), 4px 4px 0 rgba(43, 32, 24, 0.16);
}
.deck-pile.wahala { background: linear-gradient(160deg, #e25858, var(--red)); }
.deck-pile.family { background: linear-gradient(160deg, #5b97d6, #3f7fbf); }


/* Wallet and action bar. KP12: this is now ONE floating glass dock.
   A deep brown-green frosted bar (blur, translucent tint pulled from
   the game's own palette) with layered soft shadows and inset
   hairline highlights instead of borders; cash, the primary pill with
   its timer ring, the ghost utilities and the state panels all live
   inside it. The detailed dock language sits in the KP12 section at
   the end of this file. */
.walletbar {
  position: fixed; left: 12px; right: 12px; bottom: 10px; z-index: 45;
  background: linear-gradient(180deg, rgba(44, 37, 22, 0.88), rgba(25, 22, 14, 0.93));
  backdrop-filter: blur(16px) saturate(1.3); -webkit-backdrop-filter: blur(16px) saturate(1.3);
  border: none; border-radius: 22px;
  box-shadow: 0 22px 44px rgba(20, 14, 5, 0.35), 0 4px 14px rgba(20, 14, 5, 0.22),
    inset 0 1px 0 rgba(255, 248, 232, 0.13), inset 0 0 0 1px rgba(255, 248, 232, 0.05);
  padding: 11px 12px calc(11px + env(safe-area-inset-bottom));
  color: var(--cream);
}
.wallet-inner { max-width: 620px; margin: 0 auto; }
.wallet-top { display: flex; align-items: center; gap: 9px; }
.actionbar { display: flex; gap: 8px; margin-top: 9px; align-items: center; }
.actionbar .btn { flex: 1; }
/* Utility bar: KP9 made it a bottom-nav icon bar; KP12 makes the items
   ghosts inside the glass dock. No boxes, no borders: an icon over an
   11px label on the glass itself, hairline dividers between items, and
   the armed two-tap state (Leave, End) fills a soft red capsule behind
   the item and swaps its label to "Sure?". */
.ubtn {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  min-height: 46px; padding: 5px 2px 4px;
  background: transparent; color: rgba(245, 239, 226, 0.88); border: none; border-radius: 999px;
  font-family: inherit; cursor: pointer; touch-action: manipulation;
  transition: background-color 0.18s ease, color 0.18s ease;
}
.ubtn + .ubtn { box-shadow: inset 1px 0 0 rgba(255, 248, 232, 0.09); }
.ubtn:active { transform: translateY(1px); background: rgba(255, 248, 232, 0.08); }
.ubtn .u-ic { display: flex; line-height: 0; }
.ubtn .u-lb { font-size: 11px; font-weight: 700; line-height: 1.1; white-space: nowrap; }
.ubtn.armed { color: #ffb4a6; background: rgba(208, 69, 69, 0.22); box-shadow: inset 0 0 0 1px rgba(255, 130, 115, 0.35); animation: ubtnIn 0.18s ease; }
@keyframes ubtnIn { from { opacity: 0.35; transform: translateY(2px); } to { opacity: 1; transform: none; } }
.actionbar .btn.fixed { flex: none; }
.turn-timer { font-variant-numeric: tabular-nums; font-weight: 800; color: var(--red-deep); }
/* Countdowns live in their own control beside frozen primary labels
   (Lesson 4). KP12: that control is the timer ring badge; it pulses
   with each beep in the last 10s (Lesson 7), like the chip did. */
.turn-timer.pulse { animation: clockpulse 0.32s ease; }
@keyframes clockpulse { 0% { transform: scale(1); } 40% { transform: scale(1.16); } 100% { transform: scale(1); } }

/* Smart auto-end countdown on the End turn button */
#actionBar { flex-wrap: wrap; }
#abEnd { position: relative; overflow: hidden; }
#abEnd .autoend-fill { position: absolute; left: 0; bottom: 0; height: 4px; background: var(--gold); pointer-events: none; }
.autoend-label { flex: 0 0 100%; text-align: center; font-size: var(--fs-caption); font-weight: 700; color: var(--gold-deep); }

/* Alerts: bank app style money cards */
.alerts {
  position: fixed; top: calc(var(--topbar-h) + 6px); left: 50%; transform: translateX(-50%);
  z-index: 80; width: min(92vw, 380px); display: flex; flex-direction: column; gap: 8px;
  pointer-events: none;
}
.alert-card {
  display: flex; flex-direction: column; align-items: stretch;
  background: #ffffff; border-radius: 16px; padding-bottom: 10px; overflow: hidden;
  box-shadow: 0 16px 38px rgba(43, 32, 24, 0.32);
  border-top: 5px solid var(--green);
  animation: alertin 0.28s ease;
}
.alert-card.debit { border-top-color: var(--red); }
.alert-bank { display: flex; align-items: center; gap: 7px; padding: 9px 13px 0; }
.alert-logo {
  width: 22px; height: 22px; border-radius: 6px; flex: none;
  background: var(--ink); color: var(--gold);
  display: flex; align-items: center; justify-content: center;
}
.alert-bank-name { font-size: 11px; font-weight: 900; letter-spacing: 1.2px; color: var(--ink-soft); }
.alert-pill {
  margin-left: auto; font-size: 11px; font-weight: 900; letter-spacing: 0.8px;
  padding: 2.5px 7px; border-radius: 999px; background: #e7f7ee; color: var(--green-deep);
}
.alert-card.debit .alert-pill { background: #fdeaea; color: var(--red-deep); }
.alert-card .amt { font-family: 'Aeonik Pro', sans-serif; font-size: 24px; white-space: nowrap; padding: 3px 13px 0; }
.alert-card.credit .amt { color: var(--green-deep); }
.alert-card.debit .amt { color: var(--red-deep); }
.alert-card .why { font-size: var(--fs-caption); color: var(--ink-soft); line-height: 1.35; padding: 1px 13px 0; }
.alert-card .bal { font-size: var(--fs-caption); font-weight: 800; padding: 7px 13px 1px; margin-top: 7px; border-top: 1px dashed var(--line); }
/* OPay style success moment on credits: the check draws itself, ripples once. */
.alert-success { display: flex; align-items: center; gap: 9px; padding: 9px 13px 0; }
.succ-ring { position: relative; width: 34px; height: 34px; flex: none; }
.succ-ring svg { width: 100%; height: 100%; display: block; }
.succ-c { stroke: var(--green); stroke-width: 3.5; stroke-linecap: round; stroke-dasharray: 145; stroke-dashoffset: 145; animation: succdraw 0.5s ease forwards; }
.succ-k { stroke: var(--green-deep); stroke-width: 4; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 40; stroke-dashoffset: 40; animation: succdraw 0.3s 0.35s ease forwards; }
.succ-ring::after { content: ''; position: absolute; inset: 0; border-radius: 50%; border: 2px solid rgba(29, 158, 108, 0.55); opacity: 0; animation: succripple 0.85s 0.15s ease-out forwards; }
@keyframes succdraw { to { stroke-dashoffset: 0; } }
@keyframes succripple { 0% { transform: scale(0.62); opacity: 0.85; } 100% { transform: scale(1.55); opacity: 0; } }
.succ-text { font-size: var(--fs-caption); font-weight: 800; color: var(--green-deep); letter-spacing: 0.2px; }
.alert-card.credit .amt { font-size: 27px; }
@keyframes alertin { from { opacity: 0; transform: translateY(-14px) scale(0.97); } to { opacity: 1; transform: none; } }
.alert-card.out { opacity: 0; transform: translateY(-8px); transition: opacity 0.25s, transform 0.25s; }

/* Sheets and modals */
.scrim {
  position: fixed; inset: 0; background: rgba(30, 21, 13, 0.55); z-index: 60;
  display: none; align-items: flex-end; justify-content: center;
}
.scrim.show { display: flex; }
.sheet {
  background: var(--card); width: 100%; max-width: 620px; max-height: 86dvh; overflow-y: auto;
  border-radius: 22px 22px 0 0; padding: 16px 16px calc(18px + env(safe-area-inset-bottom));
  animation: sheetup 0.25s ease;
}
@keyframes sheetup { from { transform: translateY(46px); opacity: 0.4; } to { transform: none; opacity: 1; } }
.sheet .grab { width: 44px; height: 5px; border-radius: 99px; background: var(--line); margin: 0 auto 12px; }
.modal-scrim { align-items: center; padding: 18px; }
.modal-card {
  background: var(--card); border-radius: 20px; padding: 20px; width: 100%; max-width: 430px;
  box-shadow: var(--shadow); animation: sheetup 0.25s ease; max-height: 88dvh; overflow-y: auto;
}
.deed-band { height: 14px; border-radius: 8px; margin-bottom: 10px; }
.ladder { width: 100%; border-collapse: collapse; margin: 10px 0; }
.ladder td, .ladder th { padding: 6.5px 8px; border-bottom: 1px solid var(--line); text-align: left; font-size: var(--fs-body); }
.ladder td:last-child, .ladder th:last-child { text-align: right; font-weight: 800; }
.ladder tr.here { background: #fff3d1; }
.meaning {
  position: relative;
  background: #fdf3da; border: 1px dashed var(--gold-deep); border-radius: 12px;
  padding: 10px 12px 10px 36px; font-weight: 600; margin: 10px 0;
}
.meaning::before {
  content: '\201C'; position: absolute; left: 9px; top: 1px;
  font-family: 'Aeonik Pro', sans-serif; font-size: 32px; line-height: 1; color: var(--gold-deep);
}
.kv { display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px dashed var(--line); }
.kv b { font-weight: 800; }
.btnrow { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.btnrow .btn { flex: 1; }

/* Deed sheet as a printed deed */
.deed-doc {
  position: relative; background: #fffdf4; border: 1.5px solid #d9c9a3; border-radius: 14px;
  overflow: hidden; box-shadow: 0 6px 18px rgba(43, 32, 24, 0.12);
}
.deed-head { position: relative; display: flex; gap: 12px; align-items: center; padding: 14px 16px; color: #fff; }
.deed-head::before {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.30), rgba(255, 255, 255, 0) 46%, rgba(0, 0, 0, 0.24));
}
.deed-head > * { position: relative; z-index: 1; }
.deed-seal { width: 46px; height: 46px; flex: none; color: rgba(255, 255, 255, 0.92); }
.deed-seal svg { width: 100%; height: 100%; display: block; }
.deed-kicker { font-size: 11px; font-weight: 900; letter-spacing: 1.4px; opacity: 0.88; }
.deed-head .title { color: #fff; font-size: 23px; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.35); }
.deed-sub { font-size: var(--fs-caption); opacity: 0.94; margin: 2px 0 0; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); }
.deed-body {
  padding: 6px 16px 16px;
  background: repeating-linear-gradient(180deg, transparent 0 27px, rgba(92, 76, 61, 0.05) 27px 28px);
}
.deed-stamp {
  position: absolute; top: 92px; right: 12px; z-index: 2;
  transform: rotate(-13deg);
  border: 3px solid var(--own); color: var(--own);
  font-family: 'Aeonik Pro', sans-serif; font-size: 15px; letter-spacing: 2px;
  padding: 3px 10px; border-radius: 6px; background: rgba(255, 253, 244, 0.78);
  pointer-events: none;
}

/* Auction panel */
/* KP12: state panels inside the dock speak the same glass language:
   frosted light glass, layered soft shadow, inset highlights, no hard
   border. */
.auction-card {
  background: linear-gradient(180deg, rgba(255, 249, 233, 0.97), rgba(255, 241, 208, 0.94));
  border: none; border-radius: 18px; padding: 13px; margin-bottom: 10px; color: var(--ink);
  box-shadow: 0 12px 26px rgba(10, 6, 2, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.65), inset 0 0 0 1px rgba(232, 169, 12, 0.35);
}
.bid-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px; margin-top: 9px; }

/* Feed sheet list */
.feed-item { padding: 8px 2px; border-bottom: 1px dashed var(--line); }
.feed-item .k { font-size: var(--fs-meta); font-weight: 900; text-transform: uppercase; letter-spacing: 0.5px; color: var(--gold-deep); }

/* Overlays: countdown, end, share, pass banner */
.overlay {
  position: fixed; inset: 0; z-index: 90; display: none;
  align-items: center; justify-content: center; flex-direction: column;
  background: rgba(30, 21, 13, 0.78); color: var(--cream); padding: 20px; text-align: center;
}
.overlay.show { display: flex; }
/* Pause waiting card (Lesson 14): named, timed, and the controls beneath
   it visibly disabled while it stands. */
.pause-card {
  background: var(--card); color: var(--ink); border-radius: 20px; padding: 22px;
  max-width: 400px; width: 100%; text-align: left; box-shadow: var(--shadow);
}
.pause-card .caption { margin-top: 8px; }
#view-game.is-paused .walletbar { opacity: 0.45; pointer-events: none; filter: saturate(0.6); }
.count-num { font-family: 'Aeonik Pro', sans-serif; font-size: 110px; color: var(--gold); animation: countpop 0.55s ease; }
@keyframes countpop { from { transform: scale(1.7); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.end-table { width: 100%; max-width: 460px; border-collapse: collapse; margin: 14px 0; }
.end-table td, .end-table th { padding: 9px 8px; border-bottom: 1px solid rgba(245, 239, 226, 0.25); text-align: left; }
.end-table td:last-child, .end-table th:last-child { text-align: right; }
.end-table .winner-row { color: var(--gold); font-weight: 800; }
.pass-banner {
  position: fixed; top: calc(var(--topbar-h) + 6px); left: 50%; transform: translateX(-50%); z-index: 85;
  background: var(--ink); color: var(--cream); border-radius: 999px; padding: 11px 20px;
  font-weight: 800; display: none; box-shadow: var(--shadow); white-space: nowrap;
}
.pass-banner.show { display: block; animation: alertin 0.25s ease; }
.share-canvas-box canvas { width: min(78vw, 330px); height: auto; border-radius: 14px; box-shadow: var(--shadow); }
.toast {
  position: fixed; bottom: 216px; left: 50%; transform: translateX(-50%); z-index: 95;
  background: var(--ink); color: var(--cream); padding: 11px 17px; border-radius: 13px;
  font-weight: 700; display: none; max-width: 92vw; text-align: center; box-shadow: var(--shadow);
}
.toast.show { display: block; }
.toast button { margin-left: 10px; }
.spray-note {
  position: fixed; z-index: 70; font-weight: 900; color: var(--green-deep);
  background: #e7f7ee; border: 1px solid var(--green); border-radius: 6px; padding: 3px 8px;
  animation: sprayfall 1.6s ease-in forwards; pointer-events: none; font-size: var(--fs-caption);
}
@keyframes sprayfall {
  0% { transform: translateY(-6vh) rotate(-14deg); opacity: 0; }
  12% { opacity: 1; }
  100% { transform: translateY(52vh) rotate(24deg); opacity: 0; }
}
#flicker {
  position: fixed; inset: 0; background: #14100c; opacity: 0; pointer-events: none; z-index: 65;
}
#flicker.go { animation: flick 0.7s steps(1) 1; }
@keyframes flick { 0% { opacity: 0; } 12% { opacity: 0.55; } 20% { opacity: 0; } 34% { opacity: 0.4; } 46% { opacity: 0; } 60% { opacity: 0.3; } 72% { opacity: 0; } 100% { opacity: 0; } }
.trade-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.deed-check { display: flex; align-items: center; gap: 8px; padding: 7px 8px; border: 1px solid var(--line); border-radius: 10px; margin-bottom: 6px; background: var(--paper); }
.deed-check .dot { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.num-in { width: 100%; padding: 10px; border-radius: 10px; border: 1.5px solid var(--line); background: var(--paper); }
.hidden { display: none !important; }
.mt8 { margin-top: 8px; } .mt14 { margin-top: 14px; } .mb8 { margin-bottom: 8px; }
.center { text-align: center; }

/* Inflation additions */
.pledgetag {
  position: absolute; top: 26%; left: 50%; transform: translateX(-50%) rotate(-18deg);
  font-size: 7px; font-weight: 900; letter-spacing: 0.4px; color: #fff;
  background: #7a4fa3; padding: 1px 4px; border-radius: 3px; pointer-events: none; z-index: 3;
}
#infBar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: linear-gradient(90deg, #3a1428, #6e1f1f);
  color: #ffe9c9; border-radius: 13px; padding: 8px 12px;
}
#infBar .ph { font-family: 'Aeonik Pro', sans-serif; font-size: var(--fs-lead); }
#infBar .idx { background: var(--gold); color: var(--ink); font-weight: 900; border-radius: 8px; padding: 2px 8px; font-size: var(--fs-caption); }
#infBar .clk { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 800; }
#infBar button { background: rgba(255, 250, 240, 0.14); color: #ffe9c9; border: 1px solid rgba(255, 233, 201, 0.4); border-radius: 9px; padding: 6px 10px; min-height: 36px; font-weight: 700; font-size: 13px; }
.sapa-warn {
  margin-top: 8px; background: rgba(255, 240, 214, 0.95); border: none; color: #7a5200;
  border-radius: 14px; padding: 9px 12px; font-weight: 700; font-size: var(--fs-caption);
  box-shadow: 0 8px 18px rgba(10, 6, 2, 0.22), inset 0 0 0 1px rgba(232, 169, 12, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.6);
  animation: sapapulse 1.6s ease infinite;
}
@keyframes sapapulse {
  0%, 100% { box-shadow: 0 8px 18px rgba(10, 6, 2, 0.22), inset 0 0 0 1px rgba(232, 169, 12, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 0 0 0 rgba(232, 169, 12, 0); }
  50% { box-shadow: 0 8px 18px rgba(10, 6, 2, 0.22), inset 0 0 0 1px rgba(232, 169, 12, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 0 0 5px rgba(232, 169, 12, 0.22); }
}
.contract-row {
  display: flex; gap: 9px; align-items: flex-start; padding: 10px; border: 1px solid var(--line);
  border-radius: 13px; background: var(--paper); margin-bottom: 8px;
}
.contract-row .grow { flex: 1; }
.phase-ceremony { text-align: center; padding: 20px; }
.phase-ceremony .big { font-family: 'Aeonik Pro', sans-serif; font-size: 46px; color: var(--gold); }
.phase-ceremony .idx { font-family: 'Aeonik Pro', sans-serif; font-size: 30px; color: #ffb3b3; }
select.textin { appearance: none; }

/* Phase ceremony skins (lessons doc, Upgrade 1) and end screen lessons */
.ceremony { max-width: 520px; width: 100%; text-align: left; }
.ceremony .big { text-align: center; }
.ceremony-letter {
  background: #fffdf4; color: var(--ink); border-radius: 6px; padding: 26px 24px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.45); transform: rotate(-0.6deg);
}
.ceremony-letter .lhead { font-family: 'Aeonik Pro', sans-serif; border-bottom: 2px solid var(--ink); padding-bottom: 8px; margin-bottom: 14px; }
.ceremony-letter .lsign { margin-top: 16px; }
.ceremony-letter .lfoot { margin-top: 16px; border-top: 1px dashed var(--line); padding-top: 10px; font-size: var(--fs-caption); color: var(--ink-soft); }
.ceremony-news { background: #14100c; border: 3px solid var(--red); border-radius: 10px; overflow: hidden; box-shadow: 0 18px 44px rgba(0, 0, 0, 0.5); }
.ceremony-news .newsbar {
  background: var(--red); color: #fff; font-family: 'Aeonik Pro', sans-serif;
  font-size: 30px; padding: 10px 18px; letter-spacing: 1px; animation: newsflash 0.8s steps(2) 3;
}
@keyframes newsflash { 50% { background: #7d1d1d; } }
.ceremony-news .newsbody { padding: 18px; color: var(--cream); }
.ceremony-news .newsline { font-family: 'Aeonik Pro', sans-serif; font-size: 24px; color: var(--gold); }
.ceremony-notice {
  background: #241a12; color: #ffe9c9; border: 3px double var(--gold); border-radius: 8px;
  padding: 24px; box-shadow: 0 18px 44px rgba(0, 0, 0, 0.5);
}
.ceremony-notice .nhead { font-family: 'Aeonik Pro', sans-serif; font-size: 30px; color: var(--red); letter-spacing: 2px; text-align: center; margin-bottom: 12px; }
.water-chip { background: #dff2fb; color: #14557a; font-weight: 900; border-radius: 8px; padding: 2px 8px; font-size: var(--fs-caption); }
.infbar-note { flex-basis: 100%; font-size: var(--fs-meta); opacity: 0.85; }
.end-lessons { font-size: var(--fs-caption); color: #ffe9c9; opacity: 0.92; display: block; margin-top: 3px; max-width: 300px; }
.real-table-note { max-width: 460px; }

/* Landscape: the designed composition. Board left, rail right. */
@media (orientation: landscape) and (min-width: 700px) {
  /* Lesson 2: chrome slims in landscape so the stage grows: shorter top
     bar, tighter message and phase bars. The layout math reads --topbar-h,
     so shrinking it here grows the board area everywhere it matters. */
  :root { --topbar-h: 47px; }
  .topbar { padding-top: 5px; padding-bottom: 5px; }
  .wordmark { font-size: 18px; }
  .msgbar { padding: 6px 11px; min-height: 32px; }
  #infBar { padding: 6px 10px; }
  #view-game.active {
    display: grid; grid-template-columns: minmax(0, 1fr) var(--rail-w);
    height: calc(100dvh - var(--topbar-h));
  }
  /* KP11: the landscape board fills its whole stage box beside the
     rail, edge to edge, same law as portrait: no stage padding inside
     the box, no wrap frame, no square cap on the outer. */
  .game-stage { display: flex; align-items: stretch; justify-content: center; padding: 0; padding-left: env(safe-area-inset-left); min-width: 0; }
  .board-outer { flex: 1 1 auto; width: 100%; height: 100%; max-width: none; margin: 0; }
  .board-wrap { --frame: 0px; padding: 0; border-radius: 0; box-shadow: none; background: none; aspect-ratio: auto; height: 100%; }
  .board { border-radius: 0; }
  .game-rail {
    padding-bottom: 0; padding-right: env(safe-area-inset-right); height: 100%; min-height: 0;
    border-left: 1px solid var(--line);
    background: linear-gradient(180deg, rgba(255, 250, 240, 0.75), rgba(245, 239, 226, 0.4));
  }
  .rail-top { overflow-y: auto; flex: 1 1 auto; min-height: 0; padding: 10px 10px 6px; }
  /* The phase bar cannot carry its explainer note in a 306px rail: the
     chips stay, the note stays a portrait luxury. */
  #infBar { gap: 5px; padding: 7px 9px; }
  #infBar .ph { font-size: 14px; }
  #infBar .infbar-note { display: none; }
  .walletbar {
    position: static; flex: none;
    margin: 8px 10px calc(10px + env(safe-area-inset-bottom));
    padding: 11px 12px;
  }
  .wallet-inner { max-width: none; }
  .alerts { left: auto; right: max(12px, env(safe-area-inset-right)); transform: none; width: min(330px, calc(var(--rail-w) - 14px)); }
  .toast { left: 18px; bottom: 18px; transform: none; }
  /* Sheets become right side panels in landscape */
  .scrim { align-items: stretch; justify-content: flex-end; }
  .scrim.modal-scrim { align-items: center; justify-content: center; }
  .sheet {
    width: min(440px, 92vw); max-width: 440px; height: 100%; max-height: none;
    border-radius: 22px 0 0 22px; animation: sheetin 0.25s ease;
    padding-right: calc(16px + env(safe-area-inset-right));
  }
  @keyframes sheetin { from { transform: translateX(46px); opacity: 0.4; } to { transform: none; opacity: 1; } }
  /* Landing splits: hero left, join card right and scrollable */
  #view-landing.active {
    display: grid; grid-template-columns: 0.9fr 1.1fr;
    grid-template-rows: auto minmax(0, 1fr) auto;
    height: calc(100dvh - var(--topbar-h));
  }
  .landing-hero { grid-row: 1 / 4; display: flex; flex-direction: column; justify-content: center; height: 100%; }
  .banner { grid-column: 2; margin: 12px 14px 0; }
  .join-card { grid-column: 2; overflow-y: auto; margin: 12px 14px; min-height: 0; }
  #view-landing > p.caption { grid-column: 2; }
}

/* Landscape on smaller phones: a slimmer rail keeps the board big. */
@media (orientation: landscape) and (min-width: 700px) and (max-width: 940px) {
  :root { --rail-w: 306px; }
}

/* Small portrait phones: compress the busy bars, keep every tap target. */
@media (max-width: 480px) {
  #infBar { gap: 6px; padding: 8px 10px; }
  #infBar .ph { font-size: 15px; }
  #infBar .clk { margin-left: 0; }
  .infbar-note { line-height: 1.35; }
  .msgbar { padding: 8px 11px; min-height: 36px; }
  .players-strip { gap: 6px; }
  .pchip { flex-basis: 112px; padding: 4px 8px; }
  .wallet-top { gap: 8px; }
  .action-row .btn { padding: 12px 10px; }
  .alerts { top: calc(8px + env(safe-area-inset-top)); }
}

/* ================= 3D layer (board3d.js; inert when WebGL is off) ========= */
.gl-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; }
#glLandingCanvas { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 0; touch-action: none; }
#lobbyStage { position: relative; height: 304px; margin: 12px 0 4px; border-radius: 20px; overflow: hidden;
  background: linear-gradient(180deg, #ffe3ae, #f7cf8d 55%, #e8b06a); box-shadow: var(--shadow); }
.lobby-stage-fallback { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; }
.lobby-stage-fallback .ls-word { font-family: 'Aeonik Pro', sans-serif; font-size: 34px; letter-spacing: 1px; color: var(--ink); }
.lobby-stage-fallback .ls-word .gold { color: var(--gold-deep); }
.lobby-stage-fallback .ls-sub { font-weight: 700; color: var(--ink-soft); font-size: var(--fs-caption); }
body.gl-on .lobby-stage-fallback { display: none; }

/* Landing: the 3D street runs behind the whole view; content floats over it */
body.gl-on #view-landing > *:not(canvas) { position: relative; z-index: 1; }
body.gl-on .hero-board { display: none; }
body.gl-on .landing-hero .display { color: #fff8ea; text-shadow: 0 3px 0 rgba(122, 63, 6, 0.55), 0 10px 30px rgba(90, 45, 0, 0.45); }
body.gl-on .landing-hero .tagline { color: #fff3da; font-weight: 700; text-shadow: 0 2px 12px rgba(90, 45, 0, 0.55); }
body.gl-on .token-rain { color: #ffe9c9; }
body.gl-on .join-card { background: rgba(255, 252, 245, 0.88); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
body.gl-on #view-landing > p.caption { color: #fff3da; font-weight: 600; text-shadow: 0 1px 8px rgba(90, 45, 0, 0.6); }

/* Game: the DOM board becomes an invisible click layer over the 3D board */
/* pointer-events none matters: without it the invisible DOM grid swallows
   physical taps (its label spans are hit targets) and the 3D raycast never
   sees them. Programmatic clicks in tests still reach the DOM handlers. */
body.gl-on #board { background: transparent; box-shadow: none; z-index: 2; pointer-events: none; }
body.gl-on .board-wrap { background: #241708; }
body.gl-on .bspace { opacity: 0; pointer-events: none; }
body.gl-on .btokens, body.gl-on .bcenter { display: none; }
body.gl-on #boardWrap .gl-canvas { z-index: 1; }

/* Lobby slots become a compact management strip under the 3D gathering */
body.gl-on .slots { grid-template-columns: 1fr; gap: 6px; }
body.gl-on .slot { padding: 7px 10px; border-radius: 13px; }
body.gl-on .slot .avatar { width: 26px; height: 26px; }

/* End screen: podium scene behind the DOM final table */
body.gl-on #endOverlay { background: rgba(24, 15, 8, 0.42); }
body.gl-on #endOverlay > *:not(.gl-canvas) { position: relative; z-index: 1; }

/* Card faces (drawn cards, choices, proposals): the DOM reading layer */
.modal-card.card-face {
  border-radius: 18px; border: 3px solid var(--ink); overflow: hidden; padding: 0;
  box-shadow: 0 24px 60px rgba(20, 12, 4, 0.5); animation: cardflip 0.55s cubic-bezier(0.2, 1.4, 0.4, 1);
}
.modal-card.card-face .caption {
  margin: 0; padding: 12px 18px; color: #fff; font-weight: 900; letter-spacing: 1.6px;
}
.modal-card.card-face.face-wahala .caption { background: linear-gradient(180deg, #e0584d, #b02e2e); }
.modal-card.card-face.face-family .caption { background: linear-gradient(180deg, #5aa9e6, #2f6fb3); }
.modal-card.card-face .lead { padding: 4px 18px 0; font-size: 17px; }
.modal-card.card-face .btnrow { padding: 0 18px 16px; }
@keyframes cardflip { from { transform: perspective(900px) rotateY(-72deg) translateY(26px); opacity: 0.2; } to { transform: none; opacity: 1; } }
body.gl-on #cardScrim { background: rgba(30, 21, 13, 0.25); }
/* Two option choices sit as tappable halves of the card itself */
body.gl-on #choiceModal .btnrow, body.gl-on #proposalModal .btnrow {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
}
body.gl-on #choiceModal .btnrow .btn, body.gl-on #proposalModal .btnrow .btn { min-height: 58px; font-size: 15px; }

/* Scaled money theatre: the rare full-screen takeover */
#bigMoment {
  position: fixed; inset: 0; z-index: 58; display: none; pointer-events: none;
  align-items: center; justify-content: center; flex-direction: column; text-align: center;
  background: radial-gradient(ellipse at center, rgba(24, 15, 8, 0.25), rgba(24, 15, 8, 0.78));
  color: var(--cream); padding: 24px;
}
#bigMoment.show { display: flex; animation: bigmoment-in 0.3s ease; }
#bigMoment .bm-kicker { font-weight: 900; letter-spacing: 2.5px; color: var(--gold); font-size: 14px; }
#bigMoment .bm-amt { font-family: 'Aeonik Pro', sans-serif; font-size: clamp(44px, 11vw, 84px); line-height: 1.05; margin: 8px 0 2px; text-shadow: 0 6px 0 rgba(0,0,0,0.25); }
#bigMoment .bm-line { font-size: 17px; font-weight: 700; max-width: 520px; }
@keyframes bigmoment-in { from { opacity: 0; transform: scale(1.06); } to { opacity: 1; transform: none; } }

/* Phone widths: the top bar carries the wordmark, the room chip, the mode
   pill (Lesson 10) and three icon buttons. Trimmed as one row they fit a
   390px phone; left untrimmed the bar overflows the width, the page zooms
   out to fit, and taps land off their targets. This block sits last so it
   wins over the base .chip and .iconbtn rules. */
@media (max-width: 520px) {
  .topbar { gap: 4px; padding-left: 6px; padding-right: 6px; flex-wrap: nowrap; }
  .wordmark { font-size: 16px; letter-spacing: 0; }
  .chip { padding: 3px 6px; min-height: 30px; font-size: 11px; white-space: nowrap; }
  .iconbtn { width: 30px; height: 30px; flex: none; }
}

/* ================= KP8 MOBILE OVERHAUL =================
   Owner correction, binding: compact density with a native mobile game
   feel, not desktop slabs scaled down. Primary 43px/15px, utility
   38px/13px, chips and pills 28px/12px, page padding 12px, card padding
   12px, section gaps 8px. The board wins the space:
   the portrait game is one column [stage grows][rail scrolls][dock in
   flow at the bottom], so the dock never covers rail content and no
   JavaScript measures it. Hard invariant: nothing may push
   document.scrollWidth past innerWidth (the browser suite asserts it). */

/* Shared compact pieces for the portrait column and the landscape rail. */
@media (max-width: 940px) {
  /* The Inflation phase strip: one row, the phase countdown always
     visible. The salary span and the explainer note are desktop luxuries;
     the phase, index, water gauge and clock stay. (KP9 removed the
     Classic bar entirely: its match clock lives in the header chip.) */
  #infBar { padding: 7px 10px; border-radius: 12px; gap: 6px; }
  #infBar > span:not(.ph):not(.idx):not(.water-chip):not(.clk) { display: none; }
  #infBar .idx, #infBar .water-chip { display: inline-flex; align-items: center; min-height: 26px; padding: 3px 8px; font-size: 11px; border-radius: 999px; }
  #infBar button { min-height: 26px; padding: 3px 8px; font-size: 11px; }
  .msgbar { padding: 7px 10px; min-height: 34px; border-radius: 16px; }
  /* The rail is a scroll column: children keep their natural height and
     the column scrolls, instead of flex shrinking a bar and its text
     spilling out of its background (seen on the landscape message bar). */
  .rail-top > * { flex: none; }
  /* Players: one snapping row that scrolls sideways, never a tower. */
  .players-strip { flex-wrap: nowrap; overflow-x: auto; scroll-snap-type: x proximity; gap: 6px; padding-bottom: 2px; scrollbar-width: none; }
  .players-strip::-webkit-scrollbar { display: none; }
  .players-strip .pchip { flex: 0 0 auto; scroll-snap-align: start; padding: 4px 10px 4px 5px; gap: 6px; }
  .players-strip .pchip .avatar { width: 22px; height: 22px; }
  .players-strip .pchip .meta { display: none; }
  /* Dock internals at the compact scale (KP12 glass rules live in the
     KP12 section; these are the phone-size metrics). */
  .actionbar { gap: 6px; margin-top: 7px; }
  .actionbar .btn { min-height: var(--btn-h); height: var(--btn-h); padding: 0 14px; font-size: 15px; border-radius: 12px; }
  #secondaryBar { gap: 2px; margin-top: 3px !important; }
  .auction-card { padding: 10px 12px; border-radius: 16px; }
}

/* Portrait phones: the whole layout system. */
@media (max-width: 699px) {
  :root { --topbar-h: 48px; }
  .topbar { padding: 6px var(--sp-3); gap: 6px; }
  .wordmark { font-size: 17px; }
  .topbar .chip { min-height: var(--chip-h); padding: 3px 9px; font-size: 12px; }
  .topbar .iconbtn { width: 34px; height: 34px; border-radius: 10px; }
  .topbar .iconbtn svg { width: 18px; height: 18px; }

  /* Game: [tall stage] [rail at natural height] [dock pinned]. KP11
     restores the tall stage (about 60 to 65 percent of the screen) that
     KP10's square stage broke, and the board itself fills that tall
     box: the stage flex-grows into every leftover pixel of height, the
     wrap fills it completely, and the track is laid out as the box's
     own rectangle (board3d trackLayout, board.js fitLayout), edge to
     edge on all four sides, zero frame, zero padding, nothing left
     over. The rail takes its natural height under the stage and
     scrolls internally if it must; the dock stays pinned at the
     bottom. One view only. */
  #view-game.active { display: flex; flex-direction: column; height: calc(100dvh - var(--topbar-h)); }
  .game-stage { order: 1; flex: 1 1 0%; min-height: 0; padding: 0; display: flex; flex-direction: column; }
  .game-rail { display: contents; }
  .board-outer { flex: 1 1 auto; min-height: 0; width: 100%; max-width: none; margin: 0; display: flex; }
  .board-wrap { flex: 1 1 auto; width: 100%; aspect-ratio: auto; --frame: 0px; padding: 0; border-radius: 0; box-shadow: none; background: none; }
  .board { border-radius: 0; }
  .rail-top { order: 2; flex: 0 1 auto; overflow-y: auto; min-height: 0; padding: 6px var(--sp-3) 2px; gap: 6px; }
  .rail-top > .caption { display: none; }
  .walletbar { order: 3; position: static; flex: 0 0 auto; width: auto; margin: 4px 10px calc(10px + env(safe-area-inset-bottom)); padding: 10px 11px 11px; }
  .wallet-inner { max-width: none; }
  .toast { bottom: calc(156px + env(safe-area-inset-bottom)); }

  /* Lobby: compact header, slots scroll in their own region, Start pinned. */
  #view-lobby.active { display: flex; flex-direction: column; height: calc(100dvh - var(--topbar-h)); }
  .lobby-wrap { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; padding: 10px var(--sp-3) calc(10px + env(safe-area-inset-bottom)); }
  .lobby-wrap > p.caption { flex: none; }
  .code-hero { flex: none; padding: 2px 0 0; }
  .code-big { font-size: 36px; letter-spacing: 8px; }
  #lobbyModeLine { flex: none; margin: 2px 0 6px; font-size: 14px; }
  .invite-actions { flex: none; margin: 0 0 6px; gap: 6px; }
  /* The QR leaves portrait phones: Copy invite and Share already do its
     job in a thumb column, and its 98px was the difference between a
     pinned Start and a scrolled one at 360x740. It stays on desktop and
     landscape, where the column has room for it. */
  .qr-box { display: none; }
  #lobbyStage { flex: none; height: 112px; margin: 6px 0; border-radius: 14px; }
  .bot-prompt { flex: none; margin: 6px 0 0; padding: 8px var(--sp-3); border-radius: 14px; }
  .bot-prompt .bp-title { font-size: 15px; margin-bottom: 2px; }
  .bot-prompt .btnrow { margin-top: 8px; gap: 6px; }
  .bot-prompt .btn { min-height: var(--btn-h-s); font-size: 13px; padding: 7px 10px; }
  .lobby-wrap .mt14 { margin-top: 8px; }
  #lobbyHint { margin-top: 6px; }
  .slots { flex: 1 1 auto; min-height: 0; overflow-y: auto; align-content: start; gap: 6px; margin: 8px 0 0; padding: 1px; }
  .slot { padding: 8px 10px; min-height: 46px; border-radius: 13px; }
  #passAddBox { flex: none; margin-top: 8px; }
  .settings-chips { flex: none; margin: 8px 0 0; gap: 6px; }
  #chaosNote { flex: none; margin: 6px 0 0; }
  .lobby-wrap .mt14 { flex: none; }
  #startBtn { min-height: var(--btn-h); font-size: 15px; }

  /* Landing: one thumb column, compact hero and cards. */
  .landing-hero { padding: var(--sp-3) var(--sp-3) 0; }
  .landing-hero .display { font-size: 32px; }
  .landing-hero .tagline { margin-top: 4px; }
  .hero-board { height: 150px; }
  .token-rain { margin: 10px 0 0; }
  .join-card { margin: 8px var(--sp-3) 16px; padding: var(--sp-3); border-radius: 18px; }
  .join-card label { margin: 10px 0 5px; }
  .token-grid { gap: 6px; }
  .token-pick { padding: 7px 4px; border-radius: 12px; }
  .mode-row { gap: 6px; }
  .mode-pick { padding: 9px 10px; border-radius: 12px; }
  .toggle-row { padding: 8px 0; }
  .banner { margin: 8px var(--sp-3) 0; padding: 10px var(--sp-3); }
  .btnrow { gap: var(--sp-2); }

  /* Sheets and dialogs: tight padding, full width actions. */
  .sheet { padding: 10px var(--sp-3) calc(12px + env(safe-area-inset-bottom)); border-radius: 18px 18px 0 0; }
  .sheet .grab { margin-bottom: 8px; }
  .modal-scrim { padding: var(--sp-3); }
  .modal-card { padding: var(--sp-4); border-radius: 16px; }
  .deed-head { padding: var(--sp-3); }
  .deed-head .title { font-size: 20px; }
  .deed-seal { width: 38px; height: 38px; }
  .deed-body { padding: 4px var(--sp-3) var(--sp-3); }
  .meaning { padding: 9px 10px 9px 34px; margin: 8px 0; }
  .ladder td, .ladder th { padding: 5px 4px; font-size: 13px; }
  .alerts { top: calc(var(--topbar-h) + 6px); }
  .alert-card .amt { font-size: 21px; }
  .alert-card.credit .amt { font-size: 23px; }
}

/* Narrow phones: the mode pill and the graphics tier leave the top bar.
   The mode still reads in the lobby mode line, and the room code (now
   carrying the match clock) never truncates. */
@media (max-width: 520px) {
  #modePill, #tierBtn { display: none; }
  /* KP13: sharpness is the player's choice, so the tier control stays
     reachable on phones where there is room for it: the landing and the
     lobby. In game the topbar is full (wordmark, room chip, sound,
     voice) and the button stays out of the way. */
  body:has(#view-landing.active) #tierBtn,
  body:has(#view-lobby.active) #tierBtn { display: inline-flex; }
}



/* Short landscape: the lobby stays a plain scrolling page (the pinned
   column is a portrait system), just with the compact pieces. */
@media (orientation: landscape) and (min-width: 700px) and (max-height: 500px) {
  .code-big { font-size: 34px; }
  .qr-box svg { width: 84px; height: 84px; }
  #lobbyStage { height: 140px; margin: 6px 0; }
  .bot-prompt { padding: 8px var(--sp-3); }
  .bot-prompt .btn { min-height: var(--btn-h-s); font-size: 13px; padding: 7px 10px; }
}

/* ================= KP12 GLASS DOCK =================
   The bottom control zone stops looking like web buttons. One floating
   frosted dock (the .walletbar base rule above carries the glass:
   translucent brown-green tint from the game's own palette, backdrop
   blur, layered soft shadows, inset hairline highlights, no borders),
   a quiet cash segment badged with a single banknote glyph, the
   primary action as a glossed gradient pill with the countdown as a
   depleting progress ring overlapping its right end, and the utilities
   as ghosts on the glass. Radius family: 22px dock, 18px panels,
   full-round pills, chips and badges. */
/* A warm wash behind the game column, so the dock's translucent tint
   and shadow read as a layer over the page, not a flat bar. */
#view-game { background: linear-gradient(180deg, var(--cream) 0%, #f3ead6 76%, #ecdfc2 100%); }

/* Cash segment: quiet, badged, exact. The amount still counts up and
   down through renderWallet's existing animation. */
.walletbar .cash-badge {
  width: 30px; height: 30px; flex: none; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255, 200, 87, 0.16); color: var(--gold);
  box-shadow: inset 0 0 0 1px rgba(255, 200, 87, 0.38), inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
.walletbar .wallet-cash {
  font-family: 'Aeonik Pro', sans-serif; font-size: 20px; line-height: 1.05;
  color: #fff8ea; letter-spacing: -0.2px;
}
.walletbar .wallet-cash small {
  display: block; line-height: 1; margin-bottom: 2px;
  font-family: 'Figtree', sans-serif; font-size: 9.5px; font-weight: 700;
  letter-spacing: 1.1px; color: rgba(245, 239, 226, 0.58);
}
.walletbar .caption { color: rgba(245, 239, 226, 0.75); font-weight: 600; }

/* The primary pill: gradient with a gloss sweep on top, soft layered
   shadow, and a springy scale press. Contextual colours ride the same
   gloss. Muted glass when the dock only carries a waiting line. */
.walletbar .actionbar .btn {
  border-radius: 999px; min-height: 44px; height: 44px; padding: 0 18px; font-size: 15px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0) 46%), linear-gradient(180deg, #ffd97a, var(--gold));
  box-shadow: 0 10px 22px rgba(10, 6, 2, 0.30), inset 0 1px 0 rgba(255, 255, 255, 0.35), inset 0 -2px 6px rgba(0, 0, 0, 0.12);
  transition: transform 0.13s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.13s ease, filter 0.13s ease;
}
.walletbar .actionbar .btn.green { background: linear-gradient(180deg, rgba(255, 255, 255, 0.24), rgba(255, 255, 255, 0) 46%), linear-gradient(180deg, #2bbd84, var(--green)); box-shadow: 0 10px 22px rgba(6, 40, 26, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.35), inset 0 -2px 6px rgba(0, 0, 0, 0.14); }
.walletbar .actionbar .btn.red { background: linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0) 46%), linear-gradient(180deg, #e86a6a, var(--red)); box-shadow: 0 10px 22px rgba(60, 10, 10, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.32), inset 0 -2px 6px rgba(0, 0, 0, 0.14); }
.walletbar .actionbar .btn.dark { background: linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0) 46%), linear-gradient(180deg, #4a3826, var(--ink)); box-shadow: 0 10px 22px rgba(10, 6, 2, 0.38), inset 0 1px 0 rgba(255, 255, 255, 0.22), inset 0 -2px 6px rgba(0, 0, 0, 0.25); }
.walletbar .actionbar .btn.ghost {
  background: rgba(255, 250, 240, 0.10); color: var(--cream); border: none;
  box-shadow: inset 0 0 0 1px rgba(255, 250, 240, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
.walletbar .actionbar .btn:active { transform: scale(0.98); }
.walletbar .actionbar .btn:disabled { opacity: 0.45; box-shadow: none; transform: none; }
/* Small buttons inside dock panels (auction bids) join the pill family. */
.walletbar .btn.small {
  border-radius: 999px;
  box-shadow: 0 6px 14px rgba(10, 6, 2, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.3);
  transition: transform 0.13s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.walletbar .btn.small:active { transform: scale(0.97); }
.walletbar .btn.small.ghost { background: var(--paper); color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); }

/* The timer ring: a circular badge overlapping the pill's right end.
   The SVG progress circle depletes against the turn length (ticker in
   app.js), smoothed by the transition so it glides, never steps. The
   number rides the existing data-deadline text sweep. */
.timer-ring {
  position: relative; flex: none; width: 42px; height: 42px; margin-left: -16px; z-index: 2;
  border-radius: 50%;
  background: linear-gradient(180deg, #43331f, #241a10);
  box-shadow: 0 8px 18px rgba(10, 6, 2, 0.35), inset 0 1px 0 rgba(255, 248, 232, 0.22), inset 0 0 0 1px rgba(255, 248, 232, 0.10);
}
.timer-ring svg { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.timer-ring circle { fill: none; stroke-width: 3.5; }
.timer-ring .ring-bg { stroke: rgba(255, 248, 232, 0.20); }
.timer-ring .ring-fg {
  stroke: var(--gold); stroke-linecap: round;
  stroke-dasharray: 116.24; stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.55s linear;
}
.timer-ring .ring-num {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 800; color: var(--cream); font-variant-numeric: tabular-nums;
}
.timer-ring.pulse { animation: clockpulse 0.32s ease; }

/* ================= KP13 GLASS SWEEP =================
   One pass, every bar, pill and slab, the owner's order: no more
   catching dated elements one at a time. The remaining flat slabs join
   the KP12 dock's family: translucent frosted glass, radius 16+,
   layered soft shadows, inset highlights, zero hard borders, no
   dashed web-form boxes anywhere. Presentation only: no layout law,
   ids, copy or board rules move. */

/* The named one: the message bar. It was a fat opaque slab that wrapped
   to two lines and ate stage height; now it is the dock's dark glass in
   a slim bar: compact padding, one line first at 14px, with the acting
   player's token disc as a cue at the left. */
.msgbar {
  background: linear-gradient(180deg, rgba(44, 37, 22, 0.88), rgba(25, 22, 14, 0.93));
  backdrop-filter: blur(14px) saturate(1.3); -webkit-backdrop-filter: blur(14px) saturate(1.3);
  border: none; border-radius: 16px; padding: 8px 12px; min-height: 38px;
  font-size: 14px; font-weight: 600; line-height: 1.3;
  box-shadow: 0 10px 22px rgba(20, 14, 5, 0.20), inset 0 1px 0 rgba(255, 248, 232, 0.13), inset 0 0 0 1px rgba(255, 248, 232, 0.05);
}
.msg-cue {
  width: 24px; height: 24px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center; color: #fff;
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.22), 0 2px 6px rgba(10, 6, 2, 0.30);
}

/* The Inflation phase strip: the same dark glass language in a wine
   tint (its identity), one slim row; its buttons become ghost items on
   the glass instead of bordered boxes. */
#infBar {
  background: linear-gradient(180deg, rgba(74, 26, 32, 0.88), rgba(38, 16, 20, 0.93));
  backdrop-filter: blur(14px) saturate(1.3); -webkit-backdrop-filter: blur(14px) saturate(1.3);
  border: none; border-radius: 16px;
  box-shadow: 0 10px 22px rgba(20, 8, 5, 0.22), inset 0 1px 0 rgba(255, 233, 201, 0.14), inset 0 0 0 1px rgba(255, 233, 201, 0.06);
}
#infBar button {
  background: rgba(255, 250, 240, 0.10); border: none; color: #ffe9c9;
  box-shadow: inset 0 0 0 1px rgba(255, 233, 201, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.10);
}

/* Toasts and the pass banner: dark glass pills that arrive with a soft
   slide and fade instead of blinking in. */
.toast {
  background: linear-gradient(180deg, rgba(44, 37, 22, 0.92), rgba(25, 22, 14, 0.95));
  backdrop-filter: blur(12px) saturate(1.3); -webkit-backdrop-filter: blur(12px) saturate(1.3);
  border: none; border-radius: 999px;
  box-shadow: 0 14px 30px rgba(20, 14, 5, 0.30), inset 0 1px 0 rgba(255, 248, 232, 0.13);
}
.toast.show { animation: toastin 0.25s ease; }
@keyframes toastin { from { opacity: 0; translate: 0 8px; } to { opacity: 1; translate: 0 0; } }
.pass-banner {
  background: linear-gradient(180deg, rgba(44, 37, 22, 0.92), rgba(25, 22, 14, 0.95));
  backdrop-filter: blur(12px) saturate(1.3); -webkit-backdrop-filter: blur(12px) saturate(1.3);
  border: none;
  box-shadow: 0 14px 30px rgba(20, 14, 5, 0.30), inset 0 1px 0 rgba(255, 248, 232, 0.13);
}

/* The bank alert card keeps its OPay identity; only its radius and
   shadow join the family, and its receipt divider becomes a hairline. */
.alert-card { border-radius: 18px; box-shadow: 0 18px 40px rgba(20, 14, 5, 0.30), inset 0 1px 0 rgba(255, 255, 255, 0.65); }
.alert-card .bal { border-top: 1px solid rgba(227, 215, 195, 0.8); }

/* Lobby: slot rows lose the dashed waiting boxes; the bot prompt and
   the settings chips soften into the family. */
.slot { border: 1px solid rgba(227, 215, 195, 0.75); box-shadow: 0 2px 8px rgba(43, 32, 24, 0.06); }
.slot.empty { border-style: solid; background: rgba(255, 253, 247, 0.55); }
.bot-prompt { background: rgba(255, 243, 209, 0.72); border: 1px solid rgba(232, 169, 12, 0.42); box-shadow: 0 6px 16px rgba(43, 32, 24, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.55); }
.settings-chips .chip { background: rgba(255, 250, 240, 0.85); border-color: rgba(227, 215, 195, 0.7); }

/* Landing: the form card, the invite banner and the toggle rows trade
   hard and dashed lines for soft rings and hairlines. */
.join-card { border-color: transparent; box-shadow: var(--shadow), inset 0 0 0 1px rgba(227, 215, 195, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.7); }
.banner { background: rgba(255, 243, 209, 0.78); border: 1px solid rgba(232, 169, 12, 0.45); }
.toggle-row { border-bottom: 1px solid rgba(227, 215, 195, 0.65); }

/* WS5 promotion: the MOST PLAYED badge rides the Inflation card as a
   small gold pill above its title; the appeal line sits under the
   card copy in a deep gold. */
.mode-pick { position: relative; }
.mode-badge {
  display: inline-flex; align-items: center; margin-bottom: 5px;
  background: linear-gradient(180deg, #ffd97a, var(--gold)); color: var(--ink);
  font-size: 10px; font-weight: 900; letter-spacing: 1.1px;
  padding: 2.5px 8px; border-radius: 999px;
  box-shadow: 0 3px 8px rgba(232, 169, 12, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.5);
}
.mode-appeal { display: block; margin-top: 4px; font-size: 12px; font-weight: 800; color: #a87400; }

/* Sheets and modals: softer lift, an inset highlight on the surface,
   the grab handle kept. Explainer boxes (the .meaning pattern, this
   game's HelpTip equivalent) lose their dashed border for a soft ring;
   key-value and feed rows trade dashes for hairlines. */
.sheet { box-shadow: 0 -14px 34px rgba(30, 21, 13, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.7); }
.modal-card { box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, 0.7); }
.meaning { border: none; background: rgba(253, 243, 218, 0.85); box-shadow: inset 0 0 0 1px rgba(232, 169, 12, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.6); }
.kv { border-bottom: 1px solid rgba(227, 215, 195, 0.7); }
.feed-item { border-bottom: 1px solid rgba(227, 215, 195, 0.7); }
.deed-check { border-color: rgba(227, 215, 195, 0.8); border-radius: 12px; background: rgba(255, 250, 240, 0.75); }
.contract-row { border-color: rgba(227, 215, 195, 0.8); border-radius: 14px; background: rgba(255, 250, 240, 0.75); }

/* The pause waiting card joins the glass family (light frost). */
.pause-card {
  background: linear-gradient(180deg, rgba(255, 253, 247, 0.97), rgba(250, 243, 228, 0.95));
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 22px 44px rgba(20, 14, 5, 0.32), inset 0 1px 0 rgba(255, 255, 255, 0.75);
}

/* ================= KP14 LANDING =================
   The landing joins the KP12/KP13 language in full. Light frosted
   glass panels with layered soft shadows and no hard borders, the
   22/18/16 radius family, glossed pill CTAs with the dock's springy
   press, ghost secondaries resting on the glass, soft inset inputs
   with a gold focus ring, and pick cards whose selected state is a
   gold ring plus a lift. The hero keeps its street scene and
   wordmark; its overlay type joins the display scale and gains the
   settled slogan line. Landing laws hold: phone first, the name,
   Quick Play and Create reachable on arrival, no sideways scroll. */
#view-landing { background: linear-gradient(180deg, var(--cream) 0%, #f3ead6 55%, #ecdfc2 100%); }
.landing-hero { padding: 30px 18px 10px; }
.hero-slogan {
  position: relative; margin-top: 9px;
  font-family: 'Aeonik Pro', sans-serif; font-size: 17px; letter-spacing: 0.2px; color: var(--gold-deep);
}
#view-landing .join-card {
  background: linear-gradient(180deg, rgba(255, 253, 247, 0.92), rgba(255, 250, 240, 0.86));
  backdrop-filter: blur(14px) saturate(1.25); -webkit-backdrop-filter: blur(14px) saturate(1.25);
  border: none; border-radius: 22px;
  box-shadow: 0 18px 38px rgba(43, 32, 24, 0.14), 0 4px 12px rgba(43, 32, 24, 0.07),
    inset 0 1px 0 rgba(255, 255, 255, 0.75), inset 0 0 0 1px rgba(227, 215, 195, 0.5);
}
#view-landing .banner {
  border: none; border-radius: 18px;
  background: linear-gradient(180deg, rgba(255, 243, 209, 0.9), rgba(255, 238, 196, 0.82));
  backdrop-filter: blur(12px) saturate(1.25); -webkit-backdrop-filter: blur(12px) saturate(1.25);
  box-shadow: 0 12px 26px rgba(120, 78, 4, 0.14), inset 0 0 0 1px rgba(232, 169, 12, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
/* CTAs: the dock pill's gloss sweep and spring, in the landing's light. */
#view-landing .btn {
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0) 46%), linear-gradient(180deg, #ffd97a, var(--gold));
  box-shadow: 0 10px 22px rgba(120, 78, 4, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.35), inset 0 -2px 6px rgba(0, 0, 0, 0.10);
  transition: transform 0.13s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.13s ease, filter 0.13s ease;
}
#view-landing .btn:active { transform: scale(0.98); box-shadow: 0 5px 12px rgba(120, 78, 4, 0.20), inset 0 1px 0 rgba(255, 255, 255, 0.35), inset 0 -2px 6px rgba(0, 0, 0, 0.10); }
#view-landing .btn.dark {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0) 46%), linear-gradient(180deg, #4a3826, var(--ink));
  box-shadow: 0 10px 22px rgba(10, 6, 2, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.22), inset 0 -2px 6px rgba(0, 0, 0, 0.25);
}
#view-landing .btn.ghost {
  background: rgba(255, 250, 240, 0.7); border: none; color: var(--ink);
  box-shadow: inset 0 0 0 1px rgba(227, 215, 195, 0.9), 0 3px 8px rgba(43, 32, 24, 0.05);
}
#view-landing .btn:disabled { opacity: 0.45; box-shadow: none; transform: none; }
/* Inputs: a soft inset fill instead of the dated boxed border; the
   focus state is a gold ring. Selects trade the native chrome for a
   chevron in the family's gold. */
.textin {
  border: none; border-radius: 14px; background-color: rgba(43, 32, 24, 0.05);
  box-shadow: inset 0 0 0 1px rgba(227, 215, 195, 0.9), inset 0 2px 5px rgba(43, 32, 24, 0.05);
  transition: box-shadow 0.15s ease, background-color 0.15s ease;
}
.textin:focus {
  outline: none; background-color: rgba(255, 250, 240, 0.95);
  box-shadow: inset 0 0 0 1px rgba(232, 169, 12, 0.55), 0 0 0 3px rgba(255, 200, 87, 0.45);
}
#view-landing select.textin {
  appearance: none; -webkit-appearance: none; padding-right: 36px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23e8a90c' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 13px center;
}
/* Pick cards, token and mode alike: soft glass with a hairline ring;
   selected is the gold ring plus a lift, never a heavier border. */
.token-pick, .mode-pick {
  border: none; border-radius: 16px; background-color: rgba(255, 250, 240, 0.75);
  box-shadow: inset 0 0 0 1px rgba(227, 215, 195, 0.85), 0 3px 8px rgba(43, 32, 24, 0.05);
  transition: transform 0.14s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.14s ease, background-color 0.14s ease;
}
.token-pick:active, .mode-pick:active { transform: scale(0.98); }
.token-pick.sel, .mode-pick.sel {
  background: linear-gradient(180deg, #fff6da, #ffedb8);
  box-shadow: 0 0 0 2px var(--gold), 0 10px 20px rgba(120, 78, 4, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
.token-pick.sel { transform: translateY(-1px); }
.divider::before, .divider::after { background: linear-gradient(90deg, transparent, rgba(180, 158, 120, 0.55), transparent); }
.landing-foot { max-width: 560px; margin: 0 auto; padding: 2px 20px 30px; }

/* ================= KP14 LANDING STRUCTURE =================
   The owner's verdict on the part-1 frame: "it's rough, make it
   structured well." One spacing system now runs the whole join card:
   a section gap of 22px (label tops, the create block, dividers) and
   a content gap of 8px (label to control, control to explainer,
   button to its caption), identical in every section: name, token,
   mode, preset, room type, house rules. The mode cards share top and
   bottom edges by construction (grid stretch plus height 100% flex
   columns, appeal pinned to the card foot); the MOST PLAYED badge
   rides inside the selected card's top corner, out of flow, so it can
   never change a card's height. The preset explainer is a 2x2 stat
   chip grid plus two short lines, not a paragraph wall. The hero
   tokens sit on one frosted strip, one baseline, even cells. The
   tagline breaks on its two written lines at a size that fits both
   at 360 and 390. */
#view-landing .join-card label { margin: 22px 0 8px; }
#view-landing .join-card > label:first-child { margin-top: 0; }
#view-landing .join-card .mt14 { margin-top: 22px; }
#view-landing .divider { margin: 22px 0; }
#presetRow .phase-chips { margin-top: 8px; }
#presetRow .preset-note { margin-top: 8px; }
#presetRow .preset-note2 { margin-top: 2px; }
.phase-chips { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.phase-chip {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 8px 11px; border-radius: 12px; background: rgba(255, 250, 240, 0.75);
  box-shadow: inset 0 0 0 1px rgba(227, 215, 195, 0.85), 0 2px 6px rgba(43, 32, 24, 0.04);
}
.phase-chip .pc-k { font-size: 11px; font-weight: 800; letter-spacing: 0.5px; text-transform: uppercase; color: var(--ink-soft); }
.phase-chip .pc-v { font-size: 14px; font-weight: 800; color: var(--ink); white-space: nowrap; }
.mode-row { align-items: stretch; }
.mode-pick { display: flex; flex-direction: column; height: 100%; }
.mode-pick .d { flex: none; }
.mode-badge { position: absolute; top: 8px; right: 10px; margin: 0; }
.mode-pick:has(.mode-badge) .t { padding-right: 96px; }
.mode-appeal { margin-top: auto; padding-top: 6px; }
.token-rain {
  margin: 16px auto 4px; max-width: 430px; padding: 10px 14px 8px;
  display: flex; justify-content: space-between; align-items: flex-end; gap: 4px;
  border-radius: 18px; background: rgba(255, 244, 220, 0.13);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.20), inset 0 0 0 1px rgba(255, 226, 160, 0.16), 0 12px 26px rgba(20, 10, 0, 0.20);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.token-rain > span { flex: 1 1 0; height: 42px; display: flex; align-items: flex-end; justify-content: center; }
.token-rain .tokart { filter: drop-shadow(0 3px 3px rgba(20, 10, 0, 0.35)); }
.landing-hero .tagline { margin-top: 10px; font-size: clamp(13.5px, 3.85vw, 15.5px); line-height: 1.35; }
@media (min-width: 900px) { .landing-hero .tagline { font-size: 19px; } }
.landing-hero .tagline .tl { display: block; }
.hero-slogan { margin-top: 10px; }

/* ================= KP14 LANDING RE-ENGINEER =================
   Information architecture, owner order 2026-10-28: JOIN WITH CODE
   AT THE TOP. First screen at 390x844, zero scroll: slim hero, name,
   the join row, Quick Play, Create room, mode cards, token strip.
   Host fine-tuning (preset, room type, house rules) sits behind one
   collapsed Room settings disclosure; defaults unchanged, no setting
   lost, only relocated. The hero token-rain strip is deleted. */

/* Slim hero: the determiner starts immediately. */
.landing-hero { padding: 18px 18px 4px; }
.landing-hero .display { font-size: 40px; }
.tagline.lead { margin-top: 6px; }
.hero-slogan { margin-top: 6px; }

/* Join is a first-class block now, not a footnote divider. */
.join-row { gap: 8px; }
.join-row .textin { height: 48px; font-size: 17px; }
.join-row .btn { flex: 0 0 104px; height: 48px; }

/* The two big play actions sit directly under the join row. */
.play-actions { margin-top: 22px; }
.play-actions .btn { height: 48px; }

/* Mode cards go compact so the pair still fits the first screen;
   equal height from the structure pass is untouched. */
#gameModeRow .mode-pick { padding: 9px 11px; gap: 3px; }
#gameModeRow .mode-pick .t { font-size: 16px; }
#gameModeRow .mode-pick .d { font-size: 11.5px; line-height: 1.3; }
#gameModeRow .mode-appeal { font-size: 11.5px; padding-top: 4px; }

/* Token pick: one compact strip of six, names stay visible. */
.token-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 5px; }
.token-pick { flex-direction: column; align-items: center; justify-content: flex-start; gap: 3px; padding: 6px 1px 5px; border-radius: 12px; font-size: 11px; line-height: 1.15; }
.token-pick svg { width: 30px; height: 30px; }
.token-pick span { text-align: center; overflow-wrap: break-word; }

/* Room settings disclosure. */
.settings-toggle {
  display: flex; align-items: center; gap: 8px; width: 100%;
  margin-top: 22px; padding: 13px 14px;
  background: rgba(255, 250, 238, 0.5);
  border: 1px solid rgba(122, 82, 33, 0.16); border-radius: 14px;
  font-family: inherit; cursor: pointer; text-align: left;
  -webkit-tap-highlight-color: transparent;
}
.settings-toggle .st-name { font-weight: 800; font-size: 14px; color: var(--ink, #241708); }
.settings-toggle .st-hint { flex: 1; font-size: 11.5px; color: rgba(59, 42, 18, 0.62); }
.settings-toggle .st-chev {
  flex: 0 0 auto; width: 9px; height: 9px;
  border-right: 2px solid rgba(59, 42, 18, 0.55); border-bottom: 2px solid rgba(59, 42, 18, 0.55);
  transform: rotate(45deg); transition: transform 0.18s ease;
}
.settings-toggle.open .st-chev { transform: rotate(225deg); }
.settings-toggle:active { transform: scale(0.99); }
#roomSettings { padding-bottom: 2px; }
