/* Qusino -- original dark "crypto-casino" theme.
   Inspired by common genre conventions (sidebar game nav, header wallet
   dropdown, bet-slip with quick-multiplier buttons, animated coin), not a
   copy of any specific site's branding or assets. */

:root {
  color-scheme: dark;
  --bg: #0a0c12;
  --bg-elevated: #12151e;
  --bg-card: #171b28;
  --bg-input: #0e1119;
  --border: #262c3d;
  --text: #e8eaf1;
  --text-muted: #8a91a8;
  --accent: #22d67c;
  --accent-strong: #1ab568;
  --accent-contrast: #04140b;
  --danger: #ff5d6c;
  --warning: #f2b90b;
  /* STAR is a free-play/promotional currency (minted alongside QSC by
     earnSTAR, spent on votes, never itself redeemable for Qu) -- distinct
     from --accent (QSC/Qu, the "real" balances) and --warning (already used
     for the dev-only Test Seed option) so it reads as its own thing at a
     glance, not as an error/warning state. */
  --star: #8b7cf6;
  --radius-lg: 16px;
  --radius-md: 10px;
  --radius-sm: 6px;
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }

/* Client feedback (Sept 2026): "main container scrolls left to right on
   mobile. it should be fixed centered." Root cause was the usual CSS
   grid/flex trap: a grid item's (or flex item's) default min-width is
   `auto`, not 0 -- meaning it refuses to shrink narrower than its content's
   own intrinsic width even when its track is `1fr`. .game-main below (the
   coin-flip page's single-column grid item at mobile widths) and
   .game-table (a real <table>, which never shrinks below its columns'
   content width on its own) were both hitting this, and since nothing
   upstream clipped the resulting overflow, it showed up as the whole page
   panning left/right instead of just the offending element. Fixed at the
   actual sources below (min-width: 0 on the grid items, a scrollable
   wrapper around the table so only the table -- not the page -- scrolls if
   it's ever still too wide). overflow-x: hidden here is a deliberate second
   layer on top of that, not a replacement for it: it guarantees the page
   itself can never pan horizontally regardless of what a future change
   might introduce, matching "fixed centered" literally. */
html {
  overflow-x: hidden;
  width: 100%;
}

body {
  margin: 0;
  width: 100%;
  overflow-x: hidden;
  background: var(--bg);
  color: var(--text);
  line-height: 1.5;
}

a { color: inherit; }

.shell {
  display: flex;
  min-height: 100vh;
}

/* ---------- Sidebar ---------- */

.sidebar {
  width: 240px;
  flex: 0 0 240px;
  background: var(--bg-elevated);
  border-right: 1px solid var(--border);
  padding: 1.25rem 1rem;
  overflow: hidden;
  transition: width 0.18s ease, padding 0.18s ease;
}

/* ---------- Collapsible sidebar ----------
   Toggled by #sidebar-toggle in the topbar (see qusino.js's
   initSidebarToggle); the class lives on <html> rather than .sidebar/.shell
   so chrome-head's inline script can apply a remembered preference
   synchronously, before the sidebar itself is even parsed, with no flash of
   the wrong state. */

html.sidebar-collapsed .sidebar {
  width: 76px;
  flex: 0 0 76px;
  padding: 1.25rem 0.5rem;
}

html.sidebar-collapsed .brand-name,
html.sidebar-collapsed .nav-label,
html.sidebar-collapsed .nav-text {
  display: none;
}

/* Client feedback (Sept 2026): "when left panel is open, only show the
   Qusino logo but when its closed then show the emblem" -- previously both
   the icon mark (.brand-mark) and the "QUSINO" wordmark (.brand-name)
   showed together whenever the sidebar was expanded, with only the
   wordmark hiding on collapse. Now exactly one shows at a time: the full
   wordmark logo while expanded (the default, so no override needed for
   that direction), the compact emblem only once collapsed. Specificity
   here (.brand .brand-mark, two classes) is deliberately higher than
   .brand-mark's own base rule below (one class) so this hide-by-default
   wins regardless of which rule happens to come first in the file; the
   collapsed override then needs to out-specificity *that*, which
   `html.sidebar-collapsed .brand .brand-mark` does. */
.brand .brand-mark {
  display: none;
}

html.sidebar-collapsed .brand .brand-mark {
  display: inline-flex;
}

html.sidebar-collapsed .brand {
  justify-content: center;
  gap: 0;
}

html.sidebar-collapsed .nav-item {
  justify-content: center;
  gap: 0;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 1.75rem;
  padding: 0 0.25rem;
  /* Now an <a href="/"> (links back to the home/catalog page) rather than a
     plain div -- reset link styling so it still just looks like the brand
     mark, not a hyperlink. */
  color: inherit;
  text-decoration: none;
}

.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--accent-contrast);
  font-weight: 800;
  flex: 0 0 auto;
}

/* Client-provided logo mark (Sept 2026, replaces the plain-text "Q"). Sized
   smaller than the 32px badge itself so it doesn't touch the rounded
   corners -- fill is set via currentColor on the SVG itself (see
   chrome.html's brand-mark-icon template), so it automatically matches
   whatever color .brand-mark already uses here (--accent-contrast). */
.brand-mark-svg {
  width: 20px;
  height: 20px;
  display: block;
}

.brand-name {
  font-weight: 700;
  letter-spacing: 0.06em;
  font-size: 1.05rem;
}

/* Client-provided "QUSINO" wordmark (Sept 2026, replaces the plain text that
   used to be .brand-name's content). currentColor picks up the same light
   body text color the plain text used to inherit -- see .brand's
   `color: inherit` and body's --text. */
.brand-name svg {
  height: 16px;
  width: auto;
  display: block;
}

.nav-label {
  text-transform: uppercase;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin: 0 0.25rem 0.5rem;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.6rem 0.65rem;
  border-radius: var(--radius-md);
  text-decoration: none;
  color: var(--text);
  margin-bottom: 0.25rem;
}

.nav-item:hover { background: #ffffff0c; }

.nav-item--active {
  background: #ffffff14;
  box-shadow: inset 3px 0 0 var(--accent);
}

.nav-item--soon {
  opacity: 0.45;
  cursor: default;
}

.nav-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: 0 0 30px;
  border-radius: var(--radius-sm);
  background: var(--bg-card);
  font-size: 0.75rem;
  font-weight: 700;
}

.nav-text { display: flex; flex-direction: column; line-height: 1.25; }
.nav-name { font-weight: 600; font-size: 0.9rem; }
.nav-tag { font-size: 0.7rem; color: var(--text-muted); }
.nav-tag--live { color: var(--accent); }

/* ---------- Main column ---------- */

.main {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.topbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.75rem;
  padding: 1rem 1.5rem;
  border-bottom: 1px solid var(--border);
}

.tick-pill {
  margin-right: auto;
  font-size: 0.8rem;
  color: var(--text-muted);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.35rem 0.85rem;
}

.tick-pill strong { color: var(--text); }

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  border-radius: var(--radius-md);
  background: var(--bg-card);
  border: 1px solid var(--border);
  color: var(--text-muted);
  cursor: pointer;
}

.icon-btn:hover { color: var(--accent); border-color: var(--accent); }

/* ---------- Header balance pill ----------
   Always-visible Qu/QSC/STAR readout, kept in sync with the wallet dropdown
   via an hx-swap-oob block in partials/wallet.html -- see that file for why
   one fetch can update two different places on the page. */

.balance-pill {
  display: flex;
  align-items: stretch;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
}

.balance-chip {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.45rem 0.85rem;
  font-size: 0.82rem;
}

.balance-chip + .balance-chip { border-left: 1px solid var(--border); }

.balance-label {
  color: var(--text-muted);
  font-weight: 700;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.balance-value { font-weight: 700; color: var(--accent); }

/* STAR is free-play currency, not a real balance -- see the --star doc
   comment above. Distinct color plus a small "Fun mode" tag so it doesn't
   read as interchangeable with the Qu/QSC balances next to it. */
.balance-value--star { color: var(--star); }
.balance-fun-tag {
  font-size: 0.6rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--star);
  border: 1px solid var(--star);
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
  margin-left: 0.35rem;
}

/* Same tag, but placed on the bet slip's own currency selector instead of
   the header -- the currency actually in play belongs on the control the
   player is using to pick it, not just on the always-on balance display.
   `.amount-field` stretches its flex children to the toggle button's full
   height by default; center this one instead. */
#cf-asset-fun-tag {
  align-self: center;
  margin-left: 0;
  margin-right: 0.35rem;
  flex: 0 0 auto;
}

.wallet-menu { position: relative; }

.wallet-menu summary {
  list-style: none;
  cursor: pointer;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.45rem 1.1rem;
  font-weight: 600;
  font-size: 0.85rem;
}

.wallet-menu summary::-webkit-details-marker { display: none; }

.wallet-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.5rem);
  width: 320px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1rem;
  z-index: 20;
  box-shadow: 0 12px 32px #00000066;
}

.wallet-panel form {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.wallet-panel input.identity-input { flex: 1; }

/* ---------- Wallet connect modal ("qubic connect" style) ----------
   Shown in place of the wallet-menu dropdown while no wallet is connected
   yet -- see initWalletConnect in qusino.js. Three real, wired options:
   Qubic Wallet Extension, MetaMask (via its Qubic Snap), WalletConnect. */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: #05070bcc;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 1rem;
}

.modal-overlay[hidden] { display: none; }

.modal-card {
  width: 100%;
  max-width: 360px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  box-shadow: 0 20px 60px #000000aa;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.1rem;
}

.modal-title {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-weight: 800;
  font-size: 1.05rem;
}

.modal-title-text { text-transform: lowercase; letter-spacing: 0.01em; }
.modal-title-accent { color: var(--accent); }

.modal-close {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.1rem 0.4rem;
}

.modal-close:hover { color: var(--text); }

.modal-body { display: flex; flex-direction: column; gap: 0.6rem; }

.wallet-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  text-align: left;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.75rem 0.9rem;
  color: var(--text);
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
  transition: border-color 0.12s ease, transform 0.12s ease, background 0.12s ease;
}

.wallet-option-left { display: flex; align-items: center; gap: 0.75rem; }

.wallet-option:hover { border-color: var(--accent); background: var(--bg-card); transform: translateY(-1px); }
.wallet-option:disabled { opacity: 0.5; cursor: default; transform: none; }

.wallet-option::after {
  content: "\203A";
  color: var(--text-muted);
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1;
}

/* A neutral light chip behind every logo -- keeps Qubic's cyan-on-transparent
   mark, MetaMask's multi-color fox, and WalletConnect's blue glyph equally
   legible against the dark modal, without needing a different background
   color tuned to each brand's palette. */
.wallet-option-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  border-radius: var(--radius-sm);
  background: #ffffff;
  padding: 5px;
}

.wallet-option-icon img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.wallet-option-icon--qubic { background: #060a12; }

.wallet-modal-status {
  margin: 0.4rem 0 0;
  min-height: 1.1em;
  text-align: center;
}

/* Dedicated per-wallet step (install prompt, "approve in extension" wait
   state, or the WalletConnect QR pairing step below) -- swapped into
   #wallet-modal-body by connectVia()/renderWalletStep() in qusino.js,
   replacing the 3-option picker while a connection attempt is in flight. */
.wallet-step {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.wallet-step-icon {
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-md);
  background: #ffffff;
  padding: 8px;
  margin-bottom: 0.1rem;
}

.wallet-step-icon img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.wallet-step-desc {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.86rem;
  text-align: center;
  line-height: 1.5;
}

.wallet-step-spinner {
  align-self: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  animation: wallet-step-spin 0.8s linear infinite;
}

@keyframes wallet-step-spin {
  to { transform: rotate(360deg); }
}

/* WalletConnect's in-modal QR step */
.wc-qr-wrap { display: flex; justify-content: center; padding: 0.25rem 0; }
.wc-qr-wrap img { width: 200px; height: 200px; border-radius: var(--radius-md); background: #fff; padding: 10px; }

/* Copyable pairing-link fallback beneath the QR/deep-link button -- a
   secondary path for anyone testing without a phone's camera handy. */
.wc-link { display: flex; flex-direction: column; gap: 0.35rem; margin: 0.1rem 0 0.15rem; }

.wc-link-label { font-size: 0.72rem; color: var(--text-muted); text-align: center; }

.wc-link-row {
  display: flex;
  align-items: stretch;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.wc-link-value {
  flex: 1;
  min-width: 0;
  padding: 0.5rem 0.7rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.72rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.wc-link-copy {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0 0.85rem;
  background: var(--bg-card);
  border: none;
  border-left: 1px solid var(--border);
  color: var(--text);
  font-weight: 700;
  font-size: 0.78rem;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.12s ease, background 0.12s ease;
}

.wc-link-copy:hover { color: var(--accent); background: var(--bg-input); }
.wc-link-copy--done { color: var(--accent); }
.wc-link-copy-icon { flex: 0 0 auto; }

/* Dev-only "Test Seed" wallet option -- only ever rendered when the server
   was started with QUSINO_DEV_SEED_LOGIN=1 (see chrome.html/main.go), so
   this is styled to visually stand apart from the three real wallet
   options it sits below rather than blend in with them. */
.wallet-option--dev {
  border-style: dashed;
  border-color: var(--warning);
}

.wallet-option--dev:hover { border-color: var(--warning); }

.wallet-option-icon--seed {
  background: #1a1406;
  font-size: 1.1rem;
}

.wallet-option-tag {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.1rem 0.4rem;
  border-radius: var(--radius-sm);
  background: var(--warning);
  color: #1a1406;
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  vertical-align: middle;
}

/* Seed-input step: its own warning banner + text field, unlike the other
   three wallet steps which only ever show an icon/description/spinner. */
.wallet-step-warning {
  margin: 0;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--warning);
  border-radius: var(--radius-md);
  background: rgba(242, 185, 11, 0.1);
  color: var(--warning);
  font-size: 0.78rem;
  line-height: 1.5;
}

.seed-login-input {
  width: 100%;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-input);
  color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem;
}

.seed-login-input:focus { outline: none; border-color: var(--accent); }

.content {
  flex: 1;
  padding: 1.75rem;
}

.site-footer {
  padding: 1rem 1.75rem;
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 0.78rem;
}

/* ---------- Catalog ---------- */

.hero {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin-bottom: 1.5rem;
  max-width: 720px;
}

.hero h1 { margin: 0 0 0.4rem; }

/* ---------- Mascot ---------- */

.mascot { width: 64px; height: 64px; flex: 0 0 auto; }
.mascot--hero { width: 84px; height: 84px; }

.mascot-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  text-align: center;
  padding: 0.5rem 0 0.25rem;
}

.game-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1rem;
}

.game-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.25rem;
  text-align: center;
}

.game-card--soon { opacity: 0.55; }

.game-card-icon {
  width: 52px;
  height: 52px;
  margin: 0 auto 0.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: #ffffff0f;
  font-weight: 800;
  font-size: 0.95rem;
}

.game-card h2 { margin: 0 0 0.25rem; font-size: 1.05rem; }

.badge {
  display: inline-block;
  font-size: 0.72rem;
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  margin-top: 0.5rem;
}

.badge--soon { background: #ffffff14; color: var(--text-muted); }

/* ---------- Buttons ---------- */

.btn {
  display: inline-block;
  border: none;
  border-radius: var(--radius-md);
  padding: 0.55rem 1.1rem;
  font-weight: 700;
  font-size: 0.9rem;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
}

.btn--primary {
  background: var(--accent);
  color: var(--accent-contrast);
}

.btn--primary:hover { background: var(--accent-strong); }

.btn--ghost {
  background: var(--bg-input);
  color: var(--text);
  border: 1px solid var(--border);
}

.btn--ghost:hover { border-color: var(--accent); color: var(--accent); }

.btn--block { width: 100%; margin-top: 1rem; }

/* ---------- Game page ---------- */

.game-layout {
  display: grid;
  grid-template-columns: 1fr 340px;
  gap: 1.5rem;
  align-items: start;
}

.game-main {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.75rem;
  /* Grid items default to min-width: auto, not 0 -- without this a 1fr
     track still won't shrink narrower than this element's own content, which
     is exactly what was forcing the page-wide horizontal scroll on mobile
     (see the .game-table / .table-scroll fix and the html/body comment
     above for the other half of this same bug class). */
  min-width: 0;
}

/* Coin Flip's backdrop: an original "casino at night" illustration built
   entirely from CSS gradients plus one small inline-SVG tree-line tile, not
   a copy of any specific site's art -- a soft green spotlight behind the
   coin (matching the brand accent), a scattered starfield, and a tree
   silhouette along the bottom edge for depth. Layered directly on the
   background shorthand (each with its own position/size/repeat) rather than
   pseudo-elements, since nothing here needs to sit *above* the coin/flanks --
   ordinary child elements already paint over their parent's background with
   no z-index needed. */
.coin-stage {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 1.5rem;
  padding: 2.75rem 0 3rem;
  border-radius: var(--radius-lg);
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(circle, #22d67c40 0%, transparent 60%) center / 65% 90% no-repeat,
    radial-gradient(1.6px 1.6px at 10% 22%, #ffffffcc 50%, transparent 100%) no-repeat,
    radial-gradient(1.6px 1.6px at 24% 48%, #ffffff99 50%, transparent 100%) no-repeat,
    radial-gradient(1.2px 1.2px at 40% 16%, #ffffffb3 50%, transparent 100%) no-repeat,
    radial-gradient(1.6px 1.6px at 58% 34%, #ffffff8c 50%, transparent 100%) no-repeat,
    radial-gradient(1.2px 1.2px at 74% 14%, #ffffffb3 50%, transparent 100%) no-repeat,
    radial-gradient(1.6px 1.6px at 88% 30%, #ffffff99 50%, transparent 100%) no-repeat,
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='42'%3E%3Cpolygon points='30,4 52,40 8,40' fill='%230e2018'/%3E%3Cpolygon points='12,14 26,40 -2,40' fill='%230c1c15'/%3E%3Cpolygon points='48,14 62,40 34,40' fill='%230c1c15'/%3E%3C/svg%3E") left bottom / 60px 42px repeat-x,
    linear-gradient(180deg, #0a1a14 0%, #0f241b 42%, #153123 74%, #193a2a 100%) center / cover no-repeat;
}

.stage-flank {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  min-width: 76px;
  padding: 0.6rem 0.5rem;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.flank-label { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); }
.stage-flank strong { font-size: 1.1rem; }
#flip-multiplier { color: var(--accent); }

.coin-wrap { position: relative; perspective: 800px; }

.coin {
  width: 130px;
  height: 130px;
  position: relative;
  transform-style: preserve-3d;
}

/* Real coin-toss feel: accelerate from rest, hold a fast constant spin,
   decelerate onto the result. The earlier version faked this by spacing
   keyframe *values* unevenly under a flat `linear` timing function -- but
   since every full rotation starts and ends on the front face by
   construction, both the slow-start and slow-end segments landed on the
   SAME face, so front got most of the on-screen dwell time and tails only
   ever flashed by. That reads as a rigged coin, not a realistic toss.
   Fixed properly with per-keyframe `animation-timing-function`: only the
   first and last segments ease (each is a single ramp, not a repeating
   oscillation), and the 20%-80% middle -- where nearly all of the rotation
   happens -- runs at one truly constant speed, so heads and tails alternate
   with equal, even dwell time there. The only faces that legitimately get
   extra dwell are the start (at rest, always front) and the end (landing,
   correctly on whichever face actually won). */
.coin.is-flipping-heads { animation: qusino-coin-flip-heads 1.6s linear forwards; }
.coin.is-flipping-tails { animation: qusino-coin-flip-tails 1.6s linear forwards; }

@keyframes qusino-coin-flip-heads {
  0%   { transform: rotateY(0deg);    animation-timing-function: cubic-bezier(.42, 0, 1, 1); }  /* ease in from rest */
  20%  { transform: rotateY(360deg);  animation-timing-function: linear; }                       /* now at full speed */
  80%  { transform: rotateY(1260deg); animation-timing-function: cubic-bezier(0, 0, .58, 1); }   /* start easing down */
  100% { transform: rotateY(1440deg); }                                                          /* land face up */
}

@keyframes qusino-coin-flip-tails {
  0%   { transform: rotateY(0deg);    animation-timing-function: cubic-bezier(.42, 0, 1, 1); }
  20%  { transform: rotateY(360deg);  animation-timing-function: linear; }
  80%  { transform: rotateY(1410deg); animation-timing-function: cubic-bezier(0, 0, .58, 1); }
  100% { transform: rotateY(1620deg); }
}

/* Pending spin, shown for the whole "waiting for tick" window -- the bulk of
   what a player actually watches (often ~20s), versus the 1.6s reveal
   above. Same lesson as the reveal animations: a *repeating* accelerate/
   decelerate loop is the same bug at a smaller scale, since each lap's slow
   edges keep landing on the same face. Fixed the same way: accelerate once
   from rest, then hold a genuinely constant spin for as long as the wait
   lasts, via two chained animations on the same element -- a one-shot ramp
   (`qusino-coin-spinup`, fill-mode forwards) followed, after its duration,
   by a plain linear infinite loop that continues the rotation it left off.
   Constant speed means every face gets equal, even time. */
.coin.is-spinning {
  animation:
    qusino-coin-spinup 0.6s cubic-bezier(.42, 0, 1, 1) forwards,
    qusino-coin-spin-loop 0.6s linear infinite 0.6s;
}
@keyframes qusino-coin-spinup { from { transform: rotateY(0deg); } to { transform: rotateY(360deg); } }
@keyframes qusino-coin-spin-loop { from { transform: rotateY(360deg); } to { transform: rotateY(720deg); } }

.coin-face {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18%;
  backface-visibility: hidden;
  background: linear-gradient(160deg, var(--accent), var(--accent-strong));
  color: var(--accent-contrast);
  border: 3px solid #ffffff33;
  box-shadow: 0 8px 24px #00000055, inset 0 2px 6px #ffffff40;
}

.coin-face--tails { transform: rotateY(180deg); background: linear-gradient(160deg, #f2b90b, #c99206); }

.coin-art { width: 100%; height: 100%; }

.coin-badge {
  position: absolute;
  top: -0.5rem;
  right: -1.25rem;
  padding: 0.3rem 0.65rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 800;
  opacity: 0;
  transform: translateY(6px) scale(0.85);
  transition: opacity 0.3s ease, transform 0.3s ease;
  pointer-events: none;
}

.coin-badge.is-visible { opacity: 1; transform: translateY(0) scale(1); }
.coin-badge--win { background: var(--accent); color: var(--accent-contrast); }
.coin-badge--lose { background: var(--danger); color: #2a0a0d; }

form label {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  margin-top: 0.9rem;
  margin-bottom: 0.3rem;
}

form input[type="text"],
form input[type="number"] {
  width: 100%;
  background: var(--bg-input);
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: var(--radius-md);
  padding: 0.6rem 0.75rem;
  font-size: 0.95rem;
  font-family: inherit;
}

form input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.identity-input[readonly] { color: var(--accent); border-color: var(--accent); }

.wallet-connect-row {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 0.9rem;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--border);
}

.wallet-connect-row .hint { margin: 0; }

#wallet-summary { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: bottom; }

.wallet-status--connected { color: var(--accent); font-weight: 600; }
.wallet-status--error { color: var(--danger); }

.tx-pending {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.9rem;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 0.9rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.spinner {
  width: 16px;
  height: 16px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  flex: 0 0 auto;
  animation: qusino-spin 0.8s linear infinite;
}

@keyframes qusino-spin { to { transform: rotate(360deg); } }

.row { display: flex; gap: 1rem; }
.row .field { flex: 1; }

/* Amount field: asset toggle + number input + quick 1/2, 2x buttons, all in
   one bordered group (mirrors the layout convention on most crypto-casino
   bet slips). */
.amount-field {
  display: flex;
  align-items: stretch;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.amount-field input[type="number"] {
  flex: 1;
  border: none;
  border-radius: 0;
  background: transparent;
}

.amount-field input[type="number"]:focus { outline: none; }

.asset-toggle {
  flex: 0 0 auto;
  border: none;
  border-right: 1px solid var(--border);
  background: #ffffff0a;
  color: var(--accent);
  font-weight: 800;
  font-size: 0.82rem;
  padding: 0 0.9rem;
  cursor: pointer;
}

.amount-quick { display: flex; flex: 0 0 auto; }

.quick-btn {
  border: none;
  border-left: 1px solid var(--border);
  background: #ffffff0a;
  color: var(--text-muted);
  font-weight: 700;
  font-size: 0.82rem;
  padding: 0 0.75rem;
  cursor: pointer;
}

.quick-btn:hover { color: var(--accent); }

.preset-chips { display: flex; gap: 0.4rem; margin-top: 0.5rem; }

.chip {
  flex: 1;
  border: 1px solid var(--border);
  background: var(--bg-input);
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  padding: 0.35rem 0.4rem;
  font-size: 0.78rem;
  font-weight: 700;
  cursor: pointer;
}

.chip:hover { border-color: var(--accent); color: var(--accent); }

/* Heads/Tails side selection */

.side-select { display: flex; gap: 0.6rem; }

.side-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-muted);
  font-weight: 700;
  font-size: 0.88rem;
  padding: 0.7rem 0.5rem;
  cursor: pointer;
}

.side-icon { width: 22px; height: 22px; flex: 0 0 auto; }
.side-icon svg { width: 100%; height: 100%; }

.side-btn--active {
  background: #22d67c1f;
  border-color: var(--accent);
  color: var(--accent);
}

.payout-preview {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 1rem;
  padding: 0.7rem 0.9rem;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: 0.85rem;
  color: var(--text-muted);
}

.payout-preview strong { color: var(--accent); font-size: 1rem; }

.payout-preview-values {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.1rem;
}

.payout-profit {
  font-size: 0.72rem;
  color: var(--text-muted);
}

.game-side { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }

.panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.25rem;
}

.panel h3 { margin: 0 0 0.6rem; font-size: 0.95rem; }

.recent-flips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.4rem; }

.flip-chip {
  font-size: 0.78rem;
  font-weight: 700;
  padding: 0.3rem 0.65rem;
  border-radius: 999px;
}

.flip-chip--win { background: #22d67c26; color: var(--accent); }
.flip-chip--lose { background: #ff5d6c26; color: var(--danger); }

/* ---------- Shared bits ---------- */

.muted { color: var(--text-muted); font-size: 0.88rem; }

.error {
  color: var(--danger);
  font-weight: 600;
}

.mock-banner {
  background: #f2b90b1f;
  color: var(--warning);
  border: 1px solid #f2b90b4d;
  border-radius: var(--radius-md);
  padding: 0.6rem 0.9rem;
  margin-bottom: 0.9rem;
  font-size: 0.82rem;
  font-weight: 600;
}

.outcome-headline { font-size: 1.15rem; font-weight: 800; margin: 0 0 0.75rem; }
.outcome-win { color: var(--accent); }
.outcome-lose { color: var(--danger); }

.sub-details { margin-top: 0.75rem; }
.sub-details summary { cursor: pointer; color: var(--text-muted); font-size: 0.82rem; }

/* Advanced: transaction speed (Sept 2026) -- see initTickMarginSettings in
   qusino.js and getTickOffset's comment for what this actually controls. */
.tick-margin-settings { margin-top: 0.5rem; display: flex; flex-direction: column; gap: 0.35rem; }
.tick-margin-settings label { font-size: 0.78rem; color: var(--text-muted); }
.tick-margin-settings input {
  width: 100%;
  padding: 0.4rem 0.6rem;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 0.85rem;
}
#tick-margin-hint { font-size: 0.76rem; margin: 0; }
#tick-margin-hint.hint--warning { color: var(--warning); }

dl.balances {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.3rem 1rem;
  margin: 0.5rem 0;
  font-size: 0.88rem;
}

dl.balances dt { color: var(--text-muted); font-weight: 600; }
dl.balances dd { margin: 0; word-break: break-all; text-align: right; }

code {
  font-size: 0.78rem;
  word-break: break-all;
  color: var(--text-muted);
}

/* ---------- Earn / Transfer / Proposals ---------- */

.page-title { margin: 0 0 0.3rem; }
.page-intro { max-width: 640px; margin-bottom: 1.5rem; }

.platform-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1rem;
  align-items: start;
  margin-bottom: 1.5rem;
}

.platform-grid .panel h3 { margin-top: 0; }

.panel-result:empty { display: none; }
.panel-result { margin-top: 0.9rem; }

/* Real <table>s never shrink below their columns' content width on their
   own (a 60-char Proposer identity, an untruncated URI, etc. can still add
   up past a phone's viewport even with word-break on individual cells) --
   scrolling *this box* horizontally when that happens keeps it contained to
   the table itself instead of dragging the whole page along, which is what
   "main container scrolls left to right on mobile" was actually caused by
   on the Proposals page. See partials/game_list.html for the wrapper. */
.table-scroll {
  overflow-x: auto;
  max-width: 100%;
}

.game-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  margin-top: 0.5rem;
}

.game-table th,
.game-table td {
  text-align: left;
  padding: 0.45rem 0.6rem;
  border-bottom: 1px solid var(--border);
}

.game-table th { color: var(--text-muted); font-weight: 600; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; }

.game-uri { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.game-vote-cell { white-space: nowrap; }

.btn--small { padding: 0.3rem 0.6rem; font-size: 0.78rem; }

@media (max-width: 860px) {
  .shell { flex-direction: column; }

  /* ---------- Flattened mobile header ----------
     The sidebar becomes a compact two-row strip -- brand on its own row,
     then every nav icon (Games and Platform together) centered on the row
     below -- sitting directly above the topbar (tick + wallet) with no
     visual seam between them: same background, one border at the very
     bottom of the pair, so the two together read as a single header block
     rather than two stacked boxes. */
  .sidebar,
  html.sidebar-collapsed .sidebar {
    width: 100%;
    flex: none;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    row-gap: 0.6rem;
    padding: 0.75rem 1rem 0.6rem;
    border-right: none;
    border-bottom: none;
    /* Overridden from the base rule -- that overflow:hidden exists only to
       crop text mid-animation while the desktop sidebar collapses/expands.
       Flattened here, the same overflow:hidden instead silently clipped
       the Platform icons (Transfer/Proposals) off the right edge the
       moment Games + Platform's combined width ran past the viewport, so
       they weren't just cramped, they were invisible. */
    overflow: visible;
  }
  /* Forces its own row regardless of exact rendered width (font metrics
     make that width too close to the viewport to predict reliably) --
     without this, brand and the nav icons raced for the same line and lost
     by a handful of pixels on some phones, each wrapping onto its own row
     with the *desktop* vertical list's spacing between them, which is
     where the disjointed, gap-heavy look came from. */
  .brand { flex-basis: 100%; justify-content: center; margin-bottom: 0; }
  .game-nav { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.35rem; }
  .game-layout { grid-template-columns: 1fr; }
  .game-uri { max-width: 140px; }
  /* The collapse toggle exists to save horizontal space in the fixed-width
     desktop sidebar -- at this width the sidebar already reflows to a
     horizontal strip on its own, so the button (and any collapsed state
     from a wider session) would just be confusing here. */
  #sidebar-toggle { display: none; }
  html.sidebar-collapsed .brand-name { display: inline; }
  /* Same reasoning as the .brand-name override just above -- a "collapsed"
     preference carried over from a wider session shouldn't leave mobile
     showing the emblem instead of the wordmark when there's no way to
     toggle it back here. */
  html.sidebar-collapsed .brand .brand-mark { display: none; }
  /* Icon-only nav pills once the sidebar flattens into a horizontal header
     strip -- unconditionally, not just while "collapsed", since "Games" /
     "Platform" section labels and each item's spelled-out name made sense
     stacked in a 240px-wide vertical list, but crammed inline between icons
     here they just read as clutter. Every nav-item still carries a title
     attribute for a hover tooltip, so nothing is actually lost. */
  .nav-label { display: none; }
  .nav-text { display: none; }
  .nav-item { gap: 0; padding: 0.5rem; margin-bottom: 0; }
  .nav-icon { width: 28px; height: 28px; flex: 0 0 28px; }
  /* A filled accent icon reads as an active "tab" on a horizontal strip --
     the desktop list's thin inset left-edge bar (meant for a vertical row
     of full-width items) was easy to miss squeezed onto an icon-only
     pill. */
  .nav-item--active { box-shadow: none; background: transparent; }
  .nav-item--active .nav-icon { background: var(--accent); color: var(--accent-contrast); }
  /* Games and Platform are two separate <nav class="game-nav">s (see
     chrome-sidebar in chrome.html) -- a visible divider between them keeps
     it clear they're different sections rather than one row of 6 icons. */
  .platform-nav { padding-left: 0.5rem; border-left: 1px solid var(--border); }
  .topbar {
    flex-wrap: wrap;
    row-gap: 0.5rem;
    background: var(--bg-elevated);
    border-bottom: 1px solid var(--border);
  }
  /* 4 chips (Qu/QSC/STAR/QST) no longer reliably fit one row at this width
     once QST was added -- wrap instead of forcing a horizontal squeeze that
     the flex default (children don't shrink below their own text's width)
     would otherwise have pushed into page-level overflow. */
  .balance-pill { order: 1; flex-basis: 100%; flex-wrap: wrap; }
  .balance-chip { flex: 1 1 auto; justify-content: center; }
}

@media (max-width: 600px) {
  /* ---------- Simplified mobile header ----------
     hamburger + tick + Qu/QSC/STAR balance pill + Connect Wallet no longer
     fit as one legible row at phone widths. The balance pill is dropped
     entirely rather than squeezed or wrapped -- the identical numbers are
     always one tap away in the wallet dropdown (partials/wallet.html's
     #wallet-content renders the same dl.balances) -- and the tick pill
     drops its epoch suffix (see .tick-epoch / partials/tick.html), leaving
     just hamburger, tick number, and the wallet button on one row. */
  .topbar { padding: 0.65rem 0.85rem; gap: 0.5rem; flex-wrap: nowrap; }
  .balance-pill { display: none; }
  .tick-epoch { display: none; }
  .tick-pill { font-size: 0.72rem; padding: 0.3rem 0.6rem; }
  .wallet-menu summary { padding: 0.4rem 0.8rem; font-size: 0.8rem; }
  #wallet-summary { max-width: 96px; }

  /* ---------- Centered content ----------
     Page headings, intro copy, and the catalog hero were left-aligned
     against a wide desktop content column; in a single narrow column that
     reads as off-balance rather than intentional, so this centers them. */
  .content { text-align: center; }
  .page-intro { margin-left: auto; margin-right: auto; }
  .hero { flex-direction: column; margin-left: auto; margin-right: auto; }
  .coin-stage { flex-wrap: wrap; row-gap: 1rem; }
  .mascot-empty { margin: 0 auto; }

  /* Form text stays left-aligned even though the page around it is
     centered -- centered labels/hints above a full-width, left-aligned
     input look broken, not tidy. */
  form,
  .panel,
  .game-main,
  .game-side { text-align: left; }

  /* Two-column layouts that assumed real desktop width: side-by-side form
     field pairs (Transfer/Proposals) and the Earn/Transfer/Proposals
     action cards both need a single column at this width, or they either
     overflow horizontally or squeeze every field too narrow to use. */
  .row { flex-direction: column; gap: 0.5rem; }
  .platform-grid { grid-template-columns: 1fr; }
}
