/* ============ SPINARMORY — tokens + base ============
   Sky-blue (#34c0eb) brand accent on cool navy-black surfaces.
   Accent is the brand hue; wins/positive stay GREEN (--green), losses RED.
   Text on accent = cool near-black (#04141C), never pure black/white.
   Glows = rgba(52,192,235,.45), never white shadows on accent.
*/
:root {
  /* Surfaces — operator rebuild: 4-tier neutral gray stack (was a cool
     navy palette with a blue tint that the operator wanted to retire).
     Hue is fully de-saturated; each tier is a slightly lighter shade of
     near-black so elevation reads through lightness alone. */
  --bg-0: #0A0A0E;   /* page background (deepest) */
  --bg-1: #131316;   /* sidebar, chat rail, topbar */
  --bg-2: #18181C;   /* default card / panel / input */
  --bg-3: #1F1F24;   /* raised card, hovered row, active nav surface */

  /* Hairlines — cool by default, warm only when "active" */
  --line:    rgba(148, 163, 209, 0.10);
  --line-2:  rgba(148, 163, 209, 0.22);
  --line-3:  rgba(52, 192, 235, 0.38);

  /* Text */
  --text:      #F3F5FA;
  --text-dim:  #A6AECB;
  --text-mute: #6B7599;

  /* Accent — sky blue (#34c0eb). Win/positive stays GREEN via --green below. */
  --accent:        #34c0eb;
  --accent-rgb:    52, 192, 235;
  --accent-bright: #6FD4F2;
  --accent-2:      #2487AD;
  --accent-deep:   #0E3A4A;
  --accent-glow:   rgba(52, 192, 235, 0.45);

  /* Contrast on accent fills — cool near-black, NEVER #000 */
  --on-accent: #04141C;

  /* Semantic */
  --red:  #F24D5C;
  --blue: #3D7EFF;

  /* Legacy aliases — every existing `var(--xxx)` resolves to the new palette */
  --bg:          var(--bg-0);
  --panel:       var(--bg-2);
  --panel-2:     var(--bg-3);
  --line-soft:   var(--line);
  --accent-ink:  var(--on-accent);
  --danger:      var(--red);
  /* Wins/positive = GREEN, losses = RED. The brand accent is BLUE, so the
     win/success tokens are pinned to an explicit green here (NOT var(--accent))
     — every win amount, win chip, positive P&L, verified badge and "ok"/online
     indicator resolves through these so they stay green while the brand is blue. */
  --green:        #3FBF77;          /* canonical WIN/positive green */
  --green-soft:   rgba(63,191,119,.15);
  --green-bright: #6FE0A0;          /* lighter green — low win-multiplier tier */
  --success:     var(--green);     /* success/positive indicator — green */
  --gold:        var(--accent);    /* legacy alias — now the blue brand accent */
  --cyan:        var(--blue);
  --magenta:     var(--red);
  --purple:      var(--blue);
  --purple-2:    #1E3A8A;
  --purple-3:    #0F1B3D;
  --purple-ink:  var(--text);
  --purple-glow: rgba(61,126,255,.35);
  --blue-1:      #1E3A8A;
  --blue-2:      var(--blue);
  --blue-3:      var(--blue);

  /* Layout — Phase 24 fluid responsive pass.
     The classical pattern (fixed px swapping at @media boundaries) leaves
     "weird" zones between breakpoints where the main content area gets
     starved when the chat panel and rail are both visible. These clamps
     let every structural width SHRINK PROPORTIONALLY between breakpoints
     so layouts always render the same as full-desktop, just smaller.

     --main-w is updated by a ResizeObserver in app.jsx to track the
     actual computed main-column width (viewport minus open panels),
     so component-level clamps can react to "real" available width
     regardless of which panels are open. */
  --rail-w: 232px;
  --rail-w-collapsed: 72px;
  --right-w: 336px;
  --bottom-nav-h: 64px;
  --content-max: 1180px;
  --content-gutter: 40px;
  --main-pad: clamp(10px, 2.2vw, 26px);
  --card-pad: clamp(12px, 1.6vw, 22px);
  --gp-panel-w: clamp(240px, 24vw, 320px);
  --activity-multiplier-col: clamp(56px, 7vw, 80px);
  /* First-paint default — overwritten by app.jsx ResizeObserver. */
  --main-w: 100vw;

  /* === RADIUS LADDER ===
     Existing --radius (14) and --radius-sm (8) keep their values; the
     other rungs name the literals that already cluster in the codebase
     (4 → badges, 6 → micro-pills, 10 → nav rows, 18 → hero-card,
     22 → hero-main / modal-shell, 999 → fully-rounded). */
  --radius-2xs:  3px;     /* hairline progress bars, micro-dots */
  --radius-xs:   4px;
  --radius-sm:   8px;     /* (existing — chips, small inputs) */
  --radius-md:   10px;    /* nav-items, ghost buttons, dropdown rows */
  --radius:      14px;    /* (existing — cards, modals, primary surfaces) */
  --radius-lg:   18px;    /* hero-card pull-outs */
  --radius-xl:   22px;    /* hero-main, modal-shell */
  --radius-pill: 999px;   /* fully-rounded toggles, play button */

  /* === SPACING SCALE ===
     Derived from natural clusters — gap/padding cluster heavily at
     4/6/8/10/12/14/16/20/24/32/40. New code uses these tokens; existing
     literals migrate in Phase 2.  */
  --space-0:  0;
  --space-1:  4px;
  --space-2:  6px;
  --space-3:  8px;
  --space-4:  10px;
  --space-5:  12px;       /* default gap */
  --space-6:  14px;       /* default card padding */
  --space-7:  16px;
  --space-8:  20px;
  --space-9:  24px;
  --space-10: 32px;
  --space-11: 40px;
  --space-12: 56px;

  /* === TYPE SCALE ===
     Captures every font-size that already appears with frequency >= 5
     in styles.css/additions.css. Mono labels and small UI captions live
     at the small end; section headlines at 28px (sec-head h2). */
  --text-2xs:  10px;      /* tiny mono labels, micro-stats */
  --text-xs:   11px;      /* mono labels, kbd hint, kicker */
  --text-sm:   12px;      /* secondary text, captions */
  --text-md:   13px;      /* default UI text */
  --text-base: 14px;      /* body */
  --text-lg:   16px;      /* emphasized body */
  --text-xl:   18px;      /* card titles */
  --text-2xl:  22px;      /* sub-headings */
  --text-3xl:  28px;      /* sec-head h2 */

  /* === SHADOW LADDER ===
     Captures the patterns the audit identified:
       sm  — flat hairline, resting cards
       md  — lift, hover states on rows / chips
       lg  — float, dropdowns / popovers
       xl  — drop, modals
     Plus brand-tinted glows on a separate semantic axis. */
  --shadow-1:      0 1px 0 rgba(255,255,255,.03) inset, 0 0 0 1px var(--line);
  --shadow-sm:     var(--shadow-1);
  --shadow-md:     0 6px 18px rgba(0,0,0,.35);
  --shadow-lg:     0 14px 32px rgba(0,0,0,.45);
  --shadow-xl:     0 24px 80px rgba(0,0,0,.7), 0 0 0 1px var(--line);
  --shadow-accent: 0 10px 40px var(--accent-glow), 0 0 0 1px var(--line-3);
  --shadow-danger: 0 10px 28px rgba(242,77,92,.45);

  /* === ON-ACCENT OPACITY LADDER ===
     The audit found 121 rgba(52,192,235,X) literals scattered across the
     codebase. These pre-mix the accent at the common opacities call sites
     actually use, so future code references the token instead of the raw
     rgba. Phase 2.3 migrates existing literals onto these. */
  --accent-05:  rgba(52, 192, 235, 0.05);
  --accent-10:  rgba(52, 192, 235, 0.10);
  --accent-15:  rgba(52, 192, 235, 0.15);
  --accent-25:  rgba(52, 192, 235, 0.25);
  --accent-40:  rgba(52, 192, 235, 0.40);
  --accent-60:  rgba(52, 192, 235, 0.60);

  /* === ON-WHITE OPACITY LADDER ===
     187 rgba(255,255,255,X) literals — these are scrims and elevated
     backgrounds. Pre-mixed once. */
  --white-02: rgba(255, 255, 255, 0.02);   /* faint elevated bg */
  --white-04: rgba(255, 255, 255, 0.04);   /* nav hover bg */
  --white-06: rgba(255, 255, 255, 0.06);   /* row hover bg */
  --white-10: rgba(255, 255, 255, 0.10);

  /* === MOTION TOKENS ===
     The 0.12s / 0.15s / 0.2s / 0.3s clusters appear hundreds of times.
     The "lift" easing is the cubic-bezier already used by .modal-shell
     entrance animation; promoted here so all springy interactions match. */
  --t-fast: 0.12s;
  --t-base: 0.15s;
  --t-med:  0.2s;
  --t-slow: 0.3s;
  --ease:   cubic-bezier(.4, .8, .3, 1.1);

  /* === BREAKPOINT REFERENCE ===
     Custom properties cannot be used inside @media queries directly, so
     these are documentation only — but the Phase 3 lint guardrail
     enforces these are the only breakpoints used in the codebase. The
     comment block below is the source of truth.

     --bp-xs:  375px  — tiny phones (iPhone SE)
     --bp-sm:  540px  — phone portrait
     --bp-md:  720px  — phone landscape / small tablet
     --bp-lg:  860px  — tablet                ← primary mobile cutoff
     --bp-xl:  1180px — desktop full-rail layout
  */

  /* Operator typography refresh — Onest read as cramped at chat /
     bet-panel / table sizes. UI body swapped back to Inter (the
     standard premium-casino body face used by Stake / Shuffle /
     Roobet / BC.Game). Sora kept for display so headings remain
     chunky and casino-bold. JetBrains Mono kept for amounts / IDs /
     odds. Default UI weight stays at 500 (set on body) so body copy
     reads solid, not spindly. */
  --font-display: "Sora", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-ui: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", monospace;
  /* Operator rebuild — secondary/subtitle font weight floor. The
     default 400/500 Inter renders as the "thin generic AI" look the
     operator flagged on labels and subtitles. New rule: anything
     using --font-ui at "secondary" sizes (12-13px labels, kickers)
     uses 600 minimum. Display headings already use Sora 700-900. */
  --font-weight-body: 500;
  --font-weight-secondary: 600;
  --font-weight-strong: 700;

  /* === SEMANTIC ALIAS LAYER (operator visual rebuild) ===========
     Pure aliases on top of the existing tokens. No existing rule
     stops working — these names are for new code so it doesn't have
     to know whether the underlying value is `--line` or `--bg-3`.
     Conforms to the rebuild brief's required token coverage. */

  /* Surface */
  --bg-elevated:    #28282E;            /* popovers / tooltips above modals (neutral gray) */

  /* Borders */
  --border-subtle:  var(--line);
  --border-default: var(--line-2);
  --border-strong:  var(--line-3);

  /* Text */
  --text-primary:   var(--text);
  --text-secondary: var(--text-dim);
  --text-tertiary:  var(--text-mute);
  --text-disabled:  rgba(166, 174, 203, 0.40);

  /* Accent semantic */
  --accent-hover:    var(--accent-bright);
  --accent-pressed:  var(--accent-2);
  --accent-soft:     var(--accent-15);
  --accent-contrast: var(--on-accent);

  /* Semantic state colors + soft variants for tinted backgrounds */
  --color-success:      #22C55E;
  --color-success-soft: rgba(34, 197, 94, 0.14);
  --color-warning:      #F5A524;
  --color-warning-soft: rgba(245, 165, 36, 0.14);
  --color-danger:       var(--red);
  --color-danger-soft:  rgba(242, 77, 92, 0.14);
  --color-info:         var(--blue);
  --color-info-soft:    rgba(61, 126, 255, 0.6);

  /* Win — gold, distinct from success-green per brand spec */
  --color-win:          var(--green);
  --color-win-soft:     var(--green-soft);

  /* Motion (durations + named easings per rebuild brief) */
  --dur-fast:    120ms;
  --dur-base:    200ms;
  --dur-slow:    320ms;
  --dur-slower:  480ms;
  --ease-standard:   cubic-bezier(.2, 0, 0, 1);
  --ease-entrance:   cubic-bezier(0, 0, .2, 1);
  --ease-exit:       cubic-bezier(.4, 0, 1, 1);
  --ease-emphasized: cubic-bezier(.2, 0, 0, 1);

  /* Z-index scale */
  --z-base:     1;
  --z-sticky:   100;
  --z-dropdown: 1000;
  --z-modal:    5000;
  --z-toast:    6000;
  --z-tooltip:  7000;
}

*{ box-sizing: border-box; }
html, body{
  margin:0; padding:0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 14px;
  /* Phase 26.9 — bump body baseline from 400 → 500 so Onest body
     copy reads with the heavier baseline the casino-bold refresh
     calls for, without becoming a blob at small sizes. */
  font-weight: 500;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
  /* 100dvh tracks the dynamic viewport so the page doesn't visibly
     jump when the mobile address bar collapses/expands or the
     on-screen keyboard opens. Older engines fall back to the static
     100vh value just below. */
  height: 100vh;
  height: 100dvh;
  /* Pin the brand-dark colour at the html/body layer too — combined
     with overscroll-behavior-y on the document, the iOS rubber-band
     area no longer flashes white when over-scrolling and the page
     feels native (no halo above the topbar / below the bet feed). */
  background: var(--bg-1, var(--bg));
  overscroll-behavior-y: none;
}

/* ============================================================
   Task #18 — casino-bold button-weight safeguard.
   Sora (the display font) is loaded at 600-900 only. Button
   rules across the codebase set `font-family: var(--font-display)`
   without an explicit `font-weight`, which makes them inherit the
   body's 500 baseline — that maps to an awkward in-between weight
   that visibly reads thinner than the surrounding headings.
   These base rules guarantee that any <button> (and the common
   .btn-* utilities) renders at a loaded Sora weight even when the
   per-component rule forgets to declare one. Specific rules below
   may still bump primary/CTA buttons to 800 for extra punch. */
button { font-weight: 700; }
.btn-primary, .btn-danger, .reward-btn, .wallet-btn,
.tb-signup { font-weight: 800; }
.btn-ghost, .tb-login, .reward-card .r-btn,
.redeem-box button, .right-tabs button, .activity-tabs button,
.auth .a-tabs button, .auth .a-social button,
.pill-toggle button, .rail-mode button, .arrow-btn,
.icon-btn, .right-fab { font-weight: 700; }

/* ============================================================
   Operator rebuild — global focus-visible style.
   The brief mandates a non-default focus state on every interactive
   element. We replace the browser's blue ring with a brand-amber
   offset glow so keyboard users see a clear, in-palette indicator
   on every <button>, <a>, <input>, <select>, <textarea> AND any
   element with [role="button"] / [role="tab"] / [tabindex].
   `:focus-visible` is mouse-aware — only fires on keyboard focus
   so click interactions don't get a ring.
   ============================================================ */
:where(button, a, input, select, textarea, summary, [role="button"], [role="tab"], [role="menuitem"], [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
/* Component-level focus rings (e.g. .auth .a-submit, .rl-box-btn) that
   declared their own outline: stay as authored — they already match
   the brand. The :where() above has 0 specificity so any explicit
   focus-visible rule wins. */

/* Body background — operator rebuild: a soft top-down fade overlay
   so the page reads with depth (slightly lifted at the top, deepening
   toward the bottom). Sub-2% opacity on the highlight keeps it
   atmospheric — never bleeds into modals or backdrops. */
body::before{
  content:"";
  position: fixed; inset:0;
  background:
    radial-gradient(80% 50% at 50% 0%, rgba(255,255,255,.018), transparent 70%),
    radial-gradient(60% 40% at 50% 100%, rgba(0,0,0,.35), transparent 70%),
    var(--bg);
  z-index: 0;
  pointer-events: none;
}
/* Cards / panels — subtle inner top highlight so their top edge
   catches just enough light to read as elevated against the body
   fade above. Applied to the most common card surfaces; opt-in
   elsewhere via the .surface-fade utility class. */
.panel,
.gp-panel,
.gp-canvas,
.gp-stat-strip,
.modal-shell,
.surface-fade {
  position: relative;
}
.panel::before,
.gp-panel::before,
.gp-canvas::before,
.gp-stat-strip::before,
.modal-shell::before,
.surface-fade::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.06), transparent);
  pointer-events: none;
  z-index: 1;
}
/* Grain overlay DISABLED.
   Every previous version — green-tinted, amber-tinted, grayscale —
   interacted unpredictably with `mix-blend-mode` on top of the palette's
   warm yellow surfaces and produced the persistent green/lime cast the
   user kept seeing across sidebars, popups, game canvases, and chat.
   Clean dark surfaces are on-spec ("high-end, controlled"); the grain
   was costing more than it was adding. */
body::after{ display: none; }

#root{
  position: relative; z-index: 2;
  /* Same dvh trick as html/body — keeps #root flush with the dynamic
     viewport so the bottom of the chat/bet feed doesn't get hidden
     under the iOS address bar when it slides back in. */
  height: 100vh;
  height: 100dvh;
}

/* ============ app shell ============ */
.app{
  display: grid;
  grid-template-columns: var(--rail-w) 1fr var(--right-w);
  grid-template-rows: 68px 1fr;
  grid-template-areas:
    "brand topbar topbar"
    "rail  main   right";
  height: 100vh;
  height: 100dvh;
  transition: grid-template-columns .35s cubic-bezier(.6,.1,.2,1);
}
html, body{ min-width: 0; }
body{ overflow-x: hidden; }
/* Native-app-feel — kill the browser's double-tap-to-zoom gesture on
   mobile. `touch-action: manipulation` keeps scroll + pan working but
   suppresses the 300ms tap delay AND double-tap zoom. Paired with the
   viewport meta's `maximum-scale=1, user-scalable=no` so pinch-zoom
   is also disabled. Operator brief: "if user clicks fast two times
   it does a zoom, make it not possible to zoom on mobile like a
   real app." */
html { touch-action: manipulation; }
body { touch-action: manipulation; }
.app.rail-collapsed{ grid-template-columns: var(--rail-w-collapsed) 1fr var(--right-w); }
.app.right-closed{ grid-template-columns: var(--rail-w) 1fr 0; }
.app.rail-collapsed.right-closed{ grid-template-columns: var(--rail-w-collapsed) 1fr 0; }

/* tablet — collapse sidebar to icons-only, narrow the chat panel.
   Phase 24: --right-w now uses clamp() so it shrinks fluidly between
   1180px and 860px instead of jumping to a fixed 300px. */
@media (max-width: 1180px){
  :root{ --rail-w: var(--rail-w-collapsed); --right-w: clamp(220px, 26vw, 300px); }
  /* Force rail contents into collapsed mode at this width */
  .rail .rail-mode,
  .rail .rail-search,
  .rail .n-label,
  .rail .n-badge,
  .rail .section-label,
  .rail .rail-player-top .rp-user,
  .rail .rail-player-top .rp-tier,
  .rail .rail-player-top .rp-emblem,
  .rail .rail-player-top .bar,
  .rail .rail-player-top .xp{ display: none; }
  .rail .rail-player-top{ padding: var(--space-1); margin: 0 2px 10px; }
  .rail .rail-player-top .rp-avatar{ width: 32px; height: 32px; font-size: 14px; border-radius: var(--radius-sm); }
  .rail .rail-player-top .rp-top{ grid-template-columns: 1fr; justify-items: center; }
  .rail .nav-item{ justify-content: center; padding: 10px 0; }
  .rail{ padding: 12px 8px; }
}

/* mobile — stack: topbar + main only, rail + right become drawers */
@media (max-width: 860px){
  .app{
    grid-template-columns: 1fr !important;
    /* Topbar row grows by safe-area-inset-top so the chrome that
       paints under the iPhone notch is added ON TOP of the 58px
       content row. The drawer offsets below use the same
       calc(58px + env(safe-area-inset-top)) so they stay aligned
       with the topbar's actual painted bottom edge. */
    grid-template-rows: calc(58px + env(safe-area-inset-top, 0px)) 1fr;
    grid-template-areas:
      "topbar"
      "main";
  }
  .brand{ display:none !important; }
  .rail{
    /* Start at the topbar's painted bottom edge (58px row + the
       safe-area inset) so the sticky topbar stays visible and its
       hamburger/close control isn't trapped under the drawer.
       Tracks the matching grid row above so they cannot drift. */
    position: fixed;
    inset: calc(58px + env(safe-area-inset-top, 0px)) auto var(--bottom-nav-h) 0;
    width: 280px; max-width: 85vw;
    z-index: 50;
    border-right: 1px solid var(--line);
    transform: translateX(-100%);
    transition: transform .3s cubic-bezier(.6,.1,.2,1);
    background: var(--bg-2);
    box-shadow: 20px 0 60px rgba(0,0,0,.6);
  }
  .app:not(.mobile-nav-open) .rail{ transform: translateX(-100%); }
  .app.mobile-nav-open .rail{ transform: translateX(0); }
  .rail{
    overflow-y: auto !important;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    /* No bottom padding: the sticky .rail-player-dock must sit FLUSH to the
       rail's bottom. A bottom padding here offsets the sticky stop above the
       container's true bottom, leaving a gap where scrolling nav items peek
       out below the dock. The dock supplies its own bottom padding (incl.
       safe-area-inset-bottom), so the rail needs none. */
    padding-bottom: 0 !important;
    touch-action: pan-y;
  }
  /* restore expanded contents inside the mobile drawer */
  .rail .rail-mode,
  .rail .rail-search,
  .rail .n-label,
  .rail .n-badge,
  .rail .section-label,
  .rail .rail-player-top .rp-user,
  .rail .rail-player-top .rp-tier,
  .rail .rail-player-top .rp-emblem,
  .rail .rail-player-top .bar,
  .rail .rail-player-top .xp{ display: revert; }
  .rail .rail-player-top{ padding: var(--space-6); margin: 0 4px 14px; }
  .rail .rail-player-top .rp-top{ grid-template-columns: 38px 1fr auto; justify-items: stretch; }
  .rail .nav-item{ justify-content: flex-start; padding: 10px 12px; }
  .rail{ padding: 16px 8px; }
  /* Mobile drawer: pin the player-stats dock flush to the bottom with a
     fully OPAQUE bar that matches the drawer (var(--bg-2), not the desktop
     var(--bg-1)) and bleeds to the rail's 8px mobile gutter. Without this
     the dock kept the desktop -12px margin + bg-1, so nav items (e.g. "VIP
     Club") showed through / under it while the drawer slid in. The shadow
     + safe-area padding keep it occluding cleanly above the bottom-nav. */
  .rail .rail-player-dock{
    position: sticky;
    bottom: 0;
    z-index: 30;
    margin: auto -8px 0;
    padding: 12px 8px calc(12px + env(safe-area-inset-bottom, 0px));
    background: var(--bg-2);
    border-top: 1px solid var(--line-soft);
    box-shadow: 0 -10px 22px rgba(0,0,0,.55);
  }

  .right{
    /* Start at the topbar's painted bottom edge (58px row + safe-area
       inset) — same offset as .rail above so both drawers stay flush
       with the topbar regardless of notch height. */
    position: fixed;
    inset: calc(58px + env(safe-area-inset-top, 0px)) 0 var(--bottom-nav-h) auto;
    width: 320px; max-width: 90vw;
    z-index: 50;
    transform: translateX(100%);
    transition: transform .3s cubic-bezier(.6,.1,.2,1);
    background: var(--bg-2);
    box-shadow: -20px 0 60px rgba(0,0,0,.6);
    opacity: 1 !important;
    pointer-events: auto !important;
  }
  .app:not(.right-closed) .right{ transform: translateX(0); }

  .mobile-scrim{
    position: fixed; inset: 0; z-index: 40;
    background: rgba(2,4,6,.7);
    backdrop-filter: blur(4px);
    animation: fadeIn .2s;
  }
}
@keyframes fadeIn{ from{opacity:0} to{opacity:1} }

/* ============ typography helpers ============ */
.display{
  font-family: var(--font-display);
  /* Phase 26.9 — Sora display class defaults to 800 so any element
     tagged `.display` reads with the casino-bold weight without
     having to repeat font-weight at every site. */
  font-weight: 800;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  line-height: .88;
}
.stretch-tall{
  font-family: "Archivo", sans-serif;
  font-weight: 900;
  font-stretch: 75%;
  text-transform: uppercase;
  letter-spacing: .02em;
  line-height: .88;
}
/* Phase 26.9 — JetBrains Mono defaults to 600 here so amounts/IDs
   stop reading thin. Tiny mono captions (9-11px) clamp back down to
   500 in the override block at the end of this file. */
.mono{ font-family: var(--font-display); font-weight: 600; }

/* Was the signature corner-CHAMFER clip (a diagonal cut on the
   top-left + bottom-right corners). Per design direction every bar /
   card / panel is now FULLY ROUNDED instead of corner-cut — the cut
   read as a "missing corner" left over from the old look and also
   sliced into card text. We keep the class name + the `--c` scale so
   every existing `.chamfer` / `.chamfer-sm` / `.chamfer-lg` /
   `.frame.chamfer` consumer site-wide rounds uniformly with no markup
   changes. (clip-path:none also stops the cut from clipping content.) */
.chamfer {
  --c: 14px;
  clip-path: none;
  border-radius: var(--c);
}
.chamfer-sm { --c: 8px; }
.chamfer-lg { --c: 22px; }
/* The neon hairline is an inset ::before; round it to match so the
   gold border follows the corners instead of staying a sharp box. */
.frame::before { border-radius: inherit; }

/* frame with neon hairline */
.frame{
  background: var(--panel);
  position: relative;
  isolation: isolate;
}
.frame::before{
  content:""; position:absolute; inset:0;
  padding:1px;
  background: linear-gradient(180deg, rgba(52,192,235,.35), var(--white-04) 30%, var(--white-02));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  pointer-events:none;
}

/* ============ Phase 29.3 — Canonical CARD primitive ============
   Reference: home `.game-tile` / `.slot-card.v3`.
   Use `.card` for any rectangular block that holds tile-like content
   (deposit coin row, provider tile, mode tile, VIP reward, etc.).
   Add `.card-hover` for the canonical lift-on-hover treatment.
   Add `.card-chamfer` for the signature clipped corners. */
.card{
  background: var(--bg-2, var(--panel));
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: var(--space-5);
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.card.card-hover{ cursor: pointer; }
.card.card-hover:hover{
  transform: translateY(-2px);
  box-shadow: 0 12px 30px rgba(0,0,0,.35);
  border-color: rgba(52,192,235,.25);
}
.card.card-chamfer{
  --c: 14px;
  clip-path: none;
  border-radius: var(--c);
}

/* scrollbars */
::-webkit-scrollbar{ width:10px; height:10px; }
::-webkit-scrollbar-track{ background: transparent; }
::-webkit-scrollbar-thumb{ background: var(--accent-15); border-radius: var(--radius-pill); }
::-webkit-scrollbar-thumb:hover{ background: rgba(52,192,235,.3); }

/* ============ BRAND CORNER (grid area: brand) ============ */
.brand{
  grid-area: brand;
  display: flex; align-items: center; gap: var(--space-4);
  padding: 0 14px;
  border-right: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
  background: linear-gradient(180deg, rgba(52,192,235,.04), transparent);
  position: relative;
  min-width: 0;
  /* Only clip the logo TEXT — never the rail-toggle on the right. */
}
.brand .logo{
  display:flex; align-items:center; gap:var(--space-3);
  font-family: var(--font-display);
  font-size: 18px;
  letter-spacing: .02em;
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;                /* clips only the "SPINARMORY" label text */
}
.brand .logo-mark{
  width: 30px; height: 30px;
  display:grid; place-items:center;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-display); font-weight: 800;
  font-size: 17px;
  flex-shrink: 0;
  cursor: pointer;
  --c: 7px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  box-shadow: 0 0 24px rgba(52,192,235,.42);
  transition: .15s;
}
.brand .logo-mark:hover{ transform: scale(1.04); }
.brand .logo-bracket{ color: var(--accent); }
.brand .logo > span{
  min-width: 0;
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: clip;
}

/* Collapsed rail: hide the text + the rail-toggle entirely.
   The logo-mark itself acts as the "expand" affordance (click it to open). */
.rail-collapsed .brand{ padding: 0; justify-content: center; gap: 0; }
.rail-collapsed .brand .logo{ flex: 0 0 auto; justify-content: center; }
.rail-collapsed .brand .logo > span{ display: none; }
.rail-collapsed .brand .rail-toggle{ display: none; }

.rail-toggle{
  margin-left: auto;
  width: 28px; height: 28px;
  flex-shrink: 0;
  display: grid; place-items: center;
  background: transparent; color: var(--text-dim);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: .2s;
  position: relative;
  z-index: 2;                     /* always above the logo during transition */
}
.rail-toggle:hover{ color: var(--accent); border-color: var(--accent); }

/* Narrow viewports — shrink logo-mark so the mobile topbar can breathe. */
@media (max-width: 480px){
  .mobile-brand .logo-mark{ width: 26px; height: 26px; font-size: 14px; }
}

/* mobile hamburger / brand-lite — shown inside topbar on small screens */
.mobile-brand{ display:none; }
@media (max-width: 860px){
  .mobile-brand{
    display: flex; align-items: center; gap: var(--space-4);
    font-family: var(--font-display); font-size: 18px; letter-spacing: .02em;
  }
  .mobile-brand .logo-mark{
    width: 30px; height: 30px;
    display: grid; place-items: center;
    background: var(--accent); color: var(--accent-ink);
    font-family: var(--font-display); font-size: 16px;
    --c: 7px;
    clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  }
  .mobile-brand .logo-bracket{ color: var(--accent); }
  .hamburger{
    appearance:none; background: transparent; border: 1px solid var(--line-soft);
    width: 38px; height: 38px; border-radius: var(--radius-md);
    color: var(--text-dim); cursor: pointer;
    display: grid; place-items: center;
  }
  .hamburger:hover{ color: var(--accent); }
}

/* ============ TOPBAR ============ */
.topbar{
  grid-area: topbar;
  display:flex; align-items:center;
  /* Phase 24: gap and padding clamp so the topbar doesn't crowd at
     intermediate widths.
     Task #200: respect the device safe-area on notched phones — the
     status-bar / Dynamic Island lives above safe-area-inset-top, and
     in landscape the camera cutout pushes content in from one side.
     Without this padding the logo/balance/wallet/avatar tuck under
     the system chrome on iPhone (especially in PWA / standalone mode
     where there is no browser chrome to make space). The grid row
     stays 68px / 58px (mobile) — the safe-area is added BELOW that
     so the row visually grows by exactly the inset and the main grid
     row offsets we use elsewhere (58px on mobile) remain consistent
     with the topbar's actual painted bottom edge.
     `max(...)` keeps the existing horizontal padding on phones with
     no inset (everything Android, every desktop browser) and only
     widens on devices that report a non-zero safe-area-inset-left/
     right (notched iPhones in landscape). */
  gap: clamp(6px, 1vw, 14px);
  padding:
    env(safe-area-inset-top, 0px)
    max(clamp(12px, 1.6vw, 22px), env(safe-area-inset-right, 0px))
    0
    max(clamp(12px, 1.6vw, 22px), env(safe-area-inset-left, 0px));
  border-bottom: 1px solid var(--line-soft);
  /* Operator feedback — topbar background matches the sidebar top
     (.brand) so both form one continuous header strip. The gold-
     tinted fade-to-transparent over the page bg replaces the prior
     navy gradient. */
  background: linear-gradient(180deg, rgba(52, 192, 235, .04), transparent);
  position: relative;
  /* z-index var(--z-dropdown)=1000: high enough that topbar-anchored
     dropdowns (.bal-dropdown, .notif-dropdown, .user-menu — z=200/200/
     300 inside topbar's stacking context, so externally they paint at
     topbar's z) definitively beat anything inside .main, including any
     stray descendant stacking contexts (clip-path on btn-primary etc.).
     Stays below modal-root (var(--z-modal)=5000) so dialogs still cover
     the topbar correctly.
     `isolation: isolate` is belt-and-braces — it forces a stable
     stacking context root regardless of how the parent .app grid
     evolves, so future layout changes can't accidentally break the
     dropdown layering. */
  z-index: var(--z-dropdown, 1000);
  isolation: isolate;
  /* Operator feedback — defensive `overflow: visible` so hover-grow
     children (wallet/balance pills lift translateY(-1px) on hover)
     can extend past the 68px grid row without the row boundary
     clipping them. Same for dropdowns that anchor to the topbar. */
  overflow: visible;
}
/* Make sure no descendant in the topbar accidentally clips its own
   children either — especially the user-menu / wallet / balance
   wrappers that host hovering / click-out dropdowns. */
.topbar > * { overflow: visible; }
.brand{ position: relative; z-index: 30; }
@media (max-width: 860px){
  /* Task #200: preserve env(safe-area-inset-top) on mobile so the
     iPhone notch / Dynamic Island / status bar doesn't clip the
     topbar — without this re-inclusion the later mobile rule resets
     padding back to `0 14px` and undoes the desktop safe-area fix.
     Horizontal sides also widen by inset-left/right so landscape
     notches don't tuck the brand/avatar under the camera cutout. */
  .topbar{
    padding:
      env(safe-area-inset-top, 0px)
      max(14px, env(safe-area-inset-right, 0px))
      0
      max(14px, env(safe-area-inset-left, 0px));
    gap: var(--space-3);
  }
  .topbar .pill-toggle{ display: none; }
  .topbar .search{ max-width: none; flex: 1; }
  .topbar .search input{ font-size: 13px; }
  .topbar .search .kbd{ display: none; }
  .topbar .wallet .delta{ display: none; }
  .topbar .wallet{ padding: 4px 4px 4px 10px; }
  .topbar .wallet-btn{ padding: 8px 12px; font-size: 11px; }
  .topbar .spacer{ display: none; }
  .topbar .icon-btn.hide-sm{ display: none; }
}
@media (max-width: 560px){
  .topbar .search{ display: none; }
  .topbar .wallet .bal{ font-size: 12px; }
}
.pill-toggle{
  display:flex; background: var(--panel);
  padding: var(--space-1);
  border-radius: var(--radius-pill);
  border: 1px solid var(--line-soft);
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: .08em;
}
.pill-toggle button{
  appearance:none; background:transparent; border:0; color: var(--text-dim);
  padding: 7px 16px; border-radius: var(--radius-pill); cursor: pointer;
  /* Task #18 — `font: inherit` would normally reset the weight back
     to the body's 500 baseline, so we declare `font-weight: 700`
     AFTER the shorthand to keep the pill labels casino-bold. */
  font: inherit; font-weight: 700;
  transition: .2s;
}
.pill-toggle button.on{
  background: var(--accent); color: var(--accent-ink);
  box-shadow: 0 0 24px rgba(52,192,235,.45);
}
.pill-toggle button:not(.on):hover{ color: var(--text); }

.search{
  flex: 1;
  max-width: 520px;
  display:flex; align-items:center; gap:var(--space-4);
  height: 40px; padding: 0 14px;
  background: rgba(255,255,255,.03);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-md);
  color: var(--text-dim);
}
.search input{
  flex:1; background:transparent; border:0; outline:0;
  color: var(--text); font: inherit;
}
.search .kbd{
  font-family: var(--font-display); font-size:11px;
  padding: 3px 6px; border-radius: var(--radius-xs);
  background: rgba(255,255,255,.05); color: var(--text-mute);
  border: 1px solid var(--line-soft);
}

.wallet{
  display:flex; align-items:center; gap:var(--space-4);
  padding: 6px 6px 6px 14px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  font-family: var(--font-display);
  font-weight: 500;
}
.wallet .bal{ display:flex; align-items:center; gap:var(--space-3); font-size: 14px; }
.wallet .coin{
  width:22px; height:22px; border-radius:50%;
  background: radial-gradient(circle at 30% 30%, #ffed8a, #b8860b 60%, #5a3e00);
  box-shadow: inset 0 -3px 4px rgba(0,0,0,.5);
}
.wallet .delta{ font-size:11px; color: var(--accent); }
.wallet-btn{
  appearance:none; border:0; cursor:pointer;
  background: linear-gradient(180deg, var(--accent-bright), var(--accent));
  color: var(--on-accent);
  font-family: var(--font-display); font-weight: 800; font-size: 13px; letter-spacing: .06em;
  padding: 10px 16px;
  --c: 8px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  box-shadow: 0 6px 22px var(--accent-glow);
  transition: transform .12s, box-shadow .2s, filter .15s;
}
.wallet-btn:hover{ transform: translateY(-1px); box-shadow: 0 10px 28px var(--accent-glow); filter: brightness(1.04); }
.wallet-btn:active{ transform: translateY(0); }

.topbar .spacer{ flex:1; }
.icon-btn{
  width: 40px; height:40px;
  display:grid; place-items:center;
  background: transparent;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-md); color: var(--text-dim);
  cursor: pointer; position: relative;
  transition: .2s;
}
.icon-btn:hover{ color: var(--accent); border-color: rgba(52,192,235,.3); }
.icon-btn .dot{
  position:absolute; top:8px; right:8px;
  width:8px; height:8px; background: var(--danger); border-radius: 50%;
  box-shadow: 0 0 12px var(--danger);
}
.avatar{
  width:40px; height:40px;
  border-radius:50%;
  background: linear-gradient(135deg, var(--accent), #2487AD);
  display:grid; place-items:center;
  color: var(--accent-ink);
  font-family: var(--font-display); font-size: 15px;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--accent);
  cursor: pointer;
}

/* ============ LEFT RAIL ============ */
.rail{
  grid-area: rail;
  /* Flex column so the stats dock can sit at the bottom (margin-top:auto)
     even when the nav list is short, while still scrolling normally when
     it's long. */
  display: flex;
  flex-direction: column;
  overflow-y: auto; overflow-x: hidden;
  border-right: 1px solid var(--line-soft);
  /* No bottom padding on desktop — the sticky .rail-player-dock pins flush
     to the rail's bottom edge and supplies its own bottom spacing. The
     mobile drawer re-adds padding-bottom in its own rule. */
  padding: 16px 12px 0;
  background: var(--bg-1);   /* solid dark — no gradient overlay */
}
/* Sticky stats dock — the player card lives at the BOTTOM of the rail,
   pinned so it stays visible while the nav list scrolls BEHIND it. The
   opaque bar (full rail width via negative side margins that bleed past
   the rail's 12px padding) occludes nav items as they pass under it. */
.rail-player-dock{
  position: sticky;
  bottom: 0;
  z-index: 20;
  /* margin-top:auto pushes the dock to the very bottom of the flex-column
     rail when the nav list is short; when the list is long, free space is
     0 so it sits at the natural end and `bottom:0` pins it while the nav
     scrolls behind. Top breathing room comes from this block's padding-top. */
  margin: auto -12px 0;
  padding: 14px 12px 16px;
  background: var(--bg-1);
  border-top: 1px solid var(--line-soft);
}
.rail-player-dock .rail-player-top{ margin: 0; }
.rail-collapsed .rail-player-dock{ display: none; }
.rail .section-label{
  font-family: var(--font-display); font-size: 10px;
  letter-spacing: .18em; color: var(--text-mute);
  padding: 18px 14px 8px;
}
.nav-item{
  display: flex; align-items: center; gap: var(--space-5);
  padding: 10px 14px;
  color: var(--text-dim);
  cursor: pointer;
  border-radius: var(--radius-md);
  position: relative;
  font-weight: 500;
  transition: .15s;
  white-space: nowrap;
}
.nav-item:hover{ color: var(--text); background: rgba(255,255,255,.03); }
.nav-item.active{
  /* Spec-exact: active item = warm stripe gradient (accent alpha 0.18 → 0.02)
     + warm --line-3 left border + indicator bar with accent glow. */
  color: var(--text);
  background: linear-gradient(90deg, rgba(52,192,235,0.18), rgba(52,192,235,0.02));
  border-left: 1px solid var(--line-3);
}
.nav-item.active::before{
  content:""; position:absolute; left:0; top:8px; bottom:8px; width:3px;
  background: var(--accent); border-radius: 0 4px 4px 0;
  box-shadow: 0 0 22px var(--accent-glow);
}
.nav-item.active .n-icon{ color: var(--accent); }
.nav-item .n-icon{ width:20px; display:grid; place-items:center; color: currentColor; }
.nav-item .n-label{ flex:1; }
.nav-item .n-badge{
  font-family: var(--font-display); font-size: 10px;
  padding: 2px 7px; border-radius: var(--radius-pill);
  background: var(--accent-15); color: var(--accent);
}
.nav-item .n-badge.hot{ background: var(--accent); color: var(--accent-ink); }

.rail-collapsed .rail .n-label,
.rail-collapsed .rail .n-badge,
.rail-collapsed .rail .section-label{ display: none; }
.rail-collapsed .rail .nav-item{ justify-content: center; padding: 10px 8px; }

/* player card on rail bottom */
.rail-player{
  margin: 16px 4px 0;
  padding: var(--space-6);
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  position: relative;
  overflow: hidden;
}
.rail-collapsed .rail-player{ display: none; }
.rail-player::before{
  content:""; position:absolute; inset:-30% -10% auto auto; width: 140px; height:140px;
  background: radial-gradient(circle, var(--accent-25), transparent 60%);
  pointer-events: none;
}
.rail-player .label{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .15em;
  color: var(--text-mute);
}
.rail-player .rank{
  font-family: var(--font-display); font-size: 20px; margin-top: 4px;
}
.rail-player .rank em{ color: var(--accent); font-style: normal; }
.rail-player .bar{
  height: 6px; background: var(--white-06); border-radius: var(--radius-pill);
  margin-top: 12px; overflow: hidden;
  position: relative;
}
.rail-player .bar > span{
  display:block; height:100%;
  background: linear-gradient(90deg, var(--accent-2), var(--accent));
  box-shadow: 0 0 12px var(--accent);
  width: 62%;
}
.rail-player .xp{ display:flex; justify-content: space-between; font-family: var(--font-display); font-size: 11px; color: var(--text-dim); margin-top:6px; }

/* ============ MAIN ============
   Content is constrained to --content-max with generous horizontal
   gutters. Every page (home, games, profile, legal, admin, lobby)
   inherits this spacing through .main's padding, guaranteeing a
   consistent "breathing" column width across the site.
*/
.main{
  grid-area: main;
  overflow-y: auto;
  overflow-x: hidden;
  min-width: 0;
  /* padding-inline uses max() so the gutter grows past a threshold,
     keeping content aligned to --content-max on wide screens while
     never falling below the base 26px on narrow ones.
     Phase 24: floor switched from `26px` to `var(--main-pad)` (clamps
     10–26px) so on narrow main-widths the gutter shrinks before the
     content does. */
  padding: 22px max(var(--main-pad), calc((100% - var(--content-max)) / 2 + var(--main-pad))) 60px;
  position: relative;
  /* Phase 24: register as a query container so descendants (.activity-wrap,
     .gp-shell, .slots-shell, …) can container-query the actual content
     width. This is the right level — children are descendants. */
  container-type: inline-size;
  container-name: main;
}

/* section heading */
.sec-head{
  display:flex; align-items:end; justify-content:space-between;
  gap: var(--space-8); margin: 30px 0 16px;
}
.sec-head h2{
  font-family: var(--font-display);
  /* Phase 26 follow-up — bumped weight + tracking so headings read
     as actual headings (was 28 px, weight inherited / loose). */
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.015em;
  margin: 0;
  text-transform: uppercase;
}
.sec-head h2 em{ color: var(--accent); font-style: normal; font-weight: 800; }
.sec-head .kicker{
  display: block;
  /* Phase 26 follow-up — kicker is now more visible (was --text-xs
     ~10 px in --text-mute). Bump to 11.5 px + brighter dim color
     + heavier weight so labels like "ORIGINALS" / "FRESH DROPS" /
     "POPULAR PICKS" actually pop. */
  font-family: var(--font-display);
  font-size: 11.5px;
  font-weight: 700;
  color: var(--text-dim);
  letter-spacing: .22em;
  margin-bottom: 6px;
}
/* Compact variant for in-card section headers (Profile / Affiliate /
   Settings panels) — drops the 30px top margin and the 28px h2 size
   so the heading nests properly inside a .card / .panel container. */
.sec-head.sec-head-compact{
  margin: 0 0 var(--space-6);
  align-items: center;
}
.sec-head.sec-head-compact h2{
  font-size: var(--text-xl);
  letter-spacing: 0;
}
.sec-tools{ display:flex; align-items:center; gap: var(--space-3); }
.chip{
  appearance:none; border:0; cursor:pointer;
  padding: 7px 12px;
  font-family: var(--font-ui); font-size: 12px; font-weight: 600;
  color: var(--text-dim);
  background: rgba(255,255,255,.03);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  transition: .15s;
  letter-spacing: .02em;
}
.chip:hover{ color: var(--text); border-color: rgba(255,255,255,.15); }
.chip.on{
  color: var(--accent-ink); background: var(--accent); border-color: var(--accent);
  box-shadow: 0 0 18px rgba(52,192,235,.35);
}
.chip.ghost{ background: transparent; }

/* Phase 29.6 — chip tone modifiers. Use these everywhere a same-purpose
   pill/badge needs a colour tint (status, win/lose tag, tier hint).
   Migrate `.tag`, `.slot-badge`, `.bj-tag`, `.vrw-status`,
   `.pm-shuffle-tier`, `.rl-stats-pill` to `.chip` + a tone modifier. */
.chip.chip-tone-accent  { color: var(--accent); border-color: rgba(52,192,235,.30); background: rgba(52,192,235,.08); }
.chip.chip-tone-success { color: #22c55e;       border-color: rgba(34,197,94,.30);  background: rgba(34,197,94,.08); }
.chip.chip-tone-danger  { color: #ef4444;       border-color: rgba(239,68,68,.30);  background: rgba(239,68,68,.08); }
.chip.chip-tone-purple  { color: var(--purple); border-color: rgba(139,92,246,.30); background: rgba(139,92,246,.08); }
.chip.chip-mono { font-family: var(--font-display); letter-spacing: .06em; text-transform: uppercase; font-size: 11px; }

.arrow-btn{
  appearance:none; border:0; cursor:pointer; font-weight: 700;
  width: 34px; height: 34px;
  display:grid; place-items:center;
  background: rgba(255,255,255,.03);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  transition: .15s;
}
.arrow-btn:hover{ color: var(--accent); border-color: var(--accent-40); }

/* ============ HERO ============ */
.hero{
  display: grid;
  /* Phase 24: hero side card needs a min of ~220px to be readable. When
     main is below ~720px (chat open at 1280, etc) the existing @media
     1060 collapses to 1fr already. The minmax floor handles the
     in-between zone where 1.55fr/1fr tries to render the side card too
     narrow. */
  grid-template-columns: minmax(0, 1.55fr) minmax(220px, 1fr);
  gap: clamp(10px, 1.4vw, 16px);
  margin-top: 4px;
}
@container main (max-width: 720px){
  .hero{ grid-template-columns: 1fr; }
}
.hero-main{
  position: relative;
  min-width: 0;
  min-height: 280px;           /* was 420 — matches shuffle proportions */
  padding: 22px 26px;
  /* Customer-supplied hero artwork (racks/img/main header bg.png).
     Layered: solid bg color floor (so any transparent edges of the PNG
     read clean), then the image cover-fitted on top. */
  background:
    url('/img/main%20header%20bg.webp') center/cover no-repeat,
    var(--bg-2);
  overflow: hidden;
  --c: 22px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
}
/* Sports-mode hero swap — same overlay treatment, different artwork. The
   .mode-sports class lives on the .app root and is set by the Casino/Sports
   pill toggle in the brand corner (src/app.jsx). */
.app.mode-sports .hero-main{
  background:
    url('/img/sport-banner.webp') center/cover no-repeat,
    var(--bg-2);
}
/* Navy-blue gradient overlay (customer ask): knocks the hero image down
   to ≤15% effective opacity at every pixel. Two stacked layers:
     1. Diagonal navy gradient — 92% → 85% alpha, top-left to bottom-right.
        Ensures the image never reads above ~15% opacity even at the
        lowest-alpha point (1 - 0.85 = 0.15).
     2. A subtle right-side accent darken so the title + CTAs on the LEFT
        sit on the most-tinted area while the right side keeps a tiny bit
        of artwork visibility. Uses tiny alphas so it doesn't compound
        past the 15% budget.
   Pure rgba (no mix-blend-mode) so behavior is identical across browsers
   and the underlying PNG TINTS rather than lights up. */
.hero-main::after{
  content:""; position: absolute; inset: 0;
  background:
    linear-gradient(135deg, rgba(7,12,40,.92) 0%, rgba(10,16,52,.88) 50%, rgba(14,22,68,.85) 100%);
  pointer-events: none;
}
.hero-eyebrow{
  display: inline-flex; align-items:center; gap:var(--space-3);
  font-family: var(--font-display); font-size: 11px; letter-spacing: .2em;
  color: var(--accent);
  padding: 6px 10px;
  border: 1px solid rgba(52,192,235,.35);
  border-radius: var(--radius-xs);
  background: var(--accent-05);
}
.hero-eyebrow .pulse{
  width:6px; height:6px; border-radius:50%; background: var(--accent);
  box-shadow: 0 0 0 0 var(--accent-60); animation: pulse 1.6s infinite;
}
@keyframes pulse{
  0%{ box-shadow: 0 0 0 0 var(--accent-60); }
  70%{ box-shadow: 0 0 0 10px rgba(52,192,235,0); }
  100%{ box-shadow: 0 0 0 0 rgba(52,192,235,0); }
}
.hero-title{
  font-family: var(--font-display);
  font-size: clamp(24px, 3.2vw, 40px);         /* sized so UNREASON·ABLY fits on one line */
  line-height: .9;
  letter-spacing: -.02em;
  text-transform: uppercase;
  margin: 10px 0 4px;
  /* No word-break: keeps "UNREASON·ABLY" from splitting across lines. */
}
.hero-title > div{ white-space: nowrap; }
.hero-title .glow{
  color: var(--accent);
  text-shadow: 0 0 22px rgba(52,192,235,.55);
}
.hero-title .outline{
  -webkit-text-stroke: 1.5px var(--accent);
  color: transparent;
}
.hero-sub{
  max-width: 420px;
  color: var(--text-dim);
  font-size: 13px;                              /* was 15 */
  line-height: 1.45;
  margin-bottom: 14px;                          /* was 26 */
}
.hero-stats{
  display: grid; grid-template-columns: repeat(3, auto);
  gap: 22px;                                     /* was 32 */
  margin: 10px 0 14px;                           /* was 20/28 */
}
.hero-stat .k{ font-family: var(--font-display); font-size:10px; letter-spacing:.15em; color: var(--text-mute); text-transform: uppercase; }
.hero-stat .v{ font-family: var(--font-display); font-size: 20px; margin-top:2px; }  /* was 26 */
.hero-stat .v em{ color: var(--accent); font-style: normal; }

.hero-ctas{ display:flex; gap: var(--space-5); align-items:center; flex-wrap: wrap; }
.btn-primary{
  appearance:none; border:0; cursor: pointer;
  font-family: var(--font-display); font-weight: 800; font-size: 13px; letter-spacing: .06em;
  background: linear-gradient(180deg, var(--accent-bright), var(--accent));
  color: var(--on-accent);
  padding: 11px 18px;
  display:inline-flex; align-items:center; gap: var(--space-3);
  --c: 9px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  box-shadow: 0 8px 22px var(--accent-glow);
  transition: transform .12s, box-shadow .2s, filter .15s;
}
.btn-primary:hover{ transform: translateY(-1px); box-shadow: 0 12px 30px var(--accent-glow); filter: brightness(1.04); }
.btn-ghost{
  appearance:none; cursor:pointer;
  font-family: var(--font-display); font-weight: 700; font-size: 13px; letter-spacing: .06em;
  background: transparent; color: var(--text);
  padding: 11px 16px;                                /* was 14/20 */
  border: 1px solid var(--line-soft);
  --c: 9px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  transition: .2s;
}
.btn-ghost:hover{ color: var(--accent); border-color: rgba(52,192,235,.5); }

/* ============ Phase 29.5 — Canonical INPUT primitive ============
   Use `.input` for any text/number/select control across the site.
   Modifiers:
     `.input-mono`  — JetBrains Mono (numeric / amount / hash inputs)
     `.input-block` — full-width
     `.input-search`— extra left padding for the inline magnifier icon
   Existing bespoke classes (.dw-input, .gp-input, .a-input, .rl-bet-input,
   .pm-shuffle-tip-row input) inherit nearly identical declarations
   already; the new .input is the canonical reach-for from now on. */
.input{
  appearance: none;
  background: rgba(255,255,255,.03);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  color: var(--text);
  font: 14px var(--font-ui);
  transition: border-color .15s ease, background .15s ease;
  outline: none;
}
/* Use background-color longhand, NOT the `background` shorthand. The
   shorthand resets every background longhand (image, size, repeat,
   position, …) to defaults, which on `select.input` blew away the
   chevron-SVG sizing/no-repeat that the base rule sets — the 12×8
   chevron URL would then tile across the whole select on focus and
   render as a gold zigzag pattern. Keeping this as the longhand
   preserves the chevron icon untouched. */
.input:focus{ border-color: var(--accent); background-color: rgba(255,255,255,.045); }
.input::placeholder{ color: var(--text-mute); }
/* STAYS mono — opt-in modifier admins use for code-style inputs
   (regex builders, rule IDs, slug fields). Explicit escape hatch
   exempt from the May 2026 font purge. */
.input.input-mono{ font-family: var(--font-mono); }
.input.input-block{ width: 100%; }
.input.input-search{ padding-left: 36px; }
.input:disabled{ opacity: .55; cursor: not-allowed; }

/* Task #199 — `select.input` polish.
   The default OS select chrome (white list, system font, native chevron)
   clashes with the dark theme. We strip the native arrow, paint our own
   inline-SVG chevron on the right, dim the closed control to match the
   text-input variant, and force `color-scheme: dark` so the OS-rendered
   dropdown list, highlight color, and option text all render in dark
   palette in Chrome / Edge / Firefox. Per-<option> color/bg is the only
   way to dark-skin the open list in most browsers, so we set them
   explicitly. */
select.input{
  /* Mimic the existing text-input height while leaving room for the
     custom chevron on the right. The 36px right padding ≥ chevron-edge
     gap so values never crash into the arrow. */
  padding-right: 36px;
  cursor: pointer;
  /* Tell the OS to render its native widgets (option list, focus ring,
     scrollbars inside the popup) using the dark scheme. */
  color-scheme: dark;
  /* Inline-SVG chevron — uses currentColor via a soft text tint so the
     arrow matches the dim-label palette. Encoded as data URI so we
     don't ship another asset. */
  background-image:
    linear-gradient(rgba(255,255,255,0), rgba(255,255,255,0)),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.5l5 5 5-5' fill='none' stroke='%239aa0a6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center, 0 0;
  background-size: 12px 8px, auto;
}
select.input:hover{ border-color: var(--accent-40, var(--line)); }
select.input:focus{
  /* Brighter chevron on focus mirrors the border-color change so the
     control feels alive. */
  background-image:
    linear-gradient(rgba(255,255,255,0), rgba(255,255,255,0)),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.5l5 5 5-5' fill='none' stroke='%23e5b840' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}
select.input::-ms-expand{ display: none; }   /* IE/Edge legacy chevron */
/* Dark-skin the open option list. Chrome + Firefox both honor option
   bg/color when set; Safari falls back to color-scheme above. The
   :checked / :hover overrides keep the active row highlighted in
   accent. */
select.input option,
select.input optgroup{
  background: var(--bg-2, #14141a);
  color: var(--text);
}
select.input option:checked{
  background: linear-gradient(0deg, rgba(52,192,235,.25), rgba(52,192,235,.25));
  color: var(--text);
}
select.input option:disabled{
  color: var(--text-mute);
}

/* .btn-danger — destructive primary action (delete, disable, confirm-
   irreversible). Same chamfer + lift as .btn-primary but in the
   semantic-red palette so the user reads it as "destructive" at a
   glance. Used by toast confirm/prompt dialogs and any settings page
   that needs a destructive CTA. */
.btn-danger{
  appearance:none; border: 0; cursor:pointer;
  font-family: var(--font-display); font-weight: 800; font-size: 13px; letter-spacing: .06em;
  background: linear-gradient(180deg, #ff6577, var(--red, #F24D5C));
  color: #1a0508;
  padding: 11px 22px;
  --c: 9px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  box-shadow: 0 8px 24px rgba(242,77,92,.35);
  transition: transform .15s, box-shadow .2s, filter .15s;
}
.btn-danger:hover{ transform: translateY(-1px); filter: brightness(1.06); box-shadow: 0 10px 28px rgba(242,77,92,.45); }
.btn-danger:disabled{ filter: grayscale(.4) brightness(.7); cursor: not-allowed; transform: none; }

/* hero visual column — the legacy SVG art is now hidden; the customer's
   /img/main header bg.png carries the hero visual via .hero-main's
   background. Leaving .hero-art in the JSX (so we don't have to touch
   the component) but display:none here. */
.hero-art{
  display: none;
}

/* hero side stack */
.hero-side{ display:flex; flex-direction:column; gap: var(--space-5); min-width: 0; }
.hero-card{
  flex: 1;
  padding: 14px 16px;                          /* was 18 */
  position: relative;
  overflow: hidden;
  min-height: 0;
  --c: 16px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
}
.hero-card .tag{
  font-family: var(--font-display); font-size: 9px; letter-spacing: .18em;
  color: var(--accent-ink);
  background: var(--accent);
  padding: 3px 7px; border-radius: var(--radius-2xs);
  display:inline-block;
}
.hero-card h4{
  font-family: var(--font-display); font-size: 18px;      /* was 22 */
  margin: 8px 0 4px; text-transform: uppercase; letter-spacing: -.01em;
  line-height: 1;
}
.hero-card p{ font-size: 12px; color: var(--text-dim); margin: 0 0 10px; line-height: 1.4; }
.hero-card .h-link{
  font-family: var(--font-display); font-size: 11px; letter-spacing: .08em;
  color: var(--accent);
  text-transform: uppercase;
  display: inline-flex; align-items: center; gap: var(--space-2);
}

/* ============ MODES ============ */
.modes{
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  grid-template-rows: 128px 128px;             /* was 160 */
  gap: var(--space-5);
  margin-top: 6px;
}
.mode{
  position: relative;
  overflow: hidden;
  padding: 16px 18px;                          /* was 20 */
  cursor: pointer;
  transition: transform .2s;
  --c: 16px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
}
.mode:hover{ transform: translateY(-2px); }
.mode .m-title{
  font-family: var(--font-display); font-size: 28px; letter-spacing: -.01em;
  text-transform: uppercase; line-height: .9;
}
.mode .m-sub{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .14em;
  color: var(--text-dim); text-transform: uppercase; margin-top: 3px;
}
.mode .m-count{
  position: absolute; top: 20px; right: 20px;
  font-family: var(--font-display); font-size: 11px;
  padding: 4px 9px; border-radius: var(--radius-pill);
  background: rgba(0,0,0,.5);
  backdrop-filter: blur(4px);
  color: var(--text-dim);
  border: 1px solid var(--line-soft);
}
.mode .m-body{ position: absolute; left: 18px; bottom: 14px; z-index: 2; }
.mode .m-art{ position: absolute; inset: 0; z-index: 1; pointer-events: none; }

/* Slots tile uses the customer-supplied slot-banner.png art under the
   site's navy gradient overlay (same ≤15%-image-opacity treatment as the
   home hero). The legacy SVG <ModeArt id="slots"> is hidden via the
   .mode.slots .m-art rule below so the new artwork carries the visual. */
.mode.slots{
  grid-row: span 2;
  background:
    linear-gradient(135deg, rgba(7,12,40,.92) 0%, rgba(10,16,52,.86) 50%, rgba(14,22,68,.82) 100%),
    url('/img/slot-banner.webp') center/cover no-repeat,
    linear-gradient(135deg, var(--panel-2), var(--bg-2));
}
.mode.slots .m-title{ font-size: 54px; }                /* was 72 */
.mode.slots .m-art{ display: none; }                    /* hide legacy SVG — image carries the visual */
.mode.live{
  background: linear-gradient(135deg, var(--panel-2), var(--bg-2));
}
/* Sports tile uses the customer-supplied sport-banner.png art under the
   same navy overlay. SVG ModeArt hidden so the artwork shows clean. */
.mode.sports{
  background:
    linear-gradient(135deg, rgba(7,12,40,.92) 0%, rgba(10,16,52,.86) 50%, rgba(14,22,68,.82) 100%),
    url('/img/sport-banner.webp') center/cover no-repeat,
    linear-gradient(135deg, var(--panel-2), var(--bg-2));
}
.mode.sports .m-art{ display: none; }
/* Originals tile uses the BLUE radial accent (was amber rgba(52,192,235,.18)
   — replaced with the site blue rgba(61, 126, 255,.22) so the Originals
   tile matches the new blue brand color throughout the site. */
.mode.originals{
  background:
    radial-gradient(80% 80% at 0% 100%, rgba(61, 126, 255,.22), transparent 60%),
    linear-gradient(135deg, var(--panel-2), var(--bg-2));
}
.mode.shows{
  background: linear-gradient(135deg, var(--panel-2), var(--bg-2));
}

/* Mode-text accents collapse to the yellow + blue spectrum only. */
.mode-accent-slots{ color: var(--accent); }
.mode-accent-live{ color: var(--blue-3); }
.mode-accent-sports{ color: var(--blue-3); }
/* Originals title now uses the site blue (was --accent amber) so the home
   "Pick your poison" tile matches the blue used everywhere else for the
   Originals brand (corner tab on slot cards, originals stripe pill, etc). */
.mode-accent-originals{ color: var(--blue); }
.mode-accent-shows{ color: var(--accent-2); }

/* ============ CONTINUE / CAROUSELS ============ */
.scroller{
  display: grid;
  grid-auto-flow: column;
  /* Phase 24: tile width clamps 132–180px so home rails shrink fluidly
     when the chat panel is open and main is narrow. cqw is relative to
     the .main container, so closing the chat re-expands the tiles. */
  grid-auto-columns: clamp(132px, 14cqw, 180px);
  gap: clamp(8px, 1cqw, 14px);
  overflow-x: auto;
  /* Phase 27 hover-clip fix — top padding bumped from 4px → 12px so the
     `.game-tile:hover` translateY(-3px) lift is no longer clipped at
     the row's top edge. `overflow-x: auto` implicitly forces overflow-y
     to clip (CSS spec), so the only safe way to give the hover lift
     breathing room without breaking the horizontal scroll behaviour is
     additional vertical padding inside the scroll viewport. */
  padding: 12px 2px 14px;
  scroll-snap-type: x mandatory;
}
.scroller.tall{ grid-auto-columns: clamp(150px, 16cqw, 200px); }
.scroller > *{ scroll-snap-align: start; }

.game-tile{
  position: relative;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  cursor: pointer;
  --c: 14px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  transition: transform .2s;
}
.game-tile:hover{ transform: translateY(-3px); }
.game-tile .t-art{ position: absolute; inset: 0; }
.game-tile .t-overlay{
  position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 50%, rgba(0,0,0,.75));
  opacity: 0; transition: opacity .2s;
  display:flex; align-items:flex-end; padding: var(--space-6);
}
.game-tile:hover .t-overlay{ opacity: 1; }
.game-tile .t-footer{
  position: absolute; left: 12px; right: 12px; bottom: 10px;
  display:flex; justify-content: space-between; align-items: flex-end;
  z-index: 2;
  pointer-events: none;
}
.game-tile .t-name{
  font-family: var(--font-display); font-size: 12px;
  letter-spacing: .05em;
  color: #fff;
  text-shadow: 0 2px 6px rgba(0,0,0,.6);
}
.game-tile .t-provider{
  font-family: var(--font-display); font-size: 9px;
  color: rgba(255,255,255,.6); letter-spacing: .12em;
}
.game-tile .t-badge{
  position:absolute; top: 10px; left: 10px;
  font-family: var(--font-display); font-size: 9px; letter-spacing: .1em;
  padding: 3px 7px; background: rgba(0,0,0,.6);
  border: 1px solid rgba(255,255,255,.15);
  border-radius: var(--radius-xs);
  z-index: 2;
}
.game-tile .t-badge.hot{ background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.game-tile .t-players{
  position: absolute; top: 10px; right: 10px;
  font-family: var(--font-display); font-size: 10px;
  color: var(--accent);
  background: rgba(0,0,0,.6);
  padding: 3px 7px; border-radius: var(--radius-xs);
  border: 1px solid rgba(52,192,235,.3);
  display:flex; align-items:center; gap:var(--space-1);
  z-index: 2;
}
.game-tile .t-players::before{
  content:""; width:6px; height:6px; background: var(--accent); border-radius: 50%;
  box-shadow: 0 0 8px var(--accent); animation: pulse 1.8s infinite;
}
.game-tile .t-play{
  position: absolute; inset: 0;
  display: grid; place-items: center;
  opacity: 0; transition: opacity .2s;
}
.game-tile:hover .t-play{ opacity: 1; }
.game-tile .t-play span{
  width: 56px; height: 56px; display:grid; place-items:center;
  background: var(--accent); color: var(--accent-ink);
  border-radius: 50%;
  box-shadow: 0 0 30px rgba(52,192,235,.55);
}

/* ============ ACTIVITY SECTION ============ */
/* Phase 24: minmax floors so the side-stack can't squeeze below 220px,
   AND the columns flip to 1fr when the parent .main is narrow. We
   register .main as the container (already laid out as the central
   column) so .activity-wrap (a descendant) can container-query against
   it.  Viewport-keyed @media also still fires at 1060px as a safety
   floor for any weird main-w values. */
/* Operator rebuild (PDF page 3): the wager-race side panel was
   removed from the homepage activity strip, so Latest Hits now
   spans the full row width — single column instead of 1.5fr / 1fr. */
.activity-section{
  overflow: hidden;
}
.activity-wrap{
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
  margin-top: 6px;
}
@container main (max-width: 720px){
  .activity-wrap{ grid-template-columns: 1fr; }
}
.activity{
  padding: var(--space-8);
  background: var(--panel);
  border: 1px solid var(--line-soft);
  --c: 16px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  position: relative;
  overflow: hidden;
  /* Phase 23 follow-up: isolate the activity card from sibling layout.
     `contain: layout style` tells the browser nothing inside this element
     can change the layout of anything outside it, so prepended rows never
     trigger a reflow on the page below the feed. `content-visibility: auto`
     also lets the browser skip rendering when off-screen. */
  contain: layout style;
}
.activity-head{
  display:flex; align-items:center; justify-content: space-between;
  margin-bottom: 14px;
}
.activity-head h3{
  /* Operator rebuild — Latest Hits is now the primary block on the
     homepage activity strip; bumped from 18 → 22 for proper hierarchy. */
  font-family: var(--font-display); font-size: 22px; margin:0; text-transform: uppercase;
}
.activity-tabs{
  display:flex; gap: var(--space-1);
  background: rgba(0,0,0,.3);
  padding: 3px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-soft);
}
.activity-tabs button{
  appearance:none; border:0; cursor:pointer;
  background: transparent; color: var(--text-dim);
  font-family: var(--font-ui); font-weight: 700; font-size: 11px;
  padding: 6px 10px; border-radius: var(--radius-xs);
  letter-spacing: .04em;
  transition: .15s;
}
.activity-tabs button.on{ background: var(--accent-15); color: var(--accent); }

/* Fixed-height table so the live feed never shifts content underneath
   when a new row is prepended.
   Layout math: header (44px) + 10 rows × 44px = 484px steady height.
   We pin BOTH min and max so the table can't grow — even briefly — while
   a new row animates in or while React commits between insert + slice.
   Phase 24: container-type makes the table its own query context so
   columns drop based on TABLE width, not viewport. With chat open at
   1280px, the .activity column is ~430px — viewport-keyed @media doesn't
   fire because viewport says "desktop", but the table's own width tells
   the truth. */
.activity-table{
  width: 100%;
  /* Operator rebuild — bigger rows (50px x 10 = 500) + 50px header = 550. */
  min-height: 550px;
  max-height: 550px;
  height: 550px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  container-type: inline-size;
  container-name: activitytable;
  /* Operator fix — a new live-feed entry must NOT shift the page when
     the user is scrolled below it. The table is already height-clamped
     + clipped, but the browser's scroll-anchoring could still pick a
     feed row as the document anchor; the `newrow` entrance
     (transform: translateY(100%)) then makes that anchor "jump" and
     the document scroll compensates → the whole page lurches. Guards:
       1. `overflow-anchor: none` excludes the feed subtree from
          scroll-anchor selection, so the browser anchors to stable
          content elsewhere and feed churn never moves the viewport.
       2. `contain: layout paint` hard-isolates the feed's box so row
          insertions/removals + the entrance animation can never affect
          ancestor/sibling layout or the document height. */
  overflow-anchor: none;
  contain: layout paint;
  -webkit-mask-image: linear-gradient(to bottom, black calc(100% - 50px), transparent 100%);
  mask-image: linear-gradient(to bottom, black calc(100% - 50px), transparent 100%);
}
/* Belt-and-suspenders — opt individual rows (esp. the animating
   `.new` row) out of scroll-anchor candidacy too. */
.activity-row{ overflow-anchor: none; }
/* Phase 24: minmax floors keep each column readable. Multiplier column
   uses --activity-multiplier-col which clamps 56–80px so it shrinks
   first when space is tight. */
/* P3-3: Latest Hits now ships a BET column (operator feedback page 10).
   Layout: 6 columns — icon · Game · Player · Bet · Multiplier · Payout.
   Bet drops out at the same breakpoint that historically dropped
   multiplier so very narrow screens stay readable. */
.activity-row{
  display: grid;
  grid-template-columns:
    32px
    minmax(80px, 1.4fr)
    minmax(72px, 1fr)
    minmax(60px, .8fr)
    minmax(48px, var(--activity-multiplier-col))
    minmax(76px, 1fr);
  gap: var(--space-6);
  align-items: center;
  /* Operator rebuild — bigger row + base font so the table reads at
     full-row width (was 13/44 → 14/50). Table height auto-grows
     because it's height = 10 rows + header in the parent. */
  height: 50px;
  padding: 0;
  border-bottom: 1px dashed rgba(255,255,255,.05);
  font-size: 14px;
  flex-shrink: 0;
  /* Contents can't overflow the fixed row height */
  overflow: hidden;
  min-width: 0;
}
/* Phase 24: container-query column drops. These mirror the older
   viewport @media drops at lines ~1487/1501 but trigger on the TABLE'S
   own width, so closing the chat panel re-adds the multiplier column
   immediately — no viewport-resize required. */
/* Operator: on phones the feed showed "only game names". Re-prioritised so
   the MULTIPLIER + PAYOUT (the win info players actually want) survive down
   to the smallest screens; only Bet then Player are dropped, and the font /
   gap shrink so the remaining columns stay readable rather than clipped. */
@container activitytable (max-width: 540px){
  .activity-row{ grid-template-columns: 24px minmax(72px, 1.4fr) minmax(64px, 1fr) minmax(48px, var(--activity-multiplier-col)) minmax(68px, 1fr); gap: var(--space-3); font-size: 12px; }
  .activity-row > *:nth-child(4){ display: none !important; }   /* drop bet */
  .activity-row.head > *:nth-child(4){ display: none !important; }
}
@container activitytable (max-width: 420px){
  .activity-row{ grid-template-columns: 22px minmax(60px, 1.5fr) minmax(44px, var(--activity-multiplier-col)) minmax(62px, 1fr); gap: 6px; font-size: 11.5px; }
  .activity-row > *:nth-child(3){ display: none !important; }   /* also drop player; keep game · multiplier · payout */
  .activity-row.head > *:nth-child(3){ display: none !important; }
}
.activity-row > *{ min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.activity-row:last-child{ border-bottom: 0; }
.activity-row.head{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .15em;
  color: var(--text-mute); text-transform: uppercase;
  border-bottom: 1px solid var(--line-soft);
}
.activity-row .g-chip{
  width: 28px; height:28px; border-radius: var(--radius-xs);
  background: var(--panel-2);
  display:grid; place-items:center;
  font-family: var(--font-display); font-size: 10px;
}
.activity-row .user{ display:flex; align-items:center; gap: var(--space-3); font-weight: 600; }
.activity-row .user .av{
  width: 22px; height:22px; border-radius:50%;
  background: linear-gradient(135deg, var(--accent), #2487AD);
  display:grid; place-items:center; color: var(--accent-ink);
  font-size: 10px; font-weight: 700;
}
.activity-row .mono{ font-family: var(--font-display); font-size: 12px; font-variant-numeric: tabular-nums; }
.activity-row .win{
  font-family: var(--font-display); font-weight: 700;
  color: var(--green);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
}
.activity-row .multiplier{
  font-family: var(--font-display); font-size: 12px;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
}
.activity-row.new{
  animation: newrow .6s ease;
}
@keyframes newrow{
  from { background: var(--accent-10); transform: translateY(100%); opacity: 0; }
  60%  { transform: translateY(0); opacity: 1; }
  to   { background: transparent; transform: translateY(0); opacity: 1; }
}

/* jackpot / leaderboard side */
.side-stack{ display:flex; flex-direction: column; gap: var(--space-6); }
.jackpot{
  position:relative;
  padding: 22px 20px;
  background: var(--bg-2);   /* flat dark — was amber radial glow + gradient */
  border: 1px solid var(--line);
  --c: 16px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  overflow: hidden;
  /* Layout-isolate the jackpot card so the per-second amount tick can't
     reflow siblings. */
  contain: layout style;
}
.jackpot .j-label{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .2em;
  color: var(--accent); text-transform: uppercase;
}
.jackpot .j-amount{
  font-family: var(--font-display);
  /* Phase 24: clamp so the amount never overflows the (now possibly
     narrow) side-stack column. 28px is the floor — still legible at the
     narrowest sub-300px stack — and 44px is the prior fixed value at
     full desktop. */
  font-size: clamp(28px, 4cqw, 44px);
  line-height: 1;
  margin: 10px 0;
  color: var(--accent);
  text-shadow: 0 0 20px var(--accent-40);
  letter-spacing: -.02em;
  /* Tabular numerals so digit width is identical across ticks — keeps the
     jackpot amount from jiggling left/right as the value updates. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
  /* Allow line break if needed — better than overflowing */
  word-break: break-word;
}
.jackpot .j-amount .cents{ font-size: 24px; opacity: .6; }
.jackpot .j-meta{
  display:flex; justify-content: space-between;
  font-family: var(--font-display); font-size: var(--text-xs); color: var(--text-dim);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line-soft);
  margin-top: var(--space-2);
}

/* ============ WAGER-RACE BANNER (replaces .jackpot in side-stack) ============
   Same chamfered .frame shape as the legacy jackpot card so the home page
   layout / surrounding frame is undisturbed. The block now shows:
     • kicker  — period (WEEKLY / MONTHLY / CUSTOM) + LIVE pulse dot
     • status  — LIVE / SOON / ENDED  pill
     • name    — admin-defined race name (e.g. "Friday Splash")
     • pool    — large amber USD amount (was the jackpot $$$)
     • meta    — "X winners" + "PRIZE POOL" label
     • countdown — DD HH MM SS until ends_at, ticking every second
*/
.wager-race-banner{
  position: relative;
  padding: 22px 20px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  --c: 16px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)),
                     calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  overflow: hidden;
  contain: layout style;
  display: flex; flex-direction: column; gap: var(--space-2);
}
.wrb-head{
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--space-3);
}
.wrb-kicker{
  font-family: var(--font-display); font-size: var(--text-2xs);
  letter-spacing: .2em;
  color: var(--accent); text-transform: uppercase;
  display: inline-flex; align-items: center; gap: var(--space-2);
}
.wrb-pulse{
  width: 6px; height: 6px; border-radius: var(--radius-pill);
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent);
  animation: wrb-pulse 1.6s ease-in-out infinite;
}
@keyframes wrb-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .55; transform: scale(.85); }
}
.wrb-status{
  font-family: var(--font-display); font-size: var(--text-2xs); font-weight: 700;
  letter-spacing: .14em;
  padding: 3px var(--space-3);
  border-radius: var(--radius-xs);
  background: var(--accent-15); color: var(--accent);
}
.wrb-name{
  font-family: var(--font-display); font-size: var(--text-md);
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wrb-pool{
  font-family: var(--font-display);
  font-size: clamp(28px, 4cqw, 44px);
  line-height: 1;
  margin: 4px 0 6px;
  color: var(--accent);
  text-shadow: 0 0 20px var(--accent-40);
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
  word-break: break-word;
}
.wrb-cents{ font-size: 24px; opacity: .6; }
.wrb-meta-row{
  display:flex; justify-content: space-between;
  font-family: var(--font-display); font-size: var(--text-xs); color: var(--text-dim);
  padding-bottom: var(--space-5);
  border-bottom: 1px solid var(--line-soft);
}
.wrb-meta-k{ letter-spacing: .14em; color: var(--text-mute); }
.wrb-meta-v{ color: var(--text-dim); }
.wrb-countdown{
  display: flex; justify-content: space-around; align-items: stretch;
  gap: var(--space-1);
  margin-top: var(--space-3);
}
.wrb-cd-cell{
  display: flex; flex-direction: column; align-items: center;
  flex: 1 1 0; min-width: 0;
  padding: var(--space-3) 0;
  background: var(--bg-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.wrb-cd-cell b{
  font-family: var(--font-display); font-size: var(--text-xl);
  color: var(--text); line-height: 1;
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
}
.wrb-cd-cell em{
  font-style: normal; font-family: var(--font-display);
  font-size: 9px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-mute);
  margin-top: 4px;
}
.wrb-cd-sep{
  font-family: var(--font-display); font-size: var(--text-lg);
  color: var(--text-mute); align-self: center;
}
.wager-race-banner.empty .wrb-empty{
  font-family: var(--font-display); font-size: var(--text-sm);
  color: var(--text-mute);
  padding: var(--space-7) 0;
  text-align: center;
}

/* Viewer-row + gap-row inside the leaderboard (top 9 + you below). */
.lb-row.me{
  background: linear-gradient(90deg, var(--accent-15), transparent);
  border-radius: var(--radius-sm);
}
.lb-row.lb-gap{
  color: var(--text-mute);
  opacity: .55;
  padding: 2px 0;
  pointer-events: none;
}
.lb-row.lb-gap .rank{ text-align: center; letter-spacing: .3em; }

.leaderboard{
  padding: 18px;
  background: var(--panel);
  border: 1px solid var(--line-soft);
  --c: 14px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  /* Reserve space for the heading + 6 leaderboard rows so the card height
     doesn't jump when the race poll fills/empties the list. Each .lb-row
     is ~33px (8px + 8px padding + 17px content). 6×33 + heading + padding
     ≈ 270px. */
  min-height: 270px;
  contain: layout style;
}
.leaderboard h3{
  font-family: var(--font-display); font-size: 16px; margin: 0 0 12px;
  text-transform: uppercase;
}
.lb-row{
  display: grid;
  grid-template-columns: 22px 1fr auto;
  align-items:center; gap: var(--space-4);
  padding: 8px 0;
  border-bottom: 1px dashed var(--white-04);
  font-size: 13px;
}
.lb-row:last-child{ border-bottom: 0; }
.lb-row .rank{
  font-family: var(--font-display); font-weight: 700; color: var(--text-mute);
}
.lb-row.top1 .rank{ color: var(--gold); }
.lb-row.top2 .rank{ color: #d0d0d0; }
.lb-row.top3 .rank{ color: #cd9575; }
.lb-row .amt{ font-family: var(--font-display); color: var(--accent); font-weight: 700; }
/* Operator brief — tightened from --space-3 (~12 px) so the rank icon
   sits visually attached to the username on every leaderboard row. */
.lb-row .u{ display:flex; align-items:center; gap:4px; font-weight: 600; }
.lb-row .av{
  width: 22px; height:22px; border-radius:50%;
  background: linear-gradient(135deg, var(--accent), #2487AD);
}

/* ============ RIGHT PANEL ============ */
.right{
  grid-area: right;
  border-left: 1px solid var(--line-soft);
  overflow: hidden;
  display: flex; flex-direction: column;
  background: var(--bg-1);   /* solid dark — was a vertical gradient */
  transition: opacity .25s;
}
.right-closed .right{ opacity: 0; pointer-events: none; }

.right-header{
  display: flex; align-items:center;
  padding: 14px 16px;
  border-bottom: 1px solid var(--line-soft);
  gap: var(--space-3);
}
.right-tabs{
  display:flex; gap: var(--space-1);
  background: rgba(0,0,0,.3);
  padding: 3px; border-radius: var(--radius-md);
  border: 1px solid var(--line-soft);
  flex: 1;
}
.right-tabs button{
  flex: 1;
  appearance:none; border:0; cursor:pointer;
  background: transparent; color: var(--text-dim);
  font-family: var(--font-display); font-weight: 700; font-size: 11px; letter-spacing: .1em;
  padding: 8px 10px; border-radius: var(--radius-sm);
  display:flex; align-items:center; justify-content: center; gap: var(--space-2);
  transition: .15s;
}
.right-tabs button.on{
  background: var(--accent); color: var(--accent-ink);
  box-shadow: 0 0 18px rgba(52,192,235,.35);
}
.right-close{
  appearance:none; border:0; cursor:pointer;
  width: 32px; height: 32px; display:grid; place-items:center;
  color: var(--text-dim); background: transparent; border-radius: var(--radius-xs);
}
.right-close:hover{ color: var(--text); background: rgba(255,255,255,.05); }

.right-body{
  flex: 1;
  overflow-y: auto;
  padding: 14px 16px;
}

/* chat — operator feedback: bumped legibility (was 13/13 at 1.4 line —
   reads cramped). Sender name + body each up 1px; line-height up to
   1.5 so wrapped messages have proper rhythm; avatar slightly larger
   so the row reads heavier. */
.chat-msg{
  display: flex; gap: var(--space-3); margin-bottom: 12px;
  font-size: 14px; line-height: 1.5;
}
.chat-msg .av{
  width: 28px; height: 28px; border-radius: 50%;
  flex-shrink: 0;
  display:grid; place-items:center;
  color: #fff; font-weight: 700; font-size: 12px;
}
.chat-msg .body{ min-width: 0; }
.chat-msg .u{
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--font-ui); font-size: 13px; font-weight: 700;
  letter-spacing: 0;
  color: #fff;
  margin-bottom: 2px;
}
.chat-msg .u.mod{ color: var(--gold); }
.chat-msg .u.vip{ color: var(--magenta); }
.chat-msg .text{ color: var(--text); font-size: 14px; word-wrap: break-word; }

/* Inline (Twitch/Discord-style) chat rows — username + message share one
   line and wrap as a paragraph. Applied to every normal message; bet/tip
   SHARE rows omit `.chat-msg-inline` so their chip stays stacked under the
   username. */
.chat-msg-inline .body{ display: block; line-height: 1.4; }
.chat-msg-inline .u{ display: inline; margin: 0 5px 0 0; vertical-align: baseline; }
.chat-msg-inline .u .chat-rank-tip-wrap{ vertical-align: -3px; margin-right: 3px; }
.chat-msg-inline .u-name::after{ content: ':'; }
.chat-msg-inline .text{ display: inline; font-size: 13px; }
.chat-msg .text .mention{ color: var(--accent); }

.chat-input{
  padding: 12px 14px;
  border-top: 1px solid var(--line-soft);
  display:flex; gap: var(--space-3); align-items: center;
}
.chat-input input{
  flex:1;
  background: var(--white-04);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  padding: 11px 13px;
  color: var(--text); font: inherit;
  font-size: 14px;
  outline: 0;
}
.chat-input input:focus{ border-color: var(--accent-40); }
.chat-input .send{
  width: 38px; height: 38px;
  display:grid; place-items:center;
  background: var(--accent); color: var(--accent-ink);
  border: 0; cursor: pointer;
  border-radius: var(--radius-sm);
  box-shadow: 0 0 18px rgba(52,192,235,.3);
}

/* Rewards panel hero — pro rebuild. Tight composition with proper
   typography hierarchy, a CRISP visible progress bar (was barely
   showing at silver tier), and a soft tier-tinted backdrop glow for
   visual interest without noise. */
.reward-hero{
  position: relative;
  padding: 12px 14px 12px;
  background:
    radial-gradient(70% 55% at 50% 0%, rgba(255,255,255,.05), transparent 65%),
    radial-gradient(120% 70% at 50% 100%, rgba(61, 126, 255,.06), transparent 65%),
    linear-gradient(135deg, rgba(255,255,255,.025) 0%, transparent 50%),
    linear-gradient(180deg, var(--bg-2), var(--panel));
  border: 1px solid var(--line-soft);
  border-radius: 12px;
  overflow: hidden;
  text-align: center;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.04),
    0 6px 18px rgba(0,0,0,.4);
}
/* Soft halo behind the rank icon — sized down with the block. */
.reward-hero::before{
  content: '';
  position: absolute; left: 50%; top: -20px;
  width: 140px; height: 140px;
  transform: translateX(-50%);
  background: radial-gradient(circle, rgba(52,192,235,.10), transparent 60%);
  pointer-events: none;
  z-index: 0;
}
.reward-hero > *{ position: relative; z-index: 1; }

/* Tier text — uniform weight + color, tier name highlighted. Compact. */
.reward-hero .tier{
  font-family: var(--font-ui);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin: 4px 0 2px;
  line-height: 1;
}
.reward-hero .tier em{
  display: block;
  margin-top: 4px;
  font-family: var(--font-display);
  font-size: 17px;
  font-style: normal;
  font-weight: 800;
  letter-spacing: .01em;
  color: var(--text);
  text-shadow: 0 0 18px color-mix(in srgb, var(--accent) 40%, transparent);
  line-height: 1;
}

.reward-hero .pct{
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: .04em;
  color: var(--accent);
  font-weight: 600;
  margin: 6px 0 0;
}
/* Task #18 — when .pct holds plain English copy ("Sign in to track",
   "Max tier reached") instead of a numeric "47% to GOLD" value, the
   .pct--text modifier swaps it back to the UI font. Mono is reserved
   for actual numeric data. */
.reward-hero .pct.pct--text{
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: 0;
  font-weight: 600;
  text-transform: none;
}

/* Compact progress bar — slightly thinner, still glowing. */
.reward-hero .progress{
  height: 6px;
  background:
    linear-gradient(180deg, rgba(0,0,0,.35), rgba(0,0,0,.15)),
    rgba(255,255,255,.05);
  border-radius: 999px;
  margin: 8px 4px 6px;
  overflow: hidden;
  position: relative;
  box-shadow: inset 0 1px 2px rgba(0,0,0,.5);
}
.reward-hero .progress > span{
  display: block; height: 100%;
  border-radius: inherit;
  background: linear-gradient(180deg,
              color-mix(in srgb, var(--accent) 92%, white 8%) 0%,
              var(--accent) 100%);
  box-shadow:
    0 0 10px var(--accent),
    inset 0 1px 0 rgba(255,255,255,.35);
  transition: width .35s cubic-bezier(.6,.1,.2,1);
}

.reward-hero .next{
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: .14em;
  color: var(--text-mute);
  text-transform: uppercase;
}
/* Task #18 — when .next holds plain English copy ("Keep playing to
   earn rewards") instead of "Next · 1,234 XP to go", the .next--text
   modifier swaps to the UI font and drops the all-caps treatment. */
.reward-hero .next.next--text{
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: 0;
  font-weight: 500;
  text-transform: none;
  color: var(--text-dim);
}

/* "View Rewards →" CTA — keep breathing room but tighter overall. */
.reward-hero .btn-primary{
  margin-top: 12px;
  padding: 8px 14px;
  font-size: 11px;
  letter-spacing: .08em;
  border-radius: 9px;
}
.reward-btn{
  margin-top: 12px;
  appearance:none; border:0; cursor: pointer;
  background: linear-gradient(180deg, var(--accent-bright), var(--accent));
  color: var(--on-accent);
  font-family: var(--font-display); font-weight: 800; font-size: 12px; letter-spacing: .08em;
  padding: 10px 16px;
  --c: 8px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  box-shadow: 0 6px 18px var(--accent-glow);
  width: 100%;
  transition: box-shadow .2s, filter .15s;
}
.reward-btn:hover{ filter: brightness(1.04); box-shadow: 0 10px 24px var(--accent-glow); }

.reward-section-label{
  font-family: var(--font-display); font-size: 11px; letter-spacing: .15em;
  color: var(--text-mute); margin: 20px 0 10px;
  display:flex; align-items:center; gap: var(--space-3);
}
.reward-section-label::after{
  content:""; flex:1; height: 1px; background: var(--line-soft);
}

.reward-grid{
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4);
}
.reward-card{
  padding: var(--space-6);
  background: var(--panel);
  border: 1px solid var(--line-soft);
  --c: 10px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  position: relative;
}
/* Reserve right-side space on the header rows for the absolutely-
   positioned .r-art icon (36px wide, pinned 10px from the edge — so
   ~46px of unusable space). Without this padding, .timing / .r-title
   and especially long .r-value amounts ($12,345.67) ran under the
   icon at narrow right-panel widths. min-width:0 + overflow-wrap
   keep long amounts wrapping gracefully instead of pushing the card
   wider than its grid cell. */
.reward-card .timing{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .1em;
  color: var(--text-mute); text-transform: uppercase;
  padding-right: 46px; min-width: 0;
}
.reward-card .r-title{
  font-family: var(--font-display); font-size: 12px;
  text-transform: uppercase; margin: 12px 0 6px; letter-spacing: -.005em;
  padding-right: 46px; min-width: 0;
  overflow-wrap: anywhere;
}
.reward-card .r-value{
  font-family: var(--font-display); font-weight: 700; font-size: 15px; color: var(--accent);
  margin-bottom: 10px;
  padding-right: 46px; min-width: 0;
  overflow-wrap: anywhere; word-break: break-word;
}
.reward-card .r-btn{
  appearance:none; border: 1px solid var(--accent); cursor:pointer;
  background: transparent; color: var(--accent);
  font-family: var(--font-display); font-weight: 700; font-size: 11px; letter-spacing: .08em;
  padding: 7px 10px;
  width: 100%;
  --c: 6px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  transition: .15s;
}
.reward-card .r-btn:hover{ background: var(--accent); color: var(--accent-ink); }
.reward-card .r-btn.locked{
  border-color: var(--line-soft); color: var(--text-mute);
  cursor: not-allowed;
}
.reward-card .r-btn.locked:hover{ background: transparent; color: var(--text-mute); }
.reward-card .r-art{
  position: absolute; top: 10px; right: 10px;
  width: 36px; height: 36px;
  opacity: .9;
}

.redeem-box{
  padding: var(--space-6);
  background: var(--panel);
  border: 1px dashed var(--line-soft);
  border-radius: var(--radius-md);
  display:flex; gap: var(--space-3);
  margin-top: 12px;
}
.redeem-box input{
  flex:1;
  background: transparent; border: 0; outline: 0;
  color: var(--text); font: inherit; font-family: var(--font-display); font-size: 12px;
}
.redeem-box input::placeholder{ color: var(--text-mute); }
.redeem-box button{
  appearance:none; border:0; cursor:pointer;
  background: var(--accent-15); color: var(--accent);
  font-family: var(--font-display); font-weight: 700; font-size: 11px; letter-spacing: .08em;
  padding: 7px 12px; border-radius: var(--radius-xs);
}
/* Plain English placeholder ("Enter code...") in the UI font, while
   the actual entered value stays mono since redemption codes are
   alphanumeric tokens. */
.redeem-box input::placeholder{ font-family: var(--font-ui); font-weight: 500; }

/* Right panel toggle FAB when closed */
.right-fab{
  position: fixed;
  right: 20px; bottom: 20px;
  z-index: 20;
  appearance: none; border: 0; cursor: pointer;
  width: 52px; height: 52px;
  background: var(--accent); color: var(--accent-ink);
  border-radius: 50%;
  display: grid; place-items: center;
  box-shadow: 0 10px 30px rgba(52,192,235,.45);
  transition: transform .2s;
}
.right-fab:hover{ transform: scale(1.05); }
.right-fab .badge{
  position: absolute; top: -2px; right: -2px;
  min-width: 18px; height: 18px;
  background: var(--danger); color: #fff;
  font-family: var(--font-display); font-size: 10px; font-weight: 700;
  display:grid; place-items:center;
  border-radius: var(--radius-pill);
  padding: 0 4px;
  border: 2px solid var(--bg);
}

/* Tweaks panel */
.tweaks{
  position: fixed;
  right: 20px; bottom: 90px;
  z-index: 30;
  padding: 14px 16px;
  background: rgba(7,10,28,.92);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(52,192,235,.3);
  --c: 10px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  min-width: 220px;
}
.tweaks h5{
  font-family: var(--font-display); font-size: 11px; letter-spacing: .15em;
  color: var(--accent); margin: 0 0 10px;
}
.tweaks label{
  display:flex; align-items:center; justify-content: space-between;
  gap: var(--space-5); font-size: 13px; color: var(--text-dim); cursor: pointer;
  padding: 6px 0;
}
.tweaks .switch{
  width: 34px; height: 20px; border-radius: var(--radius-pill);
  background: rgba(255,255,255,.08);
  position: relative; flex-shrink: 0;
  transition: .2s;
}
.tweaks .switch::before{
  content:""; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: #fff; transition: .2s;
}
.tweaks input:checked + .switch{ background: var(--accent); }
.tweaks input:checked + .switch::before{ transform: translateX(14px); background: var(--accent-ink); }
.tweaks input{ display: none; }

/* ============ PURPLE ACCENTS — mixed into UI ============ */
.nav-item .n-badge.purple{ background: var(--accent-25); color: var(--purple); }
.chip.purple.on{ background: var(--purple); color: #fff; border-color: var(--purple); box-shadow: 0 0 18px var(--purple-glow); }

.brand{
  background: linear-gradient(180deg, rgba(61, 126, 255,.05), transparent);
}
.brand .logo-bracket{
  background: linear-gradient(180deg, var(--accent), var(--purple));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

.rail-player{
  background: linear-gradient(135deg, rgba(61, 126, 255,.08), var(--panel-2));
  border-color: rgba(61, 126, 255,.2);
}
.rail-player::before{
  background: radial-gradient(circle, rgba(61, 126, 255,.3), transparent 60%);
}
.rail-player .rank em{ color: var(--purple); }
.rail-player .bar > span{
  background: linear-gradient(90deg, var(--purple-2), var(--purple));
  box-shadow: 0 0 12px var(--purple-glow);
}

.mode.live{
  background:
    radial-gradient(80% 100% at 100% 50%, rgba(61, 126, 255,.25), transparent 60%),
    linear-gradient(135deg, #1a0f24, #0a0614);
}
.mode-accent-live{ color: var(--purple); text-shadow: 0 0 16px var(--purple-glow); }

/* Operator rebuild — the legacy home hero used a 1-main + 2-side
   layout where the 2nd .hero-card was a blue VIP tile, and this rule
   tinted its border to match. The current layout is the three-card
   carousel (HeroCarousel in src/hero.jsx), where the 2nd card is just
   another promo (currently "New Blackjack Modes") and a blue border
   reads as a stray accent. Rule kept commented out as a reference
   for any future revival of the legacy layout.

.hero-card:nth-child(2){
  border-color: rgba(61, 126, 255,.25) !important;
}
*/

/* Jackpot: alternate blue variant (was "purple" — kept class name for compat) */
.jackpot.purple{
  background:
    radial-gradient(60% 120% at 100% 0%, rgba(41,82,209,.25), transparent 60%),
    linear-gradient(135deg, #070A1C, #04060E);
  border-color: rgba(41,82,209,.28);
}
.jackpot.purple .j-label{ color: var(--blue-3); }
.jackpot.purple .j-amount{ color: var(--blue-3); text-shadow: 0 0 22px var(--purple-glow); }

/* ============ RESPONSIVE — main layout ============ */
/* (the base .main rule above handles the max-width+gutter behaviour) */

@media (max-width: 1060px){
  .hero{ grid-template-columns: 1fr; }
  .hero-main{ min-height: 240px; padding: 20px 22px; }
  .hero-main .hero-art{ opacity: .5; width: 50%; right: -50px; top: 10px; bottom: 10px; }
  .hero-title{ font-size: clamp(28px, 5.2vw, 44px); }
  .activity-wrap{ grid-template-columns: 1fr; }
  .modes{ grid-template-columns: 1fr 1fr; grid-template-rows: auto; }
  .mode.slots{ grid-row: span 1; grid-column: span 2; }
  .mode.slots .m-title{ font-size: 44px; }
}

@media (max-width: 860px){
  .main{ padding: 16px 14px 80px; }
  .sec-head{ flex-wrap: wrap; gap: var(--space-4); margin: 20px 0 10px; }
  .sec-head h2{ font-size: 20px; }
  .sec-tools{ gap: var(--space-2); flex-wrap: wrap; }
  .chip{ padding: 6px 10px; font-size: 11px; }

  /* The canonical mobile hero layout (2 promo cards) lives further down
     in the "MOBILE LAYOUT FIXES" block.  Here we just set sensible
     fallbacks so nothing overflows before that block applies. */
  .hero-main .hero-art{ display: none; }
  .hero-ctas{ gap: var(--space-3); }
  .hero-ctas .btn-primary,
  .hero-ctas .btn-ghost{ padding: 9px 14px; font-size: 12px; }

  .modes{
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    gap: var(--space-4);
  }
  .mode{ min-height: 120px; padding: 14px 16px; }
  .mode.slots{ grid-column: span 1; grid-row: span 1; }
  .mode.slots .m-title{ font-size: 36px; }
  .mode .m-title{ font-size: 26px; }

  .scroller{ grid-auto-columns: 140px; gap: var(--space-4); }
  .scroller.tall{ grid-auto-columns: 160px; }

  .activity{ padding: var(--space-7); }
  .activity-head h3{ font-size: 16px; }
  /* Column drops are handled by the @container activitytable queries
     (≈line 1743) which are aware of the current 6-column layout. The old
     viewport rules here targeted stale nth-child indices for a 4/5-column
     table and set a grid-template with fewer tracks than children, which
     pushed the payout into a clipped implicit track — leaving only the
     game name visible on phones. Removed; container queries govern. */
  .activity-row .mono{ font-size: 11px; }
  .jackpot .j-amount{ font-size: 32px; }

  .right-fab{ right: 14px; bottom: 14px; }
  .tweaks{ right: 14px; bottom: 76px; min-width: 200px; }
}

@media (max-width: 480px){
  .hero-stats{ grid-template-columns: 1fr 1fr; gap: var(--space-6); }
  .hero-stat:nth-child(3){ display: none; }
  .hero-ctas .btn-primary{ flex: 1; justify-content: center; }
  /* activity-row column drops handled by @container activitytable queries. */
}

/* ============ MODAL SYSTEM ============ */
.modal-root{
  position: fixed; inset: 0; z-index: 100;
  display: grid; place-items: center;
  padding: var(--space-9);
  background: rgba(0,0,0,.72);
  backdrop-filter: blur(10px);
  animation: modalFadeIn .2s ease-out;
}
@keyframes modalFadeIn{ from { opacity: 0; } to { opacity: 1; } }

.modal-shell{
  position: relative;
  /* Phase 24: every modal respects the page gutter at every viewport.
     min() between the size cap and the viewport-minus-gutter prevents
     edge-cropping at intermediate widths. */
  width: min(1040px, calc(100vw - var(--main-pad) * 2));
  max-height: calc(100vh - 48px);
  background: var(--bg-2);   /* flat dark — was a vertical gradient */
  border: 1px solid var(--line);
  --c: 22px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  overflow: hidden;
  animation: modalIn .3s cubic-bezier(.4,.8,.3,1.1);
  display: flex; flex-direction: column;
}
@keyframes modalIn{ from { transform: translateY(12px) scale(.98); opacity:0; } to { transform: none; opacity: 1; } }

.modal-shell.narrow { width: min(460px, calc(100vw - var(--main-pad) * 2)); }
.modal-shell.small  { width: min(520px, calc(100vw - var(--main-pad) * 2)); }
.modal-shell.medium { width: min(720px, calc(100vw - var(--main-pad) * 2)); }
.modal-shell.large  { width: min(900px, calc(100vw - var(--main-pad) * 2)); }

.modal-close{
  /* Operator rebuild — close button uses new motion tokens + soft
     accent on hover instead of just a border swap. */
  position: absolute; top: 18px; right: 18px; z-index: 20;
  appearance: none; border: 1px solid var(--border-default);
  background: rgba(0,0,0,.4); backdrop-filter: blur(8px);
  color: var(--text-secondary);
  width: 38px; height: 38px; border-radius: var(--radius-sm);
  display: grid; place-items: center; cursor: pointer;
  transition: color var(--dur-base) var(--ease-standard),
              border-color var(--dur-base) var(--ease-standard),
              background var(--dur-base) var(--ease-standard),
              transform var(--dur-fast) var(--ease-standard);
}
.modal-close:hover{
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
  transform: rotate(90deg);
}

.modal-body{
  overflow-y: auto;
  overscroll-behavior: contain;
  flex: 1; min-height: 0;
}

/* ---- VIP MODAL ---- */
.vip-hero{
  position: relative;
  padding: 42px 48px 36px;
  background:
    radial-gradient(80% 100% at 80% 0%, rgba(61, 126, 255,.3), transparent 55%),
    radial-gradient(60% 80% at 10% 100%, rgba(52,192,235,.12), transparent 55%),
    linear-gradient(180deg, #140a1e, #0a0514);
  border-bottom: 1px solid rgba(61, 126, 255,.15);
  overflow: hidden;
}
.vip-hero::after{
  content:""; position:absolute; inset:0;
  background-image:
    linear-gradient(rgba(61, 126, 255,.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(61, 126, 255,.06) 1px, transparent 1px);
  background-size: 44px 44px;
  mask-image: radial-gradient(60% 60% at 80% 20%, #000, transparent 70%);
  pointer-events: none;
}
.vip-kicker{
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-display); font-size: 11px; letter-spacing: .22em;
  color: var(--purple);
  padding: 6px 10px;
  border: 1px solid rgba(61, 126, 255,.35);
  background: var(--accent-soft);
  border-radius: var(--radius-xs);
  position: relative; z-index: 2;
}
.vip-hero h1{
  font-family: var(--font-display);
  font-size: clamp(38px, 5vw, 72px);
  line-height: .88;
  letter-spacing: -.02em;
  margin: 16px 0 8px;
  text-transform: uppercase;
  position: relative; z-index: 2;
}
.vip-hero h1 em{
  font-style: normal;
  background: linear-gradient(180deg, var(--purple), var(--purple-3));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  text-shadow: 0 0 42px rgba(61, 126, 255,.5);
}
.vip-hero p{
  max-width: 520px;
  color: var(--text-dim); font-size: 15px; line-height: 1.55;
  margin: 0;
  position: relative; z-index: 2;
}

.vip-status{
  position: relative; z-index: 2;
  margin-top: 28px;
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--space-9);
  align-items: center;
  padding: 18px 20px;
  background: rgba(0,0,0,.35);
  border: 1px solid rgba(61, 126, 255,.22);
  border-radius: var(--radius);
  backdrop-filter: blur(4px);
}
.vip-status .tier-badge{
  width: 56px; height: 56px;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--purple), var(--purple-3));
  --c: 12px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  color: #fff; font-family: var(--font-display); font-size: 22px;
  box-shadow: 0 0 30px var(--purple-glow);
}
.vip-status .st-label{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .2em;
  color: var(--text-mute); text-transform: uppercase;
}
.vip-status .st-tier{
  font-family: var(--font-display); font-size: 20px;
  text-transform: uppercase; margin-top: 2px;
}
.vip-status .st-progress{
  height: 8px; background: var(--white-06); border-radius: var(--radius-pill);
  margin-top: 10px; overflow: hidden;
}
.vip-status .st-progress > span{
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--purple-2), var(--purple));
  box-shadow: 0 0 14px var(--purple-glow);
  width: 42%;
}
.vip-status .st-meta{
  display: flex; justify-content: space-between;
  font-family: var(--font-display); font-size: 11px;
  color: var(--text-dim); margin-top: 6px;
}
.vip-status .st-claim{
  appearance:none; border:0; cursor:pointer;
  font-family: var(--font-display); font-weight: 800; font-size: 12px; letter-spacing: .08em;
  padding: 12px 18px;
  background: var(--accent); color: var(--accent-ink);
  --c: 8px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  box-shadow: 0 6px 22px rgba(52,192,235,.35);
}

.vip-section{ padding: 36px 48px; }
.vip-section-head{
  display: flex; align-items: end; justify-content: space-between;
  margin-bottom: 20px; gap: var(--space-7);
}
.vip-section-head .kicker{
  font-family: var(--font-display); font-size: 11px; letter-spacing: .2em;
  color: var(--text-mute); text-transform: uppercase;
  display: block; margin-bottom: 6px;
}
.vip-section-head h2{
  font-family: var(--font-display); font-size: 28px;
  margin: 0; text-transform: uppercase; letter-spacing: -.01em;
}
.vip-section-head h2 em{ font-style: normal; color: var(--purple); }

.tier-rail{
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--space-4);
  margin-bottom: 18px;
}
.tier-card{
  position: relative;
  padding: 16px 14px;
  background: var(--panel-2);
  border: 1px solid var(--line-soft);
  --c: 10px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  cursor: pointer;
  transition: .18s;
  text-align: left;
  color: var(--text);
}
.tier-card:hover{ transform: translateY(-2px); border-color: rgba(61, 126, 255,.35); }
.tier-card.current{
  background:
    radial-gradient(70% 100% at 100% 0%, rgba(61, 126, 255,.28), transparent 60%),
    linear-gradient(180deg, #18092a, #0a0416);
  border-color: rgba(61, 126, 255,.55);
  box-shadow: 0 0 0 1px rgba(61, 126, 255,.25), 0 10px 40px rgba(61, 126, 255,.22);
}
.tier-card.locked{ opacity: .55; }
.tier-card .tc-dot{
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--purple); box-shadow: 0 0 10px var(--purple);
  margin-bottom: 12px;
}
.tier-card.locked .tc-dot{ background: var(--text-mute); box-shadow: none; }
.tier-card.current .tc-dot::after{
  content:""; position: absolute;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--purple); opacity: .4;
  animation: pulse 1.6s infinite;
}
.tier-card .tc-name{
  font-family: var(--font-display); font-size: 14px;
  text-transform: uppercase; letter-spacing: -.005em;
}
.tier-card .tc-rake{
  font-family: var(--font-display); font-size: 11px;
  color: var(--text-dim); margin-top: 4px;
}
.tier-card .tc-req{
  margin-top: 12px; padding-top: 10px;
  border-top: 1px dashed var(--white-06);
  font-family: var(--font-display); font-size: 10px; letter-spacing: .1em;
  color: var(--text-mute); text-transform: uppercase;
}
.tier-card .tc-badge{
  position: absolute; top: 10px; right: 10px;
  font-family: var(--font-display); font-size: 9px; letter-spacing: .12em;
  padding: 2px 6px; border-radius: var(--radius-2xs);
  background: var(--purple); color: #fff;
}

.perks-grid{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-5);
}
.perk{
  position: relative;
  padding: var(--space-8);
  background: var(--panel);
  border: 1px solid var(--line-soft);
  --c: 12px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  overflow: hidden;
}
.perk::before{
  content:""; position: absolute; top:-30px; right: -30px;
  width: 90px; height: 90px; border-radius: 50%;
  background: radial-gradient(circle, rgba(61, 126, 255,.2), transparent 60%);
}
.perk .p-icon{
  width: 42px; height: 42px; border-radius: var(--radius-md);
  display: grid; place-items: center;
  background: var(--accent-soft);
  color: var(--purple);
  margin-bottom: 14px;
}
.perk h4{
  font-family: var(--font-display); font-size: 14px;
  margin: 0 0 6px; text-transform: uppercase; letter-spacing: -.005em;
}
.perk p{ font-size: 13px; color: var(--text-dim); margin: 0; line-height: 1.5; }

.bonus-row{
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4);
}
.bonus-card{
  padding: 18px;
  background:
    radial-gradient(80% 100% at 100% 0%, rgba(61, 126, 255,.14), transparent 60%),
    linear-gradient(180deg, #10071c, #06030c);
  border: 1px solid rgba(61, 126, 255,.22);
  --c: 12px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  text-align: left;
}
.bonus-card .b-title{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .15em;
  color: var(--text-mute); text-transform: uppercase;
}
.bonus-card .b-value{
  font-family: var(--font-display); font-size: 22px;
  color: var(--purple);
  margin: 8px 0 6px;
  text-shadow: 0 0 18px var(--purple-glow);
}
.bonus-card .b-caption{ font-size: 12px; color: var(--text-dim); }

.vip-footer{
  padding: 26px 48px;
  background: rgba(0,0,0,.35);
  border-top: 1px solid var(--line-soft);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-7);
}
.vip-footer .fine{
  font-family: var(--font-display); font-size: 11px; letter-spacing: .08em;
  color: var(--text-mute);
}

@media (max-width: 860px){
  .vip-hero{ padding: 30px 22px 26px; }
  .vip-hero h1{ font-size: clamp(32px, 10vw, 52px); }
  .vip-status{ grid-template-columns: 1fr; gap: var(--space-6); text-align: center; }
  .vip-status .tier-badge{ justify-self: center; }
  .vip-section{ padding: 26px 22px; }
  .tier-rail{ grid-template-columns: repeat(3, 1fr); }
  .perks-grid{ grid-template-columns: 1fr 1fr; gap: var(--space-4); }
  .bonus-row{ grid-template-columns: 1fr 1fr; }
  .vip-footer{ flex-direction: column; align-items: stretch; padding: 20px 22px; }
  .vip-footer .btn-primary{ width: 100%; justify-content: center; }
  .modal-shell{ --c: 16px; max-height: calc(100vh - 24px); }
  .modal-root{ padding: var(--space-5); }
}
@media (max-width: 480px){
  .tier-rail{ grid-template-columns: 1fr 1fr; }
  .perks-grid{ grid-template-columns: 1fr; }
  .bonus-row{ grid-template-columns: 1fr 1fr; }
}

/* ---- GAME MODAL ---- */
.game-modal{
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  min-height: 480px;
}
.game-modal .g-art{
  position: relative;
  background: var(--panel-2);
  overflow: hidden;
  min-height: 320px;
}
.game-modal .g-art .poster-wrap{ position: absolute; inset: 0; }
.game-modal .g-body{
  padding: 32px 36px;
  display: flex; flex-direction: column; gap: 18px;
  overflow-y: auto;
}
.game-modal .g-tag{
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-display); font-size: 10px; letter-spacing: .2em;
  color: var(--accent); text-transform: uppercase;
  align-self: flex-start;
}
.game-modal .g-tag .pulse{
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 8px var(--accent);
  animation: pulse 1.6s infinite;
}
.game-modal h2{
  font-family: var(--font-display); font-size: clamp(28px, 3.5vw, 44px);
  margin: 0; text-transform: uppercase; letter-spacing: -.02em; line-height: .9;
}
.game-modal .g-provider{
  font-family: var(--font-display); font-size: 12px; letter-spacing: .1em;
  color: var(--text-dim);
}
.game-modal .g-desc{
  font-size: 14px; color: var(--text-dim); line-height: 1.55;
  margin: 0;
}
.game-modal .g-stats{
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4);
  padding: var(--space-7); background: rgba(0,0,0,.3);
  border: 1px solid var(--line-soft); border-radius: var(--radius);
}
.game-modal .g-stat .k{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .15em;
  color: var(--text-mute); text-transform: uppercase;
}
.game-modal .g-stat .v{
  font-family: var(--font-display); font-size: 18px; margin-top: 4px;
}
.game-modal .g-stat .v em{ color: var(--accent); font-style: normal; }
.game-modal .g-ctas{ display: flex; gap: var(--space-4); flex-wrap: wrap; }
.game-modal .g-meta{
  display: flex; gap: 18px; flex-wrap: wrap;
  padding-top: 14px; border-top: 1px dashed var(--line-soft);
  font-family: var(--font-display); font-size: 11px; color: var(--text-dim);
}
.game-modal .g-meta span em{ color: var(--purple); font-style: normal; }

@media (max-width: 860px){
  .game-modal{ grid-template-columns: 1fr; }
  .game-modal .g-art{ min-height: 220px; }
  .game-modal .g-body{ padding: 24px 22px; }
}

/* ---- AUTH MODAL ---- */
/* ============================================================
   AUTH MODAL — full redesign. Two columns: brand artwork on the
   left, form on the right. Gold/dark brand theme, modern field +
   button system. The image + bottom legal scrim live on
   .a-side-image (additions.css); the shell chamfer is squared off
   to a clean rounded card via `.modal-shell:has(.auth)` there too.
   ============================================================ */
.auth{
  display: grid;
  grid-template-columns: 0.82fr 1fr;
  min-height: 600px;
  background: var(--bg-2);
}
.auth .a-side{
  position: relative;
  overflow: hidden;
  display: block;
}
/* Bottom legal scrim on the artwork — small muted line with a
   highlighted Terms link, matching the reference treatment. */
.auth .a-side-legal{
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 56px 30px 26px;
  font-size: 12px;
  line-height: 1.5;
  color: rgba(255,255,255,.62);
  background: linear-gradient(180deg, transparent 0%, rgba(0,0,0,.55) 55%, rgba(0,0,0,.82) 100%);
  pointer-events: none;
}
.auth .a-side-legal a{
  color: #fff;
  font-weight: 700;
  text-decoration: none;
  pointer-events: auto;
}
.auth .a-side-legal a:hover{ color: var(--accent); }

.auth .a-form{
  padding: 40px 44px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  overflow-y: auto;
  background: var(--bg-2);
}
.auth .a-head{ display: flex; flex-direction: column; gap: 6px; }
.auth .a-title{
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 800;
  letter-spacing: -.01em;
  color: var(--text);
  margin: 0;
}
.auth .a-sub{
  font-size: 13.5px;
  color: var(--text-dim);
  margin: 0;
}
.auth .a-switch{
  color: var(--accent);
  font-weight: 700;
  cursor: pointer;
  text-decoration: none;
}
.auth .a-switch:hover{ text-decoration: underline; }

/* Field — visible sentence-case label above a soft dark input. */
.field{ display: flex; flex-direction: column; gap: 7px; }
.field label{
  font-family: var(--font-ui);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--text);
  text-transform: none;
}
.field input{
  /* width:100% + border-box + min-width:0 — without these the input
     keeps its intrinsic ~170px size attribute, which inside the
     register 2-column grid kept the track from shrinking and pushed
     the right column past the form's padding (clipped fields). */
  width: 100%;
  box-sizing: border-box;
  min-width: 0;
  background: rgba(255,255,255,.035);
  border: 1px solid var(--white-06, rgba(255,255,255,.07));
  border-radius: 10px;
  padding: 13px 14px;
  color: var(--text); font: inherit; font-size: 14px;
  outline: 0;
  transition: border-color .15s var(--ease-standard, ease),
              background .15s var(--ease-standard, ease),
              box-shadow .15s var(--ease-standard, ease);
}
.field input::placeholder{ color: var(--text-mute); }
.field input:focus{
  border-color: var(--accent);
  background: var(--white-04, rgba(255,255,255,.05));
  box-shadow: 0 0 0 3px var(--accent-soft, rgba(52,192,235,.14));
}
.field.error input{ border-color: var(--danger); }
.field .hint{
  font-family: var(--font-ui);
  font-size: 11px;
  color: var(--text-mute);
}

/* Password label row — label left, "Forgot Password?" right. */
.auth .a-label-row{
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
}
.auth .a-forgot{
  font-size: 12px;
  color: var(--text-dim);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}
.auth .a-forgot:hover{ color: var(--accent); }

/* Password input + eye-toggle. The eye is a square button pinned
   to the right edge inside the field. */
.auth .a-pass{ position: relative; }
.auth .a-pass input{ width: 100%; padding-right: 50px; }
.auth .a-eye{
  position: absolute; top: 50%; right: 6px;
  transform: translateY(-50%);
  width: 36px; height: 36px;
  display: grid; place-items: center;
  appearance: none; cursor: pointer;
  background: rgba(255,255,255,.05);
  border: 1px solid var(--white-06, rgba(255,255,255,.07));
  border-radius: 8px;
  color: var(--text-mute);
  transition: color .15s var(--ease-standard, ease),
              background .15s var(--ease-standard, ease),
              border-color .15s var(--ease-standard, ease);
}
.auth .a-eye:hover{ color: var(--accent); border-color: var(--accent); background: var(--accent-soft, rgba(52,192,235,.12)); }

/* Register checkbox rows + optional referral-code input. */
/* Register 2-column field grid — keeps the full signup form short
   enough to display without an inner scrollbar. Username | Email on
   row 1, Password | Confirm Password on row 2. Collapses to a single
   column on mobile (the artwork column is hidden there anyway). */
.auth .a-grid2{
  display: grid;
  /* minmax(0,1fr) (NOT 1fr → minmax(auto,1fr)) lets each track shrink
     below the input's intrinsic width so the right column can't spill
     past the form padding and get clipped. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 14px 16px;
}
.auth .a-grid2 .field,
.auth .a-grid2 .a-pass{ min-width: 0; }
.auth .a-grid2 .a-pass input{ box-sizing: border-box; min-width: 0; }
@media (max-width: 860px){
  .auth .a-grid2{ grid-template-columns: 1fr; }
}

/* Checkboxes share one row (Terms · Code) so they cost a single line
   instead of two; the optional referral input drops full-width below
   them when "Code" is ticked. Another ~24px reclaimed for the
   no-scroll fit. */
.auth .a-checks{
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 10px 22px;
}
.auth .a-checks .a-code-input{ flex: 1 0 100%; }
.auth .a-check{
  display: flex; align-items: center; gap: 10px;
  font-size: 13px; color: var(--text-dim); cursor: pointer;
  user-select: none;
}
.auth .a-check input[type="checkbox"]{
  width: 17px; height: 17px;
  accent-color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
}
.auth .a-check a{ color: var(--accent); font-weight: 700; text-decoration: none; }
.auth .a-check a:hover{ text-decoration: underline; }
.auth .a-code-input{
  background: rgba(255,255,255,.035);
  border: 1px solid var(--white-06, rgba(255,255,255,.07));
  border-radius: 10px;
  padding: 13px 14px;
  color: var(--text); font: inherit; font-size: 14px;
  outline: 0;
  transition: border-color .15s var(--ease-standard, ease), background .15s var(--ease-standard, ease);
}
.auth .a-code-input:focus{ border-color: var(--accent); background: var(--white-04, rgba(255,255,255,.05)); }
.auth .a-code-input::placeholder{ color: var(--text-mute); }

/* Inline status messages. */
.auth .a-msg{
  font-size: 13px;
  padding: 10px 12px;
  border-radius: 8px;
  margin: -2px 0 0;
}
.auth .a-msg-err{
  color: var(--danger, #ff5d6c);
  background: rgba(255,93,108,.10);
  border: 1px solid rgba(255,93,108,.28);
}
.auth .a-msg-ok{
  color: var(--accent);
  background: var(--accent-soft, rgba(52,192,235,.12));
  border: 1px solid rgba(52,192,235,.28);
}

/* Primary CTA — modernized gold (sheen gradient + depth glow,
   crisp hover lift / press). Rounded rectangle (not a full pill)
   to match the reference proportion. */
.auth .a-submit{
  appearance: none; border: 0; cursor: pointer;
  width: 100%;
  background: linear-gradient(180deg, var(--accent-bright, #6FD4F2) 0%, var(--accent, #34c0eb) 100%);
  color: var(--accent-ink, #1A1205);
  font-family: var(--font-display); font-weight: 800; font-size: 15px;
  letter-spacing: .01em;
  padding: 15px 20px;
  border-radius: 10px;
  box-shadow: 0 1px 0 rgba(255,255,255,.25) inset,
              0 6px 18px var(--accent-glow, rgba(52,192,235,.30));
  transition: transform var(--dur-fast, .12s) var(--ease-standard, ease),
              filter var(--dur-base, .2s) var(--ease-standard, ease),
              box-shadow var(--dur-base, .2s) var(--ease-standard, ease);
  margin-top: 2px;
}
.auth .a-submit:hover{
  transform: translateY(-1px);
  filter: brightness(1.05);
  box-shadow: 0 1px 0 rgba(255,255,255,.3) inset,
              0 10px 26px var(--accent-glow, rgba(52,192,235,.42));
}
.auth .a-submit:active{ transform: translateY(0); filter: brightness(.97); }
.auth .a-submit:disabled{
  transform: none;
  filter: grayscale(.4) brightness(.72);
  cursor: not-allowed;
  box-shadow: none;
}

.auth .a-divider{
  display: flex; align-items: center; gap: 14px;
  font-family: var(--font-ui); font-size: 12px;
  color: var(--text-mute); margin: 4px 0;
}
.auth .a-divider::before, .auth .a-divider::after{
  content:""; flex: 1; height: 1px; background: var(--white-06, rgba(255,255,255,.08));
}
.auth .a-divider span{ flex: 0 0 auto; }

.auth .a-social{ display: flex; gap: 10px; }
.auth .a-social button{
  flex: 1;
  height: 48px;
  appearance: none; cursor: pointer;
  background: rgba(255,255,255,.04);
  border: 1px solid var(--white-06, rgba(255,255,255,.07));
  color: var(--text-secondary, var(--text-dim));
  border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  transition: color .15s var(--ease-standard, ease),
              background .15s var(--ease-standard, ease),
              border-color .15s var(--ease-standard, ease);
}
.auth .a-social button:not(:disabled):hover{
  color: var(--text);
  border-color: var(--accent);
  background: var(--accent-soft, rgba(52,192,235,.10));
}
.auth .a-social button:disabled{ opacity: .55; cursor: not-allowed; }

/* .a-foot retained — the 2FA step still uses it for "← Back to login". */
.auth .a-foot{
  font-size: 12.5px; color: var(--text-dim); text-align: center; margin-top: 4px;
}
.auth .a-foot a{ color: var(--accent); font-weight: 700; text-decoration: none; cursor: pointer; }
.auth .a-foot a:hover{ text-decoration: underline; }

@media (max-width: 760px){
  .auth{ grid-template-columns: 1fr; min-height: 0; }
  .auth .a-side{
    padding: 24px 22px;
    border-right: 0;
    border-bottom: 1px solid rgba(61, 126, 255,.2);
  }
  .auth .a-bullets{ display: none; }
  .auth .a-tagline{ margin-top: 16px; }
  .auth .a-form{ padding: 24px 22px; }
}

/* ============ SIDEBAR — player top / mode toggle / search ============ */
.rail-player-top{
  margin: 0 4px 14px;
}
/* Phase 24+ — match the HOME HERO side-card chrome (.hero-card.frame):
   chamfered corners (clip-path on top-left + bottom-right) + the same
   neon-hairline gradient border that the .frame class paints via ::before.
   The inline tier-tinted gradient comes from sidebar.jsx so the box's
   personality picks up the user's current tier color.

   Sizing uses clamp() so the card content auto-shrinks at narrow rail
   widths instead of overflowing or clipping. cqw is the rail's own
   container width — defined as a query container below. */
.rail-player-top.rp-frame{
  position: relative;
  /* Container query the box itself so descendants can size against ITS
     width, not the viewport. Box width tracks the rail (232/220/76/drawer)
     minus padding, so cqw lets us scale font/padding fluidly without
     writing a media query per breakpoint.

     The cqw multipliers are calibrated so a FULL-rail box (~190px inner
     width) lands at the MAX of each clamp (avatar 38, user 14px, etc.),
     and shrinks down to the MIN cleanly at half-width or smaller. */
  container-type: inline-size;
  container-name: rpcard;
  /* Padding: 7.5cqw of 190 = 14.25 ≈ 14px max */
  padding: clamp(8px, 7.5cqw, 14px);
  /* Chamfer chip — top-left + bottom-right corners cut, matching .hero-card. */
  --c: clamp(8px, 8cqw, 16px);
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)),
                     calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  overflow: hidden;
  /* Inline `background` (set by sidebar.jsx) supplies the colored gradient.
     The .frame ::before pseudo paints the gradient hairline. */
}
/* The .frame ::before defaults to a sharp rectangle border. Since we use
   a chamfer clip-path on the OUTER element, the rectangle border gets
   clipped along with the bg — automatically matching the chamfer shape. */
.rail-player-top.rp-frame .rp-top{
  /* Tighter gap so avatar + name + emblem all fit at narrow rail widths.
     min-width:0 on the middle column lets username text truncate cleanly
     instead of overflowing. */
  gap: clamp(6px, 5cqw, 10px);
  grid-template-columns: clamp(28px, 20cqw, 38px) minmax(0, 1fr) auto;
  align-items: center;
}
.rail-player-top.rp-frame .rp-avatar{
  width: clamp(28px, 20cqw, 38px);
  height: clamp(28px, 20cqw, 38px);
  font-size: clamp(12px, 8cqw, 16px);
  flex-shrink: 0;
}
.rail-player-top.rp-frame .rp-name{
  min-width: 0;
}
.rail-player-top.rp-frame .rp-user{
  font-size: clamp(11px, 7.5cqw, 14px);
  font-weight: 700;
  /* Username can be long; truncate with ellipsis instead of overflowing. */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rail-player-top.rp-frame .rp-tier{
  font-weight: 700;
  letter-spacing: .12em;
  font-size: clamp(8.5px, 5.6cqw, 10.5px);
  margin-top: 2px;
  /* Tier label can also truncate in narrow rails (e.g. "Titanium III"). */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rail-player-top.rp-frame .rp-emblem{
  /* Make room for the SVG RankIcon — the old ◆ glyph was 18px text,
     the icon is 22-28px square at full rail, scales down at narrow. */
  display: inline-flex; align-items: center; justify-content: center;
  width: clamp(22px, 15cqw, 28px); height: clamp(22px, 15cqw, 28px);
  font-size: 0;     /* hide any text fallback so size stays consistent */
  flex-shrink: 0;
}
.rail-player-top.rp-frame .rp-emblem svg{
  display: block;
  width: clamp(18px, 12cqw, 22px); height: auto;
}
/* XP bar — real progress track. Width animates so live xp updates
   SLIDE instead of jumping. Height + margin scale with rail width. */
.rail-player-top.rp-frame .bar{
  height: clamp(4px, 3.2cqw, 6px);
  margin-top: clamp(8px, 6.5cqw, 12px);
  background: rgba(255,255,255,.07);
  border-radius: 999px;
  overflow: hidden;
  position: relative;
}
.rail-player-top.rp-frame .bar > span{
  display: block; height: 100%;
  border-radius: 999px;
  transition: width .35s cubic-bezier(.6,.1,.2,1);
  box-shadow: 0 0 8px currentColor;
}
.rail-player-top.rp-frame .xp{
  display: flex; justify-content: space-between;
  gap: 6px;
  margin-top: clamp(4px, 3.2cqw, 6px);
  font-family: var(--font-display); font-size: clamp(8.5px, 5.4cqw, 10px);
  letter-spacing: .08em;
  color: var(--text-mute); text-transform: uppercase;
  /* Allow each span to truncate so neither overflows */
  min-width: 0;
}
.rail-player-top.rp-frame .xp > span{
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  /* Right span (next-tier name) takes natural width; left (XP) flexes */
  min-width: 0;
}
.rail-player-top.rp-frame .xp > span:last-child{
  flex-shrink: 0;
  text-overflow: clip;   /* keep the % readable; truncate left span first */
}
/* Guest sign-in CTA — sized to match the box's adaptive padding so it
   looks integrated, not bolted on. Amber on dark per the site CTA style. */
.rail-player-top.rp-frame .rp-cta{
  appearance: none; cursor: pointer; font-weight: 800;
  display: block; width: 100%;
  margin-top: clamp(8px, 5cqw, 12px);
  padding: clamp(7px, 4cqw, 9px) 10px;
  background: var(--accent);
  color: var(--accent-ink);
  border: 0; border-radius: 8px;
  font-family: var(--font-display);
  font-size: clamp(10px, 6cqw, 12px);
  font-weight: 700; letter-spacing: .12em;
  transition: filter .15s ease, transform .12s ease;
}
.rail-player-top.rp-frame .rp-cta:hover{ filter: brightness(1.08); transform: translateY(-1px); }
.rail-player-top.rp-frame .rp-cta:active{ transform: translateY(0); }
.rail-player .rp-top{
  display: grid; grid-template-columns: 38px 1fr auto;
  gap: var(--space-4); align-items: center; position: relative; z-index: 2;
}
.rail-player .rp-avatar{
  width: 38px; height: 38px; border-radius: var(--radius-md);
  background: linear-gradient(135deg, var(--accent), #2487AD);
  display: grid; place-items: center;
  color: var(--accent-ink); font-family: var(--font-display); font-size: 16px;
  box-shadow: 0 0 0 2px rgba(61, 126, 255,.3);
}
.rail-player .rp-user{ font-weight: 700; font-size: 13px; color: var(--text); }
.rail-player .rp-tier{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .1em;
  color: var(--purple); text-transform: uppercase; margin-top: 2px;
}
.rail-player .rp-emblem{ color: var(--purple); font-size: 18px; }

.rail-collapsed .rail-player-top .rp-user,
.rail-collapsed .rail-player-top .rp-tier,
.rail-collapsed .rail-player-top .rp-emblem,
.rail-collapsed .rail-player-top .bar,
.rail-collapsed .rail-player-top .xp{ display:none; }
.rail-collapsed .rail-player-top{ padding: var(--space-3); }
.rail-collapsed .rail-player-top .rp-top{ grid-template-columns: 1fr; justify-items: center; }

.rail-mode{
  display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--space-1); padding: var(--space-1);
  background: rgba(0,0,0,.3);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-md);
  margin: 0 4px 10px;
}
.rail-mode button{
  appearance:none; border:0; cursor:pointer; font-weight: 700;
  background: transparent; color: var(--text-dim);
  font-family: var(--font-display); font-size: 11px; letter-spacing: .08em;
  padding: var(--space-3); border-radius: var(--radius-sm);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  transition: .15s;
}
.rail-mode button.on{
  background: var(--accent); color: var(--accent-ink);
  box-shadow: 0 0 14px rgba(52,192,235,.3);
}
.rail-mode button:not(.on):hover{ color: var(--text); }
.rail-collapsed .rail-mode{ display: none; }

/* Search wrapper holds the input + suggestion popup. position:relative
   anchors the absolute-positioned .rail-search-pop. */
.rail-search-wrap{
  position: relative;
  margin: 0 4px 14px;
}
.rail-search{
  display: flex; align-items: center; gap: var(--space-3);
  padding: 0 12px;
  height: 38px;
  background: rgba(255,255,255,.03);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-md);
  color: var(--text-dim);
  flex-wrap: nowrap;          /* defensive — never let kbd wrap to row 2 */
  transition: border-color .15s, box-shadow .15s;
}
/* Operator feedback — removed the gold-halo box-shadow on focus; the
   border-color shift alone is enough to signal focus and reads cleaner. */
.rail-search:focus-within{
  border-color: rgba(52,192,235,.45);
}
.rail-search input{
  flex: 1 1 0; min-width: 0;
  background: transparent; border: 0; outline: 0;
  color: var(--text); font: inherit; font-size: 13px;
  appearance: none; -webkit-appearance: none;
}
.rail-search input::-webkit-search-cancel-button,
.rail-search input::-webkit-search-decoration{ display: none; }
.rail-search input::-ms-clear,
.rail-search input::-ms-reveal{ display: none; }
.rail-search .kbd{
  flex-shrink: 0;
  font-family: var(--font-display); font-size: 10px;
  padding: 2px 5px; border-radius: var(--radius-xs);
  background: rgba(255,255,255,.05); color: var(--text-mute);
  border: 1px solid var(--line-soft);
}
.rail-collapsed .rail-search,
.rail-collapsed .rail-search-wrap{ display: none; }

/* Suggestion popup under the search bar. Anchored to .rail-search-wrap. */
.rail-search-pop{
  position: absolute;
  left: 0; right: 0;
  top: calc(100% + 4px);
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: 0 18px 40px rgba(0,0,0,.45);
  padding: var(--space-1);
  z-index: 30;
  display: flex; flex-direction: column; gap: 2px;
  max-height: 360px; overflow-y: auto;
}
.rail-search-pop button{
  display: flex; align-items: center; gap: var(--space-4);
  padding: 8px 10px; border-radius: var(--radius-sm);
  background: transparent; border: 0; cursor: pointer;
  /* Task #18 — `font-weight: 700` AFTER `font: inherit` so the
     shorthand can't strip it back to the body's 500 baseline. */
  color: var(--text); font: inherit; font-weight: 700; font-size: 13px;
  text-align: left;
  transition: background .12s, color .12s;
}
.rail-search-pop button:hover{
  background: var(--accent-10);
  color: var(--accent);
}
.rail-search-pop .gname{
  font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail-search-pop .gprov{
  color: var(--text-mute); font-size: 11px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Mobile (≤ 860 px): hide the Cmd-K hint so the input never wraps. The
   topbar uses the same pattern at .topbar .search .kbd. */
@media (max-width: 860px){
  .rail-search .kbd{ display: none; }
  .rail-search{ height: 40px; }
  .rail-search input{ font-size: 14px; }
}

/* ============ TOPBAR — balance dropdown ============ */
.bal-group{
  display: inline-flex; gap: var(--space-3); position: relative;
  font-family: var(--font-display);
}
.bal-btn{
  appearance: none; cursor: pointer;
  background: rgba(13,20,18,.85);
  border: 1px solid var(--line-soft);
  color: var(--text);
  padding: 8px 12px;
  border-radius: var(--radius-md);
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-display); font-weight: 600; font-size: 13px;
  transition: .15s;
}
.bal-btn:hover, .bal-btn.on{ border-color: rgba(61, 126, 255,.45); }
.bal-btn .bal-coin{
  width: 20px; height: 20px; border-radius: 50%;
  background: linear-gradient(135deg, #6a9cff, #2775ca);
  color: #fff;
  display: grid; place-items: center;
  font-size: 11px; font-weight: 800;
  box-shadow: 0 0 0 2px rgba(39,117,202,.25);
}

/* Balance flash animations: red on debit (bet placed), green on credit
   (win/deposit). The class is added by topbar.jsx whenever the balance
   for the selected currency changes; auto-clears after the keyframe. */
@keyframes balFlashDown {
  0%   { color: var(--red, #f24d5c); transform: translateY(2px); }
  60%  { color: var(--red, #f24d5c); transform: translateY(0); }
  100% { color: var(--text); transform: translateY(0); }
}
@keyframes balFlashUp {
  0%   { color: var(--accent, #ffb72b); transform: translateY(-2px); }
  60%  { color: var(--accent, #ffb72b); transform: translateY(0); }
  100% { color: var(--text); transform: translateY(0); }
}
.bal-btn.bal-flash-down .bal-val { animation: balFlashDown .9s ease-out both; }
.bal-btn.bal-flash-up   .bal-val { animation: balFlashUp   .9s ease-out both; }
.bal-btn.bal-flash-down { box-shadow: 0 0 0 1px rgba(242,77,92,.45), 0 0 14px rgba(242,77,92,.18); }
.bal-btn.bal-flash-up   { box-shadow: 0 0 0 1px rgba(52,192,235,.55), 0 0 14px rgba(52,192,235,.22); }

/* Duplicate .wallet-btn override — kept for specificity.
   Now matches the canonical amber gradient CTA. */
.wallet-btn{
  appearance: none; border: 0; cursor: pointer;
  background: linear-gradient(180deg, var(--accent-bright), var(--accent));
  color: var(--on-accent);
  font-family: var(--font-display); font-weight: 800; font-size: 13px; letter-spacing: .06em;
  padding: 10px 16px;
  --c: 8px;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)), calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  box-shadow: 0 6px 22px var(--accent-glow);
  transition: transform .12s, box-shadow .2s, filter .15s;
}
.wallet-btn:hover{ transform: translateY(-1px); box-shadow: 0 10px 28px var(--accent-glow); filter: brightness(1.04); }

.tb-login{
  appearance: none; cursor: pointer;
  background: transparent; border: 1px solid var(--line-soft);
  color: var(--text); font-family: var(--font-display); font-weight: 700;
  font-size: 12px; letter-spacing: .08em;
  padding: 9px 18px; border-radius: var(--radius-pill, 999px);
  transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.tb-login:hover{ border-color: var(--accent); color: var(--accent); }
/* Operator feedback — Sign Up mirrors the Log In pill shape exactly
   (same height, padding, radius, font-weight) and uses the brand
   gold (var(--accent) #34c0eb) as a filled background — same yellow
   the rest of the site uses for primary CTAs (Place Bet / View Race
   / etc.). The previous chamfered purple block + orange wash are
   gone. */
.tb-signup{
  appearance: none; cursor: pointer;
  background: var(--accent);
  color: var(--accent-contrast, var(--accent-ink, #0a0a14));
  border: 1px solid var(--accent);
  font-family: var(--font-display); font-weight: 700; font-size: 12px; letter-spacing: .08em;
  padding: 9px 18px; border-radius: var(--radius-pill, 999px);
  box-shadow: 0 4px 14px rgba(52, 192, 235, .25);
  transition: filter .15s ease, transform .12s ease, box-shadow .2s ease;
  clip-path: none;
}
.tb-signup:hover{
  filter: brightness(1.06);
  transform: translateY(-1px);
  box-shadow: 0 8px 22px rgba(52, 192, 235, .4);
}
.tb-signup:active{ transform: translateY(0); filter: brightness(.96); }

.bal-dropdown{
  position: absolute; top: calc(100% + 8px); left: 0;
  z-index: 200;
  width: 300px;
  background: var(--bg-2);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  box-shadow: 0 24px 80px rgba(0,0,0,.7), 0 0 0 1px rgba(148,163,209,.10);
  padding: var(--space-4);
  animation: modalIn .18s ease-out;
}
.bd-search{
  display: flex; align-items: center; gap: var(--space-3);
  padding: 10px 12px;
  background: rgba(255,255,255,.03);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-md);
  color: var(--text-dim);
  margin-bottom: 8px;
}
.bd-search input{
  flex: 1; min-width: 0;
  background: transparent; border: 0; outline: 0;
  color: var(--text); font: inherit; font-size: 13px;
}
.bd-list{ display: flex; flex-direction: column; max-height: 300px; overflow-y: auto; }
.bd-row{
  appearance: none; border: 0; cursor: pointer;
  background: transparent; color: var(--text);
  display: grid; grid-template-columns: 26px 1fr auto;
  gap: var(--space-4); align-items: center;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  font: inherit; text-align: left;
}
.bd-row:hover{ background: var(--white-04); }
.bd-row.on{ background: var(--accent-15); }
.bd-coin{
  width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center;
  color: #fff; font-weight: 800; font-size: 11px;
}
.bd-sym{ font-family: var(--font-ui); font-weight: 700; font-size: 13px; }
.bd-usd{ font-family: var(--font-display); font-size: 13px; color: var(--text); font-weight: 600; }

.bd-opts{
  margin-top: 6px; padding-top: 10px;
  border-top: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: var(--space-1);
}
.bd-opts label{
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 10px; font-size: 13px; color: var(--text); cursor: pointer;
}
.bd-opts .sw{
  width: 30px; height: 18px; border-radius: var(--radius-pill);
  background: rgba(255,255,255,.08);
  position: relative; transition: .2s;
}
.bd-opts .sw::before{
  content:""; position: absolute; top: 2px; left: 2px;
  width: 14px; height: 14px; border-radius: 50%;
  background: #fff; transition: .2s;
}
.bd-opts input:checked + .sw{ background: var(--purple); }
.bd-opts input:checked + .sw::before{ transform: translateX(12px); }
.bd-opts input{ display: none; }

/* ============ PLAYER MODAL ============ */
.player-modal{
  display: grid;
  grid-template-columns: 1fr;
  background: linear-gradient(180deg, #0D1333 0%, #04060E 60%);
}
.pm-hero{
  position: relative;
  padding: 28px 32px 24px;
  background:
    radial-gradient(70% 120% at 90% 0%, rgba(61, 126, 255,.3), transparent 60%),
    linear-gradient(180deg, #170a24, #080410);
  border-bottom: 1px solid rgba(61, 126, 255,.18);
  display: grid; grid-template-columns: auto 1fr auto;
  gap: 18px; align-items: center;
}
.pm-avatar{
  width: 76px; height: 76px; border-radius: var(--radius-lg);
  display: grid; place-items: center;
  color: #0b0415; font-family: var(--font-display); font-size: 32px;
  box-shadow: 0 0 0 2px rgba(61, 126, 255,.4), 0 10px 30px rgba(61, 126, 255,.25);
}
.pm-head .pm-name{
  font-family: var(--font-display); font-size: 26px;
  letter-spacing: -.01em; text-transform: uppercase;
  margin: 0 0 4px;
}
.pm-head .pm-meta{
  display: flex; gap: var(--space-3); flex-wrap: wrap; align-items: center;
  font-family: var(--font-display); font-size: 11px; color: var(--text-dim);
}
.pm-head .pm-tier{
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 3px 8px; border-radius: var(--radius-xs);
  background: var(--accent-15); color: var(--purple);
  text-transform: uppercase; letter-spacing: .1em; font-size: 10px;
}
.pm-head .pm-online::before{
  content:""; width:6px; height:6px; border-radius:50%;
  background: var(--accent); display: inline-block; margin-right: 6px;
  box-shadow: 0 0 8px var(--accent);
}
.pm-actions{ display: flex; gap: var(--space-3); }
.pm-ghost, .pm-primary{
  appearance: none; cursor: pointer;
  font-family: var(--font-display); font-weight: 700; font-size: 12px; letter-spacing: .08em;
  padding: 10px 14px; border-radius: var(--radius-md);
  display: inline-flex; align-items: center; gap: var(--space-2);
}
.pm-ghost{
  background: transparent; color: var(--text);
  border: 1px solid var(--line-soft);
}
.pm-ghost:hover{ border-color: var(--purple); color: var(--purple); }
.pm-primary{
  background: var(--purple); color: #fff; border: 0;
  box-shadow: 0 6px 22px rgba(61, 126, 255,.35);
}

.pm-stats{
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: var(--space-4);
  padding: 20px 32px;
  border-bottom: 1px solid var(--line-soft);
}
.pm-stat{
  padding: var(--space-6); background: var(--white-02);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
}
.pm-stat .k{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .15em;
  color: var(--text-mute); text-transform: uppercase;
}
.pm-stat .v{
  font-family: var(--font-display); font-size: 20px; margin-top: 4px;
}
.pm-stat .v em{ color: var(--accent); font-style: normal; }
.pm-stat .v.purple{ color: var(--purple); }

.pm-section{ padding: 20px 32px; }
.pm-section h4{
  font-family: var(--font-display); font-size: 12px; letter-spacing: .15em;
  color: var(--text-mute); margin: 0 0 12px; text-transform: uppercase;
}
.pm-row{
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  gap: var(--space-5); align-items: center;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  font-size: 13px;
}
.pm-row:nth-child(even){ background: var(--white-02); }
.pm-row .mono{ font-family: var(--font-display); }
.pm-row .g-chip{
  width: 26px; height: 26px; border-radius: var(--radius-xs);
  display: grid; place-items: center;
  font-family: var(--font-display); font-size: 11px;
}
.pm-row .mult{ font-family: var(--font-display); font-size: 12px; color: var(--gold); }
.pm-row .win{ font-family: var(--font-display); font-weight: 700; color: var(--green); }

/* tip form */
.pm-tip{
  padding: 20px 32px 28px;
  background: var(--white-04);
  border-top: 1px solid rgba(61, 126, 255,.15);
}
.pm-tip h4{ color: var(--purple); }
.pm-tip-row{
  display: grid; grid-template-columns: 140px 1fr auto;
  gap: var(--space-4);
}
.pm-tip-row select, .pm-tip-row input{
  background: rgba(0,0,0,.35);
  border: 1px solid var(--line-soft);
  color: var(--text);
  padding: 12px 14px;
  font: inherit; font-family: var(--font-display); font-size: 14px;
  border-radius: var(--radius-md);
  outline: 0;
}
.pm-tip-row input:focus, .pm-tip-row select:focus{
  border-color: rgba(61, 126, 255,.5);
}
.pm-tip-row .tip-send{
  appearance: none; border: 0; cursor: pointer;
  background: var(--purple); color: #fff;
  font-family: var(--font-display); font-weight: 800; font-size: 12px; letter-spacing: .1em;
  padding: 0 20px; border-radius: var(--radius-md);
  box-shadow: 0 6px 22px rgba(61, 126, 255,.4);
}
.pm-tip-quick{
  display: flex; gap: var(--space-2); margin-top: 10px; flex-wrap: wrap;
}
.pm-tip-quick button{
  appearance: none; cursor: pointer;
  background: var(--accent-soft);
  border: 1px solid rgba(61, 126, 255,.25);
  color: var(--purple);
  font-family: var(--font-display); font-size: 12px; font-weight: 600;
  padding: 6px 10px; border-radius: var(--radius-xs);
}
.pm-tip-quick button:hover{ background: var(--accent-25); }

@media (max-width: 720px){
  .pm-hero{ grid-template-columns: 1fr; text-align: center; padding: 24px 20px; }
  .pm-avatar{ margin: 0 auto; }
  .pm-actions{ justify-content: center; }
  .pm-head .pm-meta{ justify-content: center; }
  .pm-stats{ grid-template-columns: 1fr 1fr; padding: 16px 20px; }
  .pm-section, .pm-tip{ padding: 16px 20px; }
  .pm-tip-row{ grid-template-columns: 1fr; }
}

/* ============ MENTION POPUP ============ */
.mention-pop{
  position: absolute;
  bottom: 100%; left: 14px; right: 14px;
  margin-bottom: 8px;
  background: var(--bg-2);
  border: 1px solid rgba(61, 126, 255,.3);
  border-radius: var(--radius);
  box-shadow: 0 20px 40px rgba(0,0,0,.6);
  overflow: hidden;
  z-index: 10;
  animation: modalIn .18s ease-out;
}
.mention-pop .mp-head{
  padding: 8px 12px; font-family: var(--font-display);
  font-size: 10px; letter-spacing: .15em; color: var(--text-mute);
  text-transform: uppercase;
  border-bottom: 1px solid var(--line-soft);
}
.mp-item{
  display: grid; grid-template-columns: 24px 1fr auto;
  gap: var(--space-4); align-items: center;
  padding: 8px 12px; cursor: pointer;
}
.mp-item.active{ background: var(--accent-15); }
.mp-item .av{
  width: 22px; height: 22px; border-radius: 50%;
  display:grid; place-items:center;
  color: #1A1205; font-weight: 800; font-size: 11px;
}
.mp-item .u{ font-weight: 700; font-size: 13px; color: var(--text); }
.mp-item .u.vip{ color: var(--magenta); }
.mp-item .u.mod{ color: var(--gold); }
.mp-item .tag{
  font-family: var(--font-display); font-size: 10px; letter-spacing: .1em;
  color: var(--text-mute); text-transform: uppercase;
}

/* ============ GAME PAGE ============ */
.gp-shell{
  /* Was: max-width: 1040px (capped & wasted right-side space).
     Now: fill the available main column. Bet panel keeps a fixed width;
     canvas takes the remainder. Matches shuffle/stake pattern. */
  width: 100%;
  padding: 0;
}
.gp{
  display: grid;
  /* Operator rebuild (PDF page 7): originals layout flipped — bet panel
     on the LEFT, game canvas in the MIDDLE/RIGHT. Was `1fr <panel-w>`. */
  grid-template-columns: var(--gp-panel-w) minmax(0, 1fr);
  min-height: 540px;
}
@container main (max-width: 760px){
  .gp{ grid-template-columns: 1fr; }
  /* On mobile the panel comes BACK underneath the canvas (canvas paints
     first via DOM order). Flip with grid order so canvas stays first. */
  .gp-canvas { order: 1; }
  .gp-panel  { order: 2; border-left: 0; border-top: 1px solid var(--line-soft); }
}
.gp-canvas{
  position: relative;
  background: var(--bg-0);   /* flat dark — was a vertical gradient. The
                                 game canvas itself paints its own scene
                                 on top. */
  overflow: hidden;
  display: grid; place-items: center;
  padding: var(--space-9);
}
.gp-canvas canvas{
  display: block;
  width: 100%; height: 100%;
  /* Phase 24: was `max-width: 560px` which silently produced black bars
     on either side of the canvas at narrow widths. Letting it flex up to
     560px or 100% (whichever is smaller) keeps the canvas using all
     available space at intermediate viewports. */
  max-width: min(560px, 100%);
}
/* Plinko canvas sizes itself via inline style to stay perfectly responsive.
   We override the generic 100%/560px cap so the precomputed physics grid
   fills the available space at every breakpoint without being stretched.
   The wrapper carries no width clamp — it fills the .gp grid cell on the
   right, and the JS ResizeObserver feeds those dims into the layout math.
   Sideways margins inside the canvas come from `sideMargin = w * 0.06`
   in computePlinkoLayout, which gives the triangle the same proportional
   inset whether the card is 560 px or 1040 px wide. */
.plinko-canvas{
  padding: var(--space-2) !important;
  align-items: stretch;
  justify-items: stretch;
  /* `align-self: start` (not stretch) is required for the aspect-ratio
     below to take effect — under stretch, the grid forces both width and
     height and the ratio is ignored, which produced a too-wide, too-short
     board on widescreen. */
  align-self: start;
  min-height: 0;
  width: 100%;
  /* Reference shape from the design screenshot is roughly 7:5 — wider
     than tall. Pinning the wrapper's aspect-ratio gives the JS fit-math
     a stable target so the triangle sits with even top/bottom and
     left/right margins regardless of row count. */
  aspect-ratio: 7 / 5;
}
.plinko-canvas canvas{
  width: 100% !important;
  height: 100% !important;
  max-width: 100% !important;
  max-height: 100% !important;
}
/* New four-layer architecture — the canvas wrapper is a relative
   stacking context so .plinko-bg / .plinko-buckets / .plinko-balls /
   .plinko-fx can be absolute-positioned and overlap pixel-perfectly.
   Operator bug fix — buckets used to be baked into the bg canvas with
   a separate `.plinko-flash` overlay div animating on top when a ball
   landed. The overlay's inset highlights + drop shadow read as a
   duplicate raised tile appearing over the flat canvas-drawn cell.
   Buckets are now their own DOM layer so we animate the ACTUAL tile. */
.plinko-canvas { position: relative; }
/* Operator request — balls pass BEHIND the bucket row. Swapping the
   ball + bucket z-order means as a ball reaches the bucket band it
   visibly slides under the tile and disappears behind it, then the
   tile clicks. This sells the "ball captured by the tile" beat much
   better than the prior "ball lands on top" stacking, which left the
   ball briefly resting on the tile face. */
.plinko-bg     { z-index: 0; }
.plinko-balls  { z-index: 1; }
.plinko-buckets{ z-index: 2; }
.plinko-fx     { z-index: 3; }

/* Bucket tile — one per multiplier cell, positioned imperatively by
   the React render of bucketSpecs. Visual = flat fill + label, no
   inset highlights or drop shadow (those decorations were what made
   the previous press-overlay read as a different tile from the
   canvas-drawn bucket underneath; with buckets in DOM we don't need
   them). `transform-origin: 50% 100%` anchors the scaleY press at the
   bottom edge so it reads like a keyboard key being pushed in. */
.plinko-bucket {
  position: absolute;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: ui-monospace, monospace;
  font-weight: 700;
  line-height: 1;
  transform-origin: 50% 100%;
  will-change: transform, filter;
  pointer-events: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Ball element — sized + positioned imperatively by spawnBallDom.
   `will-change: transform` and `translate3d(..., 0)` (set inline)
   guarantee the browser promotes each ball to its own compositor
   layer, so element.animate() runs the keyframes on the GPU thread
   instead of the JS main thread. `contain: layout paint` short-
   circuits style/layout/paint propagation to ancestors when the
   ball moves. */
.plinko-ball{
  position: absolute;
  left: 0; top: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #DEF3FC 0%, #6FD4F2 55%, #34c0eb 100%);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .15);
  will-change: transform;
  contain: layout paint;
  pointer-events: none;
}
/* Puff overlay — instant click feedback at the canvas spawn point.
   Auto-removes via `animationend`. */
.plinko-puff{
  position: absolute;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 244, 204, .55);
  background: radial-gradient(circle, rgba(246, 213, 117, .35) 0%, rgba(246, 213, 117, 0) 70%);
  pointer-events: none;
  animation: plinko-puff-anim 350ms ease-out forwards;
  will-change: transform, opacity;
}
@keyframes plinko-puff-anim{
  0%   { opacity: .9; transform: scale(.4); }
  100% { opacity: 0;  transform: scale(1.4); }
}

@media (max-width: 860px){
  .plinko-canvas{
    align-self: stretch;
    min-height: 0 !important;
    /* Phone-portrait canvas: square aspect lets the 16-row triangle
       fill the card top-to-bottom without crushing the bucket pills
       at the bottom. With the tightened computePlinkoLayout margins
       (4% top / 10% bottom / 2% sides on phones), the triangle now
       occupies ~80% of the canvas instead of ~60%. */
    aspect-ratio: 1 / 1;
  }
}
.gp-panel{
  /* Operator rebuild — panel on the LEFT (`order:-1`), now a rounded
     standalone card with a small margin so it reads as its own surface
     rather than as a bisected half of the shell. */
  order: -1;
  padding: 16px 18px;
  background: var(--panel);
  border: 1px solid var(--border-default);
  border-radius: var(--radius);
  margin-right: var(--space-5);
  display: flex; flex-direction: column; gap: 10px;
  overflow-y: auto;
  overflow-x: hidden;
  min-width: 0;
}
.gp-canvas{
  /* Match the panel — separate rounded card on the right. */
  border-radius: var(--radius);
  border: 1px solid var(--border-subtle);
}
.gp-panel > *{ min-width: 0; }
.gp-panel .g-title{
  font-family: var(--font-display); font-size: 18px;
  text-transform: uppercase; letter-spacing: -.01em; margin: 0;
}
.gp-panel .g-sub{
  /* Operator rebuild (PDF page 7): bumped 11 → 13 so subtitles read
     at a glance from across the panel. */
  font-family: var(--font-display); font-size: 13px; letter-spacing: .1em;
  color: var(--text-dim);
}
.gp-field{ display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.gp-field label{
  /* Operator rebuild — switched from mono uppercase to mixed-case UI
     font (Degen reference). Plain "Bet Amount" / "Direction" reads
     softer; the right-aligned currency/value still surfaces the data. */
  font-family: var(--font-ui); font-size: 12px; letter-spacing: 0;
  text-transform: none; font-weight: 500;
  color: var(--text-secondary);
  display: flex; justify-content: space-between; align-items: baseline;
  min-width: 0;
}
.gp-field label > *:last-child {
  color: var(--text-tertiary); font-weight: 500;
  font-family: var(--font-display); font-variant-numeric: tabular-nums;
}
.gp-field label > span{
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Bet-amount input wrapper. Cleaner, less glowy: solid bg-2 fill, soft
   line border, on focus the border tightens to amber WITHOUT an outer
   ring. The inner <input> has every UA decoration (spinners, clear,
   reveal) explicitly suppressed so the visible "rectangle inside"
   artifact can't reappear regardless of the input's `type`. */
.gp-input{
  /* Operator rebuild — Degen-style clean input. Solid darker fill
     (no border by default), pads up bigger for tap, accent ring on
     focus only (no border swap which felt twitchy). */
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--bg-3);
  border: 0;
  border-radius: var(--radius-sm);
  padding: 4px 4px 4px 10px;
  min-width: 0;
  transition: box-shadow var(--dur-base) var(--ease-standard);
}
.gp-input-coin{
  width: 22px; height: 22px;
  display: grid; place-items: center;
  background: var(--accent);
  color: var(--accent-contrast);
  border-radius: 50%;
  font-family: var(--font-display);
  font-size: 12px; font-weight: 800;
  flex-shrink: 0;
}
.gp-input:focus-within{
  box-shadow: 0 0 0 2px var(--accent);
}
.gp-input input{
  flex: 1 1 0; min-width: 0;
  appearance: none; -webkit-appearance: none;
  background: transparent; border: 0; outline: 0; box-shadow: none;
  padding: 4px 0;
  color: var(--text); font: inherit; font-family: var(--font-display);
  font-size: 15px; font-weight: 700;
}
.gp-input input::-webkit-outer-spin-button,
.gp-input input::-webkit-inner-spin-button{ appearance: none; -webkit-appearance: none; margin: 0; }
.gp-input input::-webkit-search-cancel-button,
.gp-input input::-webkit-search-decoration{ display: none; }
.gp-input input::-ms-clear,
.gp-input input::-ms-reveal{ display: none; }
.gp-input input[type="number"]{ -moz-appearance: textfield; }
.gp-input .mini{
  appearance: none; cursor: pointer;
  background: var(--bg-elevated);
  border: 0;
  color: var(--text-primary);
  font-family: var(--font-ui); font-size: 12px; font-weight: 700;
  padding: 6px 10px; border-radius: var(--radius-sm);
  transition: color var(--dur-base) var(--ease-standard),
              background var(--dur-base) var(--ease-standard);
}
.gp-input .mini:hover{
  background: var(--accent);
  color: var(--accent-contrast);
}
.gp-row{ display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-4); min-width: 0; }
.gp-row > *{ min-width: 0; }
/* Phase 24: when the main column is narrow the bet panel reflows.
   Triggered by the parent .main container, not the .gp itself (a
   container can't query its own width). */
@container main (max-width: 980px){
  .gp-panel{ padding: 14px 14px; }
}
@container main (max-width: 820px){
  .gp-row{ grid-template-columns: 1fr; gap: var(--space-3); }
}

/* Chip toggle group (Risk / Rows / Drop count / Direction etc.).
   Operator rebuild — neutral dark fills, accent only on active. */
.gp-chips{ display: flex; gap: var(--space-2); flex-wrap: wrap; }
.gp-chips button{
  appearance: none; cursor: pointer;
  background: var(--bg-elevated);
  border: 0;
  color: var(--text-secondary);
  font-family: var(--font-ui); font-size: 12px; font-weight: 600;
  letter-spacing: 0;
  padding: 7px 11px; border-radius: var(--radius-sm);
  transition: background var(--dur-base) var(--ease-standard),
              color var(--dur-base) var(--ease-standard);
}
.gp-chips button:hover{
  color: var(--text-primary);
  background: var(--bg-3);
}
.gp-chips button.on{
  background: var(--accent);
  color: var(--accent-contrast);
}
.gp-chips button:disabled{
  opacity: .5; cursor: not-allowed;
}

/* Primary CTA — operator rebuild. Pill-shaped, solid brand gold,
   no chamfer or gradient (Degen-style cleanness). Bigger touch
   target. The only gold thing in the panel; pulls focus naturally. */
.gp-bet,
.gp-bet.purple{
  appearance: none; border: 0; cursor: pointer;
  background: var(--accent);
  color: var(--accent-contrast);
  font-family: var(--font-display); font-size: 14px; letter-spacing: .04em;
  font-weight: 800;
  padding: 12px var(--space-7);
  border-radius: var(--radius-pill);
  box-shadow: 0 4px 14px rgba(52, 192, 235, .22);
  margin-top: auto;
  transition: transform var(--dur-fast) var(--ease-standard),
              filter var(--dur-base) var(--ease-standard),
              box-shadow var(--dur-base) var(--ease-standard);
}
.gp-bet:hover,
.gp-bet.purple:hover{
  transform: translateY(-1px);
  filter: brightness(1.04);
  box-shadow: 0 8px 22px rgba(52, 192, 235, .35);
}
.gp-bet:active,
.gp-bet.purple:active{
  transform: translateY(0);
  filter: brightness(.96);
}
.gp-bet:disabled,
.gp-bet.purple:disabled{
  transform: none;
  filter: grayscale(.4) brightness(.7); cursor: not-allowed;
  box-shadow: none;
}

/* Inline result chip inside the .gp-bet button — replaces the old
   "WIN · +$X" / "LOSS · -$X" pop-text that used to sit below the
   button (customer ask). The button content uses flex so the action
   label stays centered while the result chip floats to the right. */
.gp-bet{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  position: relative;
}
.gp-bet .gp-bet-label{
  /* takes natural width */
}
.gp-bet .gp-bet-result{
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .04em;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(0,0,0,.22);
  white-space: nowrap;
  flex-shrink: 0;
}
/* Win chip — keeps the button gold, dark text reads on the chip */
.gp-bet .gp-bet-result.win{
  color: #0d3a1c;
  background: rgba(60,200,120,.92);
  box-shadow: 0 0 0 1px rgba(255,255,255,.15);
}
/* Loss chip — red on darker bg so it still pops on the gold button */
.gp-bet .gp-bet-result.loss{
  color: #fff;
  background: rgba(217,42,61,.92);
  box-shadow: 0 0 0 1px rgba(255,255,255,.15);
}
/* Subtle button-state tint when there's a result. Win = brighter glow.
   Loss = slight desaturation so the button reads "neutral but result
   acknowledged" instead of celebrating a loss. */
.gp-bet.is-win{
  box-shadow: 0 2px 6px rgba(0,0,0,.25), 0 0 22px rgba(60,200,120,.35);
}
.gp-bet.is-loss{
  filter: brightness(.92);
}

/* Mobile: stack the label + result chip vertically when the bet panel is
   narrow so neither truncates. Keeps the chip readable on phones. */
@media (max-width: 480px){
  .gp-bet{ flex-direction: column; gap: 4px; padding: 10px 14px; }
  .gp-bet .gp-bet-result{ font-size: 11px; padding: 3px 8px; }
}
/* Operator rebuild — neutral .gp-stat (Degen reference). The "value"
   text is now white (not gold) so the panel reads calmer; gold stays
   reserved for the Place Bet CTA. Used inside the side panel and
   inside the new horizontal stat strips below the canvas. */
.gp-stat{
  display: flex; flex-direction: column;
  gap: var(--space-1);
  padding: 8px 10px;
  background: var(--bg-elevated);
  border: 0;
  border-radius: var(--radius-sm);
  min-width: 0;
}
.gp-stat .k{
  color: var(--text-tertiary);
  text-transform: none;
  letter-spacing: 0;
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 500;
}
.gp-stat .v{
  color: var(--text-primary);
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 13px;
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Variant: horizontal stat strip below the canvas (Dice / Plinko /
   etc.). 3 equal columns, single rounded panel. */
.gp-stat-strip{
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--bg-2);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  margin-top: var(--space-7);
}
.gp-stat-strip .gp-stat{
  background: var(--bg-3);
}
@container main (max-width: 760px){
  .gp-stat-strip{ grid-template-columns: 1fr; }
}

/* Editable stat-strip cells (Dice — Multiplier / Win Chance / Roll
   target). The number input fills the cell and shares the cell's font
   so it reads as text, not as a form control. Border + ring only on
   focus to flag editability. */
.gp-stat-strip-editable .gp-stat{
  cursor: text;
  transition: background var(--dur-base) var(--ease-standard);
}
.gp-stat-strip-editable .gp-stat:hover{
  background: var(--bg-elevated);
}
.gp-stat-strip-editable .gp-stat:focus-within{
  background: var(--bg-elevated);
  box-shadow: inset 0 0 0 2px var(--accent);
}
.gp-stat-edit{
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  width: 100%;
  min-width: 0;
}
.gp-stat-edit input{
  appearance: none; -webkit-appearance: none;
  background: transparent;
  border: 0;
  outline: 0;
  padding: 0;
  margin: 0;
  width: 100%;
  min-width: 0;
  color: var(--text-primary);
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 15px;
  text-overflow: ellipsis;
}
.gp-stat-edit input::-webkit-outer-spin-button,
.gp-stat-edit input::-webkit-inner-spin-button{
  appearance: none; -webkit-appearance: none; margin: 0;
}
.gp-stat-edit input[type="number"]{ -moz-appearance: textfield; }
.gp-stat-edit input:disabled{
  cursor: not-allowed;
  opacity: .6;
}
.gp-stat-suffix{
  flex-shrink: 0;
  color: var(--text-tertiary);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
}

@media (max-width: 860px){
  .gp{ grid-template-columns: 1fr; min-height: 0; }
  .gp-canvas{ min-height: 320px; }
  .gp-panel{ border-left: 0; border-top: 1px solid var(--line-soft); }

  /* Phase 24+ — on mobile, hoist the PLAY button to the TOP of every
     original game's bet panel (above Manual/Auto tabs, BET AMOUNT, and
     direction toggles). The .gp-panel is `display: flex; flex-direction:
     column` so flex `order` reorders without touching JSX. Negative
     order moves an item ahead of all default-order siblings.
     Affects: ROLL DICE / DRAW / DROP BALL / Bet (Blackjack) / BET (Limbo)
     / SPIN / START / CASH OUT (Mines+Crash) / PLACE BET (Crash). */
  .gp-panel .gp-bet{
    order: -1;
    /* margin-top: auto is the default rule used to push the button to the
       BOTTOM of a tall panel. Override here so it sits flush at the TOP. */
    margin-top: 0;
    margin-bottom: 4px;
  }
  /* Operator bug fix — Crash per-bet Cash Out rows must follow the
     hoisted Start/Stop Autobet + Stop & Cashout action buttons, not
     land at the bottom of the panel past all the bet inputs. The
     `.gp-bet` rule above only catches direct children of `.gp-panel`,
     so the Stop Autobet + Stop & Cashout pair gets `order:-1` but
     `.crash-v2-mybets` (which holds the per-bet Cash Out rows) stays
     at the default order 0 and ends up far below the bet inputs.
     Bumping it into the same `order:-1` bucket pulls it up alongside
     the action buttons; JSX order within the bucket places it
     immediately after them. Net mobile sequence:
       Stop Autobet → Stop & Cashout → per-bet Cash Out rows → bet inputs. */
  .gp-panel .crash-v2-mybets{
    order: -1;
    margin-top: 0;
  }
}


/* ============ MOBILE CATEGORY PILLS ============ */
.mobile-cats{ display: none; }
@media (max-width: 860px){
  .mobile-cats{
    display: flex; gap: var(--space-2);
    overflow-x: auto;
    scrollbar-width: none;
    padding: 10px 14px 4px;
    margin: 0 -14px 14px;
    background: linear-gradient(180deg, var(--bg-2), transparent);
    border-bottom: 1px solid var(--line-soft);
  }
  .mobile-cats::-webkit-scrollbar{ display: none; }
  .mobile-cats .cat{
    appearance: none; cursor: pointer;
    background: transparent;
    border: 0;
    color: var(--text-dim);
    font-family: var(--font-ui); font-weight: 600; font-size: 13px;
    padding: 8px 12px;
    border-radius: var(--radius-md);
    display: inline-flex; align-items: center; gap: var(--space-2);
    white-space: nowrap;
    flex-shrink: 0;
  }
  .mobile-cats .cat.on{
    background: var(--white-06);
    color: var(--text);
    border: 1px solid var(--line);
  }
}

/* ============ MOBILE BOTTOM NAV ============ */
.bottom-nav{
  display: none;
}
@media (max-width: 860px){
  .bottom-nav{
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    position: fixed; bottom: 0; left: 0; right: 0;
    height: 64px;
    z-index: 45;
    background: rgba(8,12,10,.92);
    backdrop-filter: blur(14px);
    border-top: 1px solid var(--line-soft);
    padding-bottom: env(safe-area-inset-bottom);
  }
  .bn-item{
    position: relative;
    appearance: none; cursor: pointer;
    background: transparent; border: 0;
    color: var(--text-tertiary);
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: var(--space-1);
    font-family: var(--font-ui); font-size: 11px; font-weight: 600;
    letter-spacing: .04em;
    transition: color var(--dur-base) var(--ease-standard),
                transform var(--dur-fast) var(--ease-standard);
  }
  .bn-item svg{ width: 22px; height: 22px; }
  .bn-item.on{ color: var(--accent); }
  /* Operator rebuild — active tab now has a brand indicator at the top
     edge (Stake-style) so the active state reads at a glance even when
     the icon color cue is missed. */
  .bn-item.on::before{
    content: '';
    position: absolute; top: 0; left: 50%;
    transform: translateX(-50%);
    width: 24px; height: 3px;
    background: var(--accent);
    border-radius: 0 0 var(--radius-xs) var(--radius-xs);
    box-shadow: 0 4px 12px var(--accent-glow);
  }
  .bn-item:active{ transform: scale(.95); }
  .bn-item:focus-visible{
    outline: 2px solid var(--accent);
    outline-offset: -4px;
    border-radius: var(--radius-sm);
  }
  /* leave space for bottom nav */
  .main{ padding-bottom: 80px; }
}

/* ============ MOBILE LAYOUT FIXES ============ */
@media (max-width: 860px){
  /* Topbar — center balance, compact.
     Task #200: this rule wins over the earlier mobile override via
     `!important`, so the safe-area-inset-top must be re-applied here
     too. Without it the iPhone notch / status bar clips the topbar
     in standalone PWA mode and at the top of the page in Safari. */
  .topbar{
    padding:
      env(safe-area-inset-top, 0px)
      max(12px, env(safe-area-inset-right, 0px))
      0
      max(12px, env(safe-area-inset-left, 0px))
      !important;
    gap: var(--space-3) !important;
    justify-content: space-between;
  }
  .topbar .mobile-brand .logo-mark{
    width: 36px; height: 36px; font-size: 18px;
    box-shadow: 0 0 20px rgba(52,192,235,.35);
  }
  .topbar .bal-group{
    gap: var(--space-2);
    margin-left: auto; margin-right: auto;
    position: relative;
  }
  .topbar .bal-btn{
    padding: 8px 10px;
    font-size: 13px;
    background: rgba(20,30,26,.85);
  }
  .topbar .wallet-btn{
    padding: 10px 12px;
    font-size: 0;
    min-width: 44px;
  }
  .topbar .wallet-btn svg{ width: 16px; height: 16px; }
  .topbar .wallet-btn::after{ content:""; }
  .topbar .tb-login.hide-mobile,
  .topbar .tb-signup.hide-mobile{ display: none; }
  .topbar .avatar{
    width: 36px; height: 36px; font-size: 14px;
    flex-shrink: 0;
  }
  .topbar .bal-dropdown{
    right: 0; left: auto;
    width: min(88vw, 320px);
  }

  /* Main column — tighter gutters */
  .main{
    padding: 14px 14px 80px !important;
    overflow-x: hidden;
  }

  /* Hero → 2-up promo banners — matches shuffle.com mobile reference.
     Cards are a fixed aspect ratio so title + tag + blurb always fit
     at any viewport width from 320px up.  Background artwork is
     rendered by <HeroCardArt> (top-right, scaled up for presence). */
  .hero{
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    gap: var(--space-3) !important;
    margin: 0 !important;
    min-height: auto !important;
  }
  .hero-side{
    display: contents !important;
  }
  .hero-main{
    display: none !important;
  }
  .hero-card{
    aspect-ratio: 1.25 / 1;         /* slightly taller than wide — better type fit */
    padding: var(--space-6) !important;
    min-height: 0 !important;
    overflow: hidden;
    position: relative;
    display: flex !important;
    flex-direction: column;
    justify-content: space-between;
  }
  .hero-card .tag{
    font-size: 9px !important;
    padding: 3px 7px !important;
    align-self: flex-start;
  }
  .hero-card h4{
    font-size: 16px !important;
    line-height: 1 !important;
    letter-spacing: -.02em !important;
    margin: 8px 0 4px !important;
  }
  .hero-card p{
    font-size: 11px !important;
    line-height: 1.3 !important;
    margin: 0 !important;
    color: var(--text-dim);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .hero-card .h-link{ display: none !important; }
  /* Beef up the background art so the cards feel premium, not empty. */
  .hero-card > div[style*="position:absolute"],
  .hero-card > div[style*="position: absolute"]{
    width: 180px !important; height: 180px !important;
    right: -30px !important; top: -20px !important;
    opacity: .45 !important;
  }

  /* Ultra-narrow safety: stack if viewport can't fit two cards readably. */
  @media (max-width: 340px){
    .hero{ grid-template-columns: 1fr !important; }
    .hero-card{ aspect-ratio: 2.2 / 1; }
  }

  /* Section heads */
  .sec-head{
    margin: 20px 0 10px !important;
    padding: 0 !important;
    align-items: center;
  }
  .sec-head h2{
    font-size: 20px !important;
    line-height: 1 !important;
    margin: 0 !important;
  }
  .sec-head .kicker{
    font-size: 10px !important;
  }
  /* Mode row — 2-up grid on mobile, aspect-driven height */
  .modes{
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    grid-template-rows: auto !important;
    gap: var(--space-4) !important;
  }
  .mode{
    aspect-ratio: 1.3 / 1 !important;
    min-height: 0 !important;
    min-width: 0 !important;
    width: 100% !important;
  }
  .mode.slots{
    grid-row: span 1 !important;
    grid-column: span 2 !important;
    aspect-ratio: 2.6 / 1 !important;
  }
  .mode .m-title{ font-size: 16px !important; }

  /* Game tiles smaller */
  .scroller{
    gap: var(--space-4) !important;
    /* Phase 27 hover-clip fix — was `2px 0 10px`; bumped top from 2px →
       8px so the `.game-tile:hover` translateY(-3px) lift remains visible
       on touch / narrow-viewport renders too. */
    padding: 8px 0 10px !important;
    -webkit-overflow-scrolling: touch;
  }
  .game-tile{ width: 140px !important; }
  .scroller.tall .game-tile{ width: 160px !important; }

  /* Hide scroller arrows on mobile (swipe instead) */
  .scroll-arrows{ display: none !important; }

  /* Phase 24: was `display: none` on mobile — but Phase 23 added the
     My Bets tab and explicit "live feed on every page" requirement. The
     container queries on .activity-table now drop the multiplier (and
     optionally player) column when narrow, keeping the feed compact but
     visible on mobile. Card padding stays tight via the existing rule
     at line ~1487. */
}

@media (max-width: 480px){
  /* Task #200: keep env(safe-area-inset-top) on small phones too —
     this is the breakpoint that matches every modern iPhone in
     portrait, which is exactly where the notch / Dynamic Island
     clipping reproduces. Without this re-inclusion the previous two
     mobile rules' safe-area padding gets reset to `0 10px`. */
  .topbar{
    padding:
      env(safe-area-inset-top, 0px)
      max(10px, env(safe-area-inset-right, 0px))
      0
      max(10px, env(safe-area-inset-left, 0px))
      !important;
    gap: var(--space-2) !important;
  }
  .topbar .bal-btn{ padding: 7px 9px; font-size: 12px; }
  .topbar .bal-btn .bal-coin{ width: 18px; height: 18px; font-size: 10px; }
  .topbar .mobile-brand .logo-mark{ width: 32px; height: 32px; font-size: 16px; }
  .topbar .avatar{ width: 32px; height: 32px; font-size: 13px; }
  .main{ padding: 12px 10px 80px !important; }
  .game-tile{ width: 124px !important; }
  .scroller.tall .game-tile{ width: 140px !important; }
  .hero-card h4{ font-size: 16px !important; }
  .sec-head h2{ font-size: 18px !important; }

  /* Bottom-nav slightly smaller */
  :root{ --bottom-nav-h: 58px; }
  .bottom-nav{ height: 58px; }
  .bn-item{ font-size: 9px; }
  .bn-item svg{ width: 20px; height: 20px; }
}

/* Fix game pages on mobile */
@media (max-width: 860px){
  .gp-canvas{ padding: var(--space-5) !important; min-height: 280px !important; }
  .gp-panel{ padding: var(--space-7) !important; }
  .gp-panel .g-title{ font-size: 18px !important; }
}

/* ============ Phase 26.9 — casino-bold weight overrides ============
   Sora (display) and Onest (UI) replaced Inter as part of this refresh.
   Most existing display-tagged elements (brand mark, pill toggles,
   wallet button, hero stat values, breadcrumbs, sidebar nav, section
   headings, primary/ghost/danger buttons) inherited the family but no
   explicit weight, so they were rendering at the body-default 400 and
   looked thin even after the family swap. The :where(...) rule below
   defaults those sites to 700-800 so the casino-bold feel is real and
   not just a font family change. :where() has 0 specificity, so any
   selector that already declares an explicit `font-weight` (e.g.
   .sec-head h2 = 800) keeps winning. The mono block bumps amount-class
   render sites to 600/700 so digits read solid. The final small-text
   block clamps tiny mono captions back to 500 so they don't turn into
   blobs at 9-11px. */
:where(
  /* brand */
  .brand .logo, .brand .logo-mark,
  .mobile-brand, .mobile-brand .logo-mark,
  /* topbar / wallet / avatar */
  .pill-toggle, .pill-toggle button,
  .wallet-btn, .avatar,
  /* sidebar */
  .rail .section-label,
  .rail-player .label, .rail-player .rank,
  /* main section heading helpers + hero */
  .sec-head .kicker,
  .hero-eyebrow, .hero-title, .hero-stat .v,
  .hero-card h4, .hero-card .h-link, .hero-card .tag,
  /* primary controls */
  .btn-primary, .btn-ghost, .btn-danger,
  /* nav rows */
  .nav-item, .nav-item .n-badge,
  /* activity / live feed */
  .activity-head h3, .activity-tabs button,
  .activity-row.head, .activity-row .multiplier,
  /* lobby tiles */
  .game-tile .t-name, .mode .m-title, .mode .m-sub, .mode .m-count,
  /* jackpot / wager-race banner */
  .jackpot .j-label, .jackpot .j-amount,
  .wrb-kicker, .wrb-status, .wrb-name, .wrb-pool,
  /* game-page brand label inside accessory bar */
  .gp-bb-brand
){
  font-weight: 800;
}

/* Mono amount sites — bump to 600/700 so wallet balance, multipliers,
   win chance %, payout, USD pool figures, and IDs render as solid
   numerals instead of the spindly default. */
:where(
  .wallet, .wallet .bal,
  .activity-row .mono, .activity-row .win,
  .hero-stat .k, .jackpot .j-meta,
  .wrb-pool, .gp-quickstats
){
  font-weight: 600;
}

/* Small-text guard rail — keep tiny mono captions and 9-11px micro
   labels readable instead of letting them blob out at the new heavier
   default. Caps these specific spots back to 500/600. */
:where(
  .activity-row.head,
  .rail .section-label,
  .sec-head .kicker,
  .game-tile .t-provider, .game-tile .t-badge, .game-tile .t-players,
  .nav-item .n-badge,
  .pm-shuffle-recent-head,
  .lsw-grid .k, .lsw-big .k,
  .mode .m-sub
){
  font-weight: 600;
}

/* Inputs reset their font with the `font:` shorthand which wipes
   weight back to 400. Bring them up to the body baseline (500) so
   the new heavier UI font actually shows in form fields. */
.input,
.dw-input,
.gp-input,
.a-input,
.rl-bet-input{
  font-weight: 500;
}

/* ensure balance dropdown fits within mobile viewport */
@media (max-width: 560px){
  .topbar .bal-group{ margin-left: 0; margin-right: 0; }
}
