:root { --bg:#0f1419; --fg:#e6edf3; --dim:#8b98a5; --line:#22303c; --accent:#2f9e44; }
* { box-sizing: border-box; }
body { margin:0; background:var(--bg); color:var(--fg);
  font:15px/1.5 system-ui, -apple-system, Segoe UI, sans-serif; }
header { display:flex; justify-content:space-between; align-items:center;
  padding:12px 20px; border-bottom:1px solid var(--line); }
.brand { font-weight:700; letter-spacing:.02em; }
nav { display:flex; gap:10px; align-items:center; }
nav .bal { color:var(--accent); font-variant-numeric:tabular-nums; }
button { background:var(--accent); color:#fff; border:0; padding:7px 14px;
  border-radius:6px; cursor:pointer; font:inherit; }
main { padding:16px 20px; }
main h1 { font-size:1.4em; margin:0 0 12px; }
.modal { position:fixed; inset:0; background:rgba(0,0,0,.7);
  display:flex; align-items:center; justify-content:center; }
/* The `hidden` attribute works only through the UA rule `[hidden]{display:none}`,
   which ANY author `display` declaration outranks -- including the `display:flex`
   directly above. Without this the modal overlay rendered on every page load with
   an empty body, and #modal-close could not dismiss it: the handler sets
   .hidden = true and the CSS simply ignored it. Keep the attribute authoritative.
   If you ever change how .modal is displayed, this rule must stay. */
.modal[hidden] { display:none; }
.modal-box { background:#161b22; border:1px solid var(--line); border-radius:10px;
  padding:24px; min-width:340px; max-width:90vw; max-height:85vh; overflow:auto;
  position:relative; }
.close { position:absolute; top:8px; right:8px; background:none; color:var(--dim);
  font-size:22px; }
form { display:flex; flex-direction:column; gap:10px; }
input { background:#0d1117; border:1px solid var(--line); color:var(--fg);
  padding:9px; border-radius:6px; font:inherit; }
table { width:100%; border-collapse:collapse; font-variant-numeric:tabular-nums; }
td { padding:6px 8px; border-bottom:1px solid var(--line); }
.err { color:#f85149; min-height:1.2em; }

/* Balances page */
nav .nav-link { color:var(--fg); text-decoration:none; background:var(--accent);
  padding:7px 14px; border-radius:6px; font:inherit; }
/* Compact balance cards. The cards used to be 20px-padded full-width blocks
   with a 2em figure and a 16px gutter each, which pushed the buttons below the
   fold on a laptop. They now sit in an auto-fitting grid and are sized so the
   whole page -- every currency plus the request buttons -- fits on one screen.
   The balance figure stays the largest thing on the card (1.55em against a
   .9em heading): compacting must not demote the number the player came for.
   The grid collapses to one column below ~230px of available width, so this
   stays readable on a phone. */
.balance-grid { display:grid; gap:10px; margin-bottom:14px;
  grid-template-columns:repeat(auto-fit, minmax(230px, 1fr)); }
.balance-card { border:1px solid var(--line); border-radius:8px;
  padding:10px 12px; }
/* No text-transform here: vCLASH is spelled with a lowercase v on purpose and
   uppercasing it would rename the currency on screen. */
.balance-card h2 { font-size:.9em; font-weight:600; color:var(--dim); margin:0 0 2px; }
.balance-card--clash { border-color:var(--accent); }
.balance-card .amount { font-size:1.55em; line-height:1.15;
  font-variant-numeric:tabular-nums; margin:0 0 6px; }
.balance-card--clash .amount { color:var(--accent); font-weight:700; }
.balance-card .tag { font-size:.75em; color:var(--dim); font-weight:400;
  border:1px solid var(--line);
  border-radius:4px; padding:1px 5px; }
.card-actions { display:flex; gap:6px; flex-wrap:wrap; }
.card-actions button { padding:4px 10px; font-size:.9em; }
.reset-note { color:var(--dim); font-size:.8em; margin:0 0 6px; }
.balance-actions { display:flex; gap:8px; flex-wrap:wrap; margin-bottom:8px; }
.panel { border:1px solid var(--line); border-radius:8px; padding:14px;
  margin:10px 0; }
.panel[hidden] { display:none; }
.hint { color:var(--dim); font-size:.9em; }

/* Admin page (/admin). Deliberately dense: one section on screen at a time,
   its own scroller, small type -- the pattern the Balances page had to be
   rewritten into after the cards pushed everything below the fold. */
.brand-link { color:inherit; text-decoration:none; }
.admin-tabs { display:flex; gap:6px; flex-wrap:wrap; margin-bottom:10px;
  border-bottom:1px solid var(--line); padding-bottom:8px; }
.admin-tabs button { padding:5px 12px; font-size:.9em; background:#161b22;
  border:1px solid var(--line); color:var(--fg); }
.admin-tabs button[disabled] { background:var(--accent); border-color:var(--accent);
  color:#fff; cursor:default; }
.admin-section[hidden] { display:none; }
.admin-controls { display:flex; gap:6px; flex-wrap:wrap; align-items:center; margin-bottom:8px; }
.admin-controls button { padding:4px 10px; font-size:.9em; }
.admin-section table { font-size:.9em; }
.admin-section th { text-align:left; color:var(--dim); font-weight:600;
  padding:4px 8px; border-bottom:1px solid var(--line); }
.admin-section td { padding:4px 8px; }
.admin-section input { padding:5px 7px; }
/* A scroller per section, so a 200-row list never turns the page into an
   endless scroll and the tab strip stays reachable. */
.admin-scroll { max-height:62vh; overflow:auto; border:1px solid var(--line);
  border-radius:8px; }
/* FIVE tracks, because renderPlayers() in public/admin.js always emits five
   cells. This is the same trap .table-row fell into: a cell rendered only
   when it applied, against a fixed track count, wraps onto an implicit second
   row and the box grows. If you add or remove a cell there, change both. */
.admin-row { display:grid; grid-template-columns:1fr auto auto auto auto;
  align-items:center; gap:10px; padding:6px 10px; border-bottom:1px solid var(--line); }
.admin-row:last-child { border-bottom:0; }
.admin-row button { padding:4px 10px; font-size:.9em; }
.admin-row .dim { color:var(--dim); font-size:.85em; }
.admin-row .bal { color:var(--accent); font-variant-numeric:tabular-nums; }
#credit-form select { background:#0d1117; border:1px solid var(--line); color:var(--fg);
  padding:8px; border-radius:6px; font:inherit; }

/* --- Balances: the per-currency ledger and the request list ---
   Both are built out of <table> and plain block rows, NOT a CSS grid, and
   that is deliberate. .table-row and .admin-row have each already shipped the
   same bug: a fixed grid-template-columns track count against a cell that was
   only rendered when it applied, so the row wrapped onto an implicit second
   line and the box visibly grew. A table cannot fall into it -- every row has
   exactly the cells it has and the browser sizes the columns -- so there is no
   track count here to keep in sync with any client code.

   Dense on purpose. The owner has twice asked for smaller boxes: .9em type,
   4px cell padding, and the history capped at 46vh with its own scroller so a
   long ledger never turns the page into an endless scroll. */
.ledger-panel { padding:10px 12px; }
.ledger-panel h2 { font-size:.95em; margin:0 0 8px; color:var(--dim); }
.ledger-scroll { max-height:46vh; overflow:auto; border:1px solid var(--line);
  border-radius:6px; }
table.ledger { font-size:.9em; }
table.ledger th { text-align:left; color:var(--dim); font-weight:600;
  padding:4px 8px; border-bottom:1px solid var(--line); position:sticky; top:0;
  background:#161b22; }
table.ledger td { padding:4px 8px; }
table.ledger .num { text-align:right; white-space:nowrap; }
table.ledger .dim { color:var(--dim); }
table.ledger .pos { color:var(--accent); }
table.ledger .neg { color:#f85149; }
.ledger-foot { display:flex; align-items:center; justify-content:space-between;
  gap:10px; margin-top:8px; }
.ledger-foot button { padding:4px 10px; font-size:.9em; }
.ledger-foot .hint { margin:0; }

.ticket-list { padding:10px 12px; }
.ticket-list h2 { font-size:.95em; margin:0 0 8px; color:var(--dim); }
.ticket { border:1px solid var(--line); border-radius:6px; padding:8px 10px;
  margin-bottom:8px; font-size:.9em; }
.ticket:last-child { margin-bottom:0; }
.ticket-head { display:flex; flex-wrap:wrap; align-items:center; gap:8px;
  margin-bottom:4px; }
.ticket-head .tag { font-size:.8em; color:var(--dim); border:1px solid var(--line);
  border-radius:4px; padding:1px 5px; }
.ticket-head .dim { color:var(--dim); font-size:.85em; margin-left:auto; }
.ticket-line { margin:2px 0; }
.ticket-admin { color:var(--fg); }
.ticket-mine { color:var(--dim); white-space:pre-line; }
.ticket button { padding:4px 10px; font-size:.9em; margin-top:6px; }

/* The buy-chips amount and its currency belong on one line -- they are one
   statement ("250.00 CLASH"), not two questions. `form` is a column flexbox,
   so this row opts out of it. */
.field-row { display:flex; gap:8px; }
.field-row input { flex:1 1 auto; min-width:0; }
.field-row select { flex:0 0 auto; }
select { background:#0d1117; border:1px solid var(--line); color:var(--fg);
  padding:9px; border-radius:6px; font:inherit; }
.steps { margin:6px 0 0; padding-left:18px; }
.steps li { margin:1px 0; }

/* ---- Settings page: avatar picker -------------------------------------- *
 * The tile sheet is one 5632x88 PNG of PokerMavens' 64 built-in avatars, the
 * same image the poker client draws from, so what a player picks here is
 * exactly what appears at the table. Each tile is 88px wide; the index is a
 * background-position offset, not 64 separate requests. */
.av-tile{display:block;background-image:url('/avatars/sheet.png');background-repeat:no-repeat;
  /* THE TILE SIZE LIVES HERE AND NOWHERE ELSE. The server and settings.js used
     to emit `background-position:-Npx 0` computed against a hardcoded 88, so a
     context that RESCALED the tile could not rescale the offset with it: the
     dashboard header drew the tile at 56px and still offset it in 88px steps,
     landing 17.29 tiles along a 64-tile sheet -- the wrong face, plus a sliver
     of the next one. They now emit only --av-i (the zero-based tile index) and
     every length below is derived from --av-tile, so overriding that one value
     rescales the tile and its offset together. */
  --av-tile:88px; --av-i:0;
  width:var(--av-tile); height:var(--av-tile);
  background-size:calc(var(--av-tile) * 64) var(--av-tile);
  background-position:calc(var(--av-i) * var(--av-tile) * -1) 0}
.av-current{width:88px;height:88px;margin:8px 0 14px;border:2px solid rgba(255,255,255,.15);
  border-radius:6px;overflow:hidden}
.av-current img{width:88px;height:88px;object-fit:cover;display:block}
.av-grid{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
.av-opt{padding:0;border:2px solid transparent;border-radius:6px;background:none;
  cursor:pointer;line-height:0;overflow:hidden}
.av-opt img{width:88px;height:88px;object-fit:cover;display:block}
.av-opt:hover{border-color:rgba(255,255,255,.35)}
.av-opt.sel{border-color:#2f9e44}
.av-upload{display:inline-block;cursor:pointer}
.av-upload input{display:none}
.field{margin:6px 0}
.field-label{opacity:.65;margin-right:8px}
.field-value{font-weight:600}
.msg{min-height:1.2em;margin:10px 0 0}
.msg.ok{color:#2f9e44}
.msg.err{color:#e5534b}
.warn{color:#d8a657;line-height:1.5;max-width:52ch}

/* --- Landing page and /about --------------------------------------------- *
 * Dense on purpose: the owner has twice asked for smaller boxes and reads
 * this on a laptop, so the whole pitch plus the live table list is meant to
 * fit without scrolling. Type sizes here are the only thing carrying the
 * hierarchy -- there are no extra borders, cards or padded panels, because
 * that is what made the last two pages too big.
 *
 * NOTE: nothing here is a CSS grid with a fixed track count. .table-row and
 * .admin-row have each shipped the same bug (a conditionally rendered cell
 * against a fixed grid-template-columns, wrapping onto an implicit second
 * row and visibly growing the box); flex and normal flow cannot fall into it.
 */
main.landing, main.about { max-width: 900px; }
.hero { margin: 0 0 18px; }
.hero h1 { font-size: 1.7em; line-height: 1.2; margin: 0 0 6px; }
.lead { color: var(--fg); margin: 0 0 10px; max-width: 62ch; }
.pitch { list-style: none; margin: 0 0 12px; padding: 0;
  display: flex; flex-wrap: wrap; gap: 8px; }
/* Three columns on a laptop, one on a phone -- auto-fitting rather than a
   fixed count, so nothing overflows at any width. */
.pitch li { flex: 1 1 240px; min-width: 0; border: 1px solid var(--line);
  border-radius: 8px; padding: 9px 11px; font-size: .93em; line-height: 1.45;
  color: var(--dim); }
.pitch li strong { color: var(--fg); }
.hero-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.text-link { color: var(--accent); text-decoration: none; font-size: .93em; }
.text-link:hover { text-decoration: underline; }
.lobby-head { font-size: 1.1em; margin: 0 0 8px; }
main.landing h1.lobby-head { font-size: 1.4em; }
.lobby-foot { margin: 8px 0 0; }

.about h1 { margin: 0 0 6px; }
.about-sec { margin: 0 0 16px; }
.about-sec h2 { font-size: 1.02em; margin: 0 0 5px; color: var(--accent); }
.about-sec p { margin: 0 0 7px; max-width: 68ch; line-height: 1.55; }
.about-sec p.hint { max-width: 68ch; }
.about-dl { margin: 0; }
.about-dl dt { font-weight: 600; margin-top: 6px; }
.about-dl dd { margin: 2px 0 0; padding: 0; color: var(--dim);
  max-width: 66ch; line-height: 1.55; }
.about-steps { margin: 4px 0 7px; color: var(--dim); max-width: 68ch; }
.about-steps li { margin: 2px 0; line-height: 1.5; }

@media (max-width: 560px) {
  header { flex-wrap: wrap; gap: 6px; padding: 10px 14px; }
  nav { flex-wrap: wrap; gap: 6px; }
  main { padding: 14px; }
  .hero h1 { font-size: 1.35em; }
  .hero-actions button { flex: 1 1 auto; }
  /* .table-row is a six-track grid; at this width the tracks are narrower than
     their content, so let it fall back to normal flow rather than overflow the
     viewport. The track count itself is untouched -- see the note on the rule
     itself, and public/app.js#loadTables, which must keep emitting six cells. */
  .table-row { display: block; }
  .table-row > * { margin-right: 8px; }
}

/* --- The player dashboard (/settings) ------------------------------------ *
 * Dense on purpose. The owner reads this on a laptop and has twice asked for
 * smaller boxes, so the stat blocks are a WRAPPING FLEX ROW: three across on a
 * laptop, one on a phone, and nothing overflows at any width.
 *
 * NOTHING here is a grid with a fixed track count. .table-row and .admin-row
 * have each already shipped the bug where a conditionally rendered cell wraps
 * onto an implicit second row and visibly grows the box; flex and normal flow
 * cannot fall into it. */
main.dash { max-width: 900px; }
main.dash h1 { font-size: 1.4em; margin: 0 0 10px; }
main.dash .panel { padding: 11px 13px; margin: 0 0 10px; }
main.dash .panel h2 { font-size: .95em; margin: 0 0 7px; color: var(--accent); }
.stat-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 8px; }
.stat { flex: 1 1 130px; min-width: 0; border: 1px solid var(--line);
  border-radius: 6px; padding: 6px 9px; }
.stat-label { display: block; color: var(--dim); font-size: .78em; }
.stat-value { display: block; font-size: 1.05em; font-weight: 600;
  font-variant-numeric: tabular-nums; }
.stat-value.pos { color: var(--accent); }
.stat-value.neg { color: #e5534b; }
/* Identity: the avatar beside the name, wrapping rather than squashing. */
.dash-id { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.dash-id-av { width: 56px; height: 56px; border-radius: 6px; overflow: hidden;
  border: 1px solid var(--line); flex: 0 0 auto; }
.dash-id-av img { width: 56px; height: 56px; display: block; object-fit: cover; }
/* Rescaling the tile is now ONE declaration: --av-tile drives the width, the
   height, the sheet size and the offset together. This block used to restate
   width/height/background-size and leave the inline background-position at its
   88px scale, which is what broke the header avatar. */
.dash-id-av .av-tile { --av-tile: 56px; }
.dash-id-who { flex: 1 1 220px; min-width: 0; }
.dash-id-who h2 { margin: 0 0 2px; font-size: 1.05em; color: var(--fg); }
.dash-id-who .hint { margin: 0; }
main.dash .ledger { width: 100%; }
main.dash .ledger-scroll { max-height: 40vh; overflow: auto;
  border: 1px solid var(--line); border-radius: 6px; }
main.dash .ticket { margin: 0 0 7px; }

/* A visible way out of every inline panel on /balances. Same affordance as the
   modal's close button -- the same class, positioned in the panel -- because a
   second dismissal pattern on one site is one too many. The panels themselves
   stay inline; only their dismissal was missing. */
.panel { position: relative; }
.panel-close { position: absolute; top: 6px; right: 8px; font-size: 1.2em;
  line-height: 1; padding: 2px 6px; }

@media (max-width: 560px) {
  .stat { flex: 1 1 100%; }
}

/* ---- Player dashboard tabs ---------------------------------------------
   The strip reuses .admin-tabs so the two tabbed pages on the site look and
   behave the same; only the spacing differs, because the dashboard puts the
   player's identity card above it. .dash-section[hidden] needs saying
   explicitly for the same reason .admin-section[hidden] did: the panels
   inside carry display rules that would otherwise win over the attribute. */
.dash-tabs { margin: 14px 0 12px; }
.dash-section[hidden] { display: none; }
.dash-section > .tab-title { margin: 0 0 10px; font-size: 1.15em; }
.dash-section .balance-grid { margin-top: 0; }

/* =========================================================================
   MOBILE SWEEP, 2026-09-03
   ========================================================================= */

/* A long unbroken token -- a wallet address, a transaction hash, a poker name
   with no spaces -- is the classic way a dark, tidy layout acquires a
   horizontal scrollbar on a phone. Break them rather than let one push the
   whole page sideways. `anywhere` and not `break-word`: break-word will not
   break inside a word that is alone in its box, which is exactly the wallet
   address case. */
body { overflow-wrap: break-word; }
/* NOT `anywhere`. The two differ in one way that is easy to miss and cost the
   lobby its layout: `anywhere` is counted when the browser computes a box's
   MIN-CONTENT SIZE, `break-word` is not. So `anywhere` on body silently
   collapsed every min-content grid track on the site to a single character,
   and .table-row's note cell rendered one letter per line. The wallet address
   -- the case that motivated `anywhere` in the first place -- carries its own
   `overflow-wrap: anywhere` on main.currency-info .addr code, where it is
   scoped to a box that genuinely needs it. Do not widen it back to body. */
/* Prose keeps normal wrapping -- `anywhere` in a paragraph produces ugly
   mid-word breaks on ordinary text, and these boxes are never the problem. */
p, li, dd, .hint, .lead { overflow-wrap: break-word; }

/* -------------------------------------------------------------------------
   The public currency page (/currency/:code).
   It had NO styles of its own at all -- it inherited `main` and nothing else,
   so the proof-of-reserves page, the one page written to be shown to a
   sceptic, was the least finished thing on the site. It is also the page most
   likely to be opened from a link on a phone.
   ------------------------------------------------------------------------- */
main.currency-info { max-width: 720px; margin: 0 auto; }
main.currency-info h1 { display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 8px; font-size: 1.5em; margin: 0 0 4px; }
main.currency-info h1 .sym { font-size: .6em; color: var(--dim);
  font-weight: 600; letter-spacing: .04em; }
main.currency-info section { border: 1px solid var(--line); border-radius: 8px;
  padding: 14px; margin: 0 0 12px; }
main.currency-info h2 { font-size: .95em; color: var(--accent); margin: 0 0 8px; }
main.currency-info .held .amount { font-size: 1.9em; line-height: 1.15;
  margin: 0 0 6px; font-variant-numeric: tabular-nums; }
main.currency-info .held .amount strong { color: var(--accent); }
/* The address itself. It is the one string on the site guaranteed to be longer
   than a phone is wide, so it gets its own box and breaks anywhere. */
main.currency-info .addr { margin: 0 0 10px; }
main.currency-info .addr code { display: block; background: #0d1117;
  border: 1px solid var(--line); border-radius: 6px; padding: 9px 10px;
  font-size: .9em; line-height: 1.45; overflow-wrap: anywhere; }
main.currency-info .explorer { color: var(--accent); }
/* The honest "no wallet published" state should read as a caveat, not as a
   card that failed to load. */
main.currency-info .proof--none { border-style: dashed; }
main.currency-info .facts dl { display: grid;
  grid-template-columns: auto 1fr; gap: 4px 14px; margin: 0; }
main.currency-info .facts dt { color: var(--dim); }
main.currency-info .facts dd { margin: 0; }
.currency-links { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 0;
  align-items: baseline; }

/* -------------------------------------------------------------------------
   Phone.
   ------------------------------------------------------------------------- */
@media (max-width: 560px) {
  /* THE MODAL WAS WIDER THAN A SMALL PHONE. min-width:340px against a 320px
     viewport (iPhone SE, and Android at large font sizes) forced the whole
     document to scroll sideways whenever the log-in box opened -- on the
     first screen a new player sees. max-width:90vw could not save it: a
     min-width always wins over a max-width. */
  .modal-box { min-width: 0; width: 100%; max-width: 100%; padding: 18px 16px; }
  .modal { padding: 12px; }

  /* iOS SAFARI ZOOMS THE PAGE IN when a focused input's font is under 16px,
     and does not zoom back out on blur. Every form on the site inherited the
     15px body font, so tapping "Poker name" left the player on a zoomed page
     with the header off-screen. 16px is the whole fix. */
  input, select, textarea { font-size: 16px; }

  /* Comfortable touch targets. 7px of vertical padding on a 15px font is a
     ~31px tap target; the usual floor is 44. */
  button, .nav-link { min-height: 40px; padding: 9px 14px; }
  .card-actions button, .admin-controls button { min-height: 36px; }

  /* The header stacks: brand on one line, the nav under it, rather than the
     brand being squeezed to nothing by a nav carrying a balance and three
     controls. */
  header { flex-direction: column; align-items: stretch; }
  nav { justify-content: flex-start; }

  /* A tab strip of five or six wraps to two or three rows on a phone and
     pushes the content it labels below the fold. One scrolling row keeps the
     page anchored; -webkit-overflow-scrolling gives it momentum on iOS. */
  .admin-tabs { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; }
  .admin-tabs::-webkit-scrollbar { display: none; }
  .admin-tabs button { flex: 0 0 auto; }

  /* Any table still wide at this width scrolls inside its own box rather than
     taking the page with it. */
  .ledger-scroll, .admin-scroll { max-height: none; }

  /* ...but overflow:auto alone does NOT make a table scroll. With no width of
     its own a table SHRINKS to its container, and the browser then wraps every
     cell as hard as it has to: on /admin the seven-column ticket table came out
     with 26px columns, so When read as W-h-e-n stacked one letter per line and
     a ticket took most of a screen. A min-width wider than the box is what
     turns the wrapper's overflow into a real horizontal scroll, and nowrap
     headers stop the header row restacking inside it. */
  .ledger-scroll table, .admin-scroll table { min-width: 560px; }
  .ledger-scroll th, .admin-scroll th { white-space: nowrap; }

  /* The buy-chips amount and its currency picker stop sharing one line. */
  .field-row { flex-wrap: wrap; }
  .field-row select { flex: 1 1 100%; }

  /* Avatars: the sheet tiles are a fixed 88px (the background-position maths
     in lib/renderSettings.js depends on it, so they must NOT be scaled here).
     Centring the wrap keeps the ragged last row from looking broken on a
     three-per-row phone layout. */
  .av-grid { justify-content: center; }

  main.currency-info .facts dl { grid-template-columns: 1fr; gap: 0 0; }
  main.currency-info .facts dt { margin-top: 6px; }
}

/* The leaderboard page. Numbers right-aligned and tabular so ranks and balances
   line up down the column. */
main.leaderboard-page .leaderboard { width: 100%; border-collapse: collapse; }
main.leaderboard-page .leaderboard th,
main.leaderboard-page .leaderboard td { padding: 8px 10px; border-bottom: 1px solid var(--line); }
main.leaderboard-page .leaderboard .num { text-align: right;
  font-variant-numeric: tabular-nums; white-space: nowrap; }
main.leaderboard-page .leaderboard th { color: var(--text-secondary); font-weight: 600; }

/* The currency cards on the index -- how a player reaches a game now.
   auto-fit + minmax rather than a fixed track count: the owner adds currencies
   from /admin, so the number of cards is data, and a hardcoded column count
   would strand the last row the first time that number changed. */
.currency-cards { margin-top: 26px; }
.card-grid { display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.currency-card { display: grid; gap: 4px; padding: 14px;
  border: 1px solid var(--line); border-radius: 10px;
  color: inherit; text-decoration: none; }
.currency-card:hover { border-color: var(--accent); }
.cc-name { font-size: 1.1em; font-weight: 600; }
.cc-sym { font-size: .8em; color: var(--text-secondary); }
.cc-blurb { margin-top: 4px; }
.cc-tables { margin-top: 6px; font-size: .9em; color: var(--accent); }
/* The CTA when a player is already signed in: a link that has to sit level with
   the buttons beside it. */
.cta-link { display: inline-block; background: var(--accent); color: #fff;
  text-decoration: none; padding: 9px 14px; border-radius: 6px; }

/* The tables list on /currency/:code.
   overflow-x on the WRAPPER and min-width on the TABLE, both. Neither works
   alone: a table with no width of its own shrinks to fit its wrapper instead of
   overflowing it, so the wrapper never scrolls and the browser wraps cells as
   hard as it has to -- a seven-column admin table once rendered at 26px per
   column with "When" stacked one letter per line. */
.currency-tables-scroll { overflow-x: auto; border: 1px solid var(--line);
  border-radius: 8px; margin-top: 10px; }
.currency-tables { min-width: 460px; width: 100%; border-collapse: collapse; }
.currency-tables th { text-align: left; font-weight: 600; white-space: nowrap;
  color: var(--text-secondary); padding: 8px 10px; }
.currency-tables td { padding: 8px 10px; border-top: 1px solid var(--line); }
.seat-link { display: inline-block; background: var(--accent); color: #fff;
  text-decoration: none; padding: 4px 12px; border-radius: 6px; white-space: nowrap; }

/* The per-table breakdown (/admin/table/:slug). The window selector is a row
   of LINKS, not buttons -- each window is its own URL, so it can be bookmarked
   and shared -- but it must read as the same control as the tab strips
   elsewhere, so it borrows their look. */
.admin-tab-link { display: inline-block; padding: 5px 12px; font-size: .9em;
  background: #161b22; border: 1px solid var(--line); border-radius: 6px;
  color: var(--fg); text-decoration: none; }
.admin-tab-link:hover { border-color: var(--accent); }
.admin-tab-link.on { background: var(--accent); border-color: var(--accent);
  color: #fff; cursor: default; }
/* The quieter second line under a headline figure: the raw total beside the
   real-play one, so an admin never has to remember which is which. */
.stat-sub { display: block; color: var(--dim); font-size: .75em; line-height: 1.35; }

/* --- The admin-curated avatar library ------------------------------------
   The library tiles are IMAGE FILES, not sheet tiles, so they are sized here
   rather than by the background-position maths that drives .av-tile. They are
   deliberately the same 88px, so the two grids read as one set of choices at
   two origins rather than two unrelated widgets. */
.av-grid--library { margin-top: 6px; margin-bottom: 4px; }
.av-grid--library .av-opt img { width: 88px; height: 88px; object-fit: cover; display: block; }
.av-sheet-label { margin: 12px 0 2px; }
/* The admin row's thumbnail, small enough that a long library reads as a list
   rather than a gallery. */
.av-lib-thumb { width: 34px; height: 34px; border-radius: 5px; object-fit: cover;
  vertical-align: middle; margin-right: 8px; border: 1px solid var(--line); }
.av-row { align-items: center; }


/* Heads-up match buy-in buttons (lib/render.js#matchTiersHtml).
   These had NO styling at all when they shipped, so the buttons rendered hard
   against each other with nothing to separate them. auto-fit + minmax rather
   than a fixed column count, for the same reason the currency cards use it:
   the tier list is data the owner edits from /admin, so the layout must not
   assume how many there are. */
.tiers { margin-top: 22px; }
.tiers h2 { font-size: 1.1em; margin: 0 0 4px; }
.tiers > p { color: var(--dim); margin: 0 0 12px; }
.tier-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 12px;
  max-width: 520px;
}
.tier-grid .tier {
  padding: 16px 12px;
  font-size: 1.15em;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  border-radius: 8px;
}
.tier-grid .tier .fee {
  display: block;
  font-size: .62em;
  font-weight: 400;
  opacity: .8;
  margin-top: 3px;
}
.tier-grid .tier:disabled { opacity: .55; cursor: default; }
