:root{
    /* Brand/state colors — fixed across themes, carry meaning rather than mood */
    --green:#1a6b3c;--felt:#217a47;--gold:#f5c518;--red:#c0392b;--blue:#2980b9;--success:#4ade80;
    /* Structural colors — these are what actually change between themes.
       This is the DEFAULT theme — the original dark green felt look. */
    --dark:#0d1f14;            /* legacy alias, kept for older inline refs */
    --bg:#0d1f14;              /* page background */
    --surface:#1a2e22;         /* card/panel background */
    --surface-alt:#0d2518;     /* secondary panel (info boxes, token ledger cards) */
    --surface-hover:#1d3a28;   /* hovered/active row background */
    --surface-deep:#0a1a10;    /* deepest recessed panel (collapsible headers, mock screens) */
    --border:#2d4a38;          /* subtle border */
    --border-strong:#3d6b4f;   /* input/stronger border */
    --text:#f0f0f0;            /* primary text */
    --text-soft:#d0f0e0;       /* secondary readable text (tooltips, labels) */
    --text-muted:#7a9e89;      /* muted text */
    --text-faint:#5a8a6a;      /* more muted (subtitles) */
    --text-dim:#3d5a45;        /* dimmest (ended-state dots etc) */
    --text-label:#aaa;         /* form labels, dark-btn text */
    --header-text:rgba(255,255,255,0.75);
    --on-accent:#0d1f14;       /* text color used ON TOP of --gold/--success backgrounds */
  }
  [data-theme="light"]{
    /* Revised (Ryan, 2026-07-09): previous version used a stark pure-white
       surface plus the DARK theme's neon gold/green/red accent colors
       (tuned for high contrast against near-black backgrounds) unchanged —
       on white those same colors wash out and are hard to read. Surface
       toned down slightly off pure white, and --gold/--success darkened
       here (same mechanism the midnight theme already uses to customize
       accents per-theme) so every element using var(--gold)/var(--success)
       for text automatically gets a readable value on this background.
       Follow-up (Ryan, 2026-07-10): that first pass went too dark/brown —
       especially noticeable on --gold text/backgrounds sitting on the
       header, which stays a fixed dark green in every theme (see
       header{background:var(--green)} below — --green is a brand color,
       not themed) rather than on the light surface this value was tuned
       for. Brightened back up to a richer amber that still reads fine on
       the light surface but no longer looks muddy/brown against the header
       or on gold-background buttons (Rejoin, admin Yes toggles, etc). */
    --gold:#c9960b;
    --success:#1f8a4c;
    /* Item (Ryan, 2026-07-10, follow-up): first pass only darkened --bg, but
       --surface (.card's background — see .card{background:var(--surface)})
       is what actually fills almost the entire screen on every page, and it
       was still nearly pure white (#fcfdfc), so the app still read as
       "white" overall. Darkened --surface itself this time, and brought
       --bg/--surface-alt/--surface-hover/--surface-deep/--border down together
       so the whole theme reads as one consistent light grey rather than a
       grey gutter behind near-white cards. */
    --dark:#e2e5e3;
    --bg:#e2e5e3;
    --surface:#eef1ef;
    --surface-alt:#e2e8e4;
    --surface-hover:#d5ded8;
    --surface-deep:#d8e0dc;
    --border:#c7d1cb;
    --border-strong:#a3b3aa;
    --text:#16261c;
    --text-soft:#2c4234;
    --text-muted:#5c7468;
    --text-faint:#6f8a7a;
    --text-dim:#9bb0a3;
    --text-label:#5c6b62;
    --header-text:rgba(13,31,20,0.8);
    --on-accent:#16261c;
  }
  [data-theme="midnight"]{
    --green:#2a4a8c;--felt:#34579e;--gold:#ffb347;--red:#e8584a;--blue:#5a8fd6;--success:#5fd99a;
    --dark:#0a0e1a;
    --bg:#0a0e1a;
    --surface:#141b2e;
    --surface-alt:#0f1424;
    --surface-hover:#1c2740;
    --surface-deep:#070b15;
    --border:#28324a;
    --border-strong:#374564;
    --text:#eef0f5;
    --text-soft:#cfd6ec;
    --text-muted:#8893b8;
    --text-faint:#6b76a0;
    --text-dim:#454f70;
    --text-label:#9098b8;
    --header-text:rgba(238,240,245,0.8);
    --on-accent:#0a0e1a;
  }
  /* Light theme (Ryan, 2026-07-09): a few spots pair gold TEXT with a
     hardcoded dark tinted background, designed to make bright gold pop
     against a dark theme. Now that --gold is a dark amber on light theme,
     those same dark backgrounds would swallow the text — give them a pale
     gold/green equivalent instead so the text stays legible. */
  [data-theme="light"] .ppbtn.selected{background:#f7ecc9;}
  [data-theme="light"] #turnBanner.my-turn #turnBannerBottom{background:#f7ecc9;}
  [data-theme="light"] .header-live-pill{background:#dcefe1;}
  *{box-sizing:border-box;margin:0;padding:0;}
  html{background:var(--dark);}
  body{background:var(--dark);color:var(--text);font-family:'Segoe UI',sans-serif;}
  header{background:var(--green);padding:10px 16px;display:flex;align-items:center;justify-content:space-between;border-bottom:3px solid var(--gold);gap:8px 12px;flex-wrap:wrap;position:sticky;top:0;z-index:100;}
  .header-logo{display:flex;align-items:center;gap:10px;text-decoration:none;flex-shrink:0;}
  .header-logo-img{width:36px;height:36px;flex-shrink:0;}
  .header-logo-textwrap{display:flex;flex-direction:column;line-height:1;}
  .header-logo-text{font-size:1.2rem;font-weight:900;color:var(--gold);letter-spacing:-0.5px;white-space:nowrap;}
  /* TRACKER stretches to span the exact rendered width of "Kellypool" above it.
     text-justify:inter-character (the previous approach) isn't reliably supported
     for Latin text in Chrome/WebKit, so it rendered narrower than "Kellypool"
     despite the stated intent — a flex row with space-between, one span per
     letter (see markup), guarantees the first and last letters sit flush with
     the edges and the rest distribute evenly between them, regardless of
     browser/font. */
  .header-logo-sub{display:flex;width:100%;justify-content:space-between;font-size:0.62rem;line-height:1.3;font-weight:800;color:#000;text-transform:uppercase;margin-top:1px;}
  /* header-mid is currently always rendered empty (updateHeader() clears it —
     the old "Playing as X" line is dead per the comment there), but it still
     used to carry flex:1, which claimed all the row's spare width and left
     header-right squeezed into whatever sliver was left. That's what caused
     the match-code pill, token pill, and account button to crowd/overlap on
     narrow phones once a display name or token count got long. Dropping
     flex:1 here gives that width back to header-right. If header-mid is ever
     reused for visible content again, flex:1 (or a fixed share) should come
     back deliberately rather than by default. */
  .header-mid{text-align:center;font-size:0.78rem;color:rgba(255,255,255,0.75);}
  .header-mid strong{color:var(--gold);}
  /* flex-wrap + flex-end: on a narrow phone with a long-ish match code, a
     3-digit token pill, and a 15-char display name all present at once, this
     row can legitimately run out of horizontal room. Wrapping lets the
     account button (or join-requests bell) drop to its own line below the
     pills instead of clipping or overlapping — same idea as the outer header
     row above. min-width:0 lets the flex children actually shrink/ellipsis
     instead of forcing the row wider than the viewport. */
  .header-right{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap;min-width:0;}
  /* ROOT CAUSE (2026-06-26, found after padding bumps had no visible effect):
     the match-code pill and token pill are both injected as siblings inside
     #headerRight (a single div), NOT directly inside .header-right itself.
     .header-right's gap only ever applied between #headerRight as a whole,
     #joinReqWrap, and #accountBtn — it never reached between the two pills,
     since #headerRight had no display:flex/gap of its own. That's why
     bumping .pill's padding only ever changed space INSIDE each pill and
     never touched the visible gap BETWEEN them. This rule is what actually
     creates breathing room between the pills. */
  #headerRight{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}
  .pill{background:var(--gold);color:var(--on-accent);font-weight:700;padding:5px 14px;border-radius:20px;font-size:0.95rem;white-space:nowrap;}
  /* Display name is capped at 15 chars server-side, but the 👤 prefix plus a
     full 15-char name plus emoji rendering width varies enough across devices
     that we still cap+ellipsis defensively here rather than trust char-count
     alone to guarantee it fits every screen. */
  .account-btn{background:rgba(255,255,255,0.12);border:1px solid rgba(255,255,255,0.2);color:white;padding:6px 14px;border-radius:20px;font-size:0.9rem;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:200px;}

  .page{display:none;max-width:600px;margin:0 auto;padding:16px;}
  .page.active{display:block;}
  .card{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:18px;margin-bottom:14px;}
  .card h2{color:var(--gold);margin-bottom:12px;font-size:1.05rem;}
  input,select{width:100%;padding:10px 14px;border-radius:8px;border:1px solid var(--border-strong);background:var(--bg);color:var(--text);font-size:1rem;margin-bottom:10px;}
  input:focus,select:focus{outline:none;border-color:var(--gold);}
  /* Bug #5 (Ryan, 2026-07-05): locked club-invite email field looked like a
     normal editable input (white text) even with readOnly set — make it
     visually match the other greyed-out helptip/disabled fields instead. */
  input[readonly]{background:var(--surface-alt);color:var(--text-muted);border-color:var(--border);cursor:not-allowed;}
  .btn{display:block;width:100%;padding:12px;border-radius:8px;border:none;font-size:1rem;font-weight:700;cursor:pointer;margin-bottom:8px;transition:all 0.15s;}
  .btn:active{transform:scale(0.97);}
  .btn-primary{background:var(--gold);color:var(--on-accent);}
  .btn-green{background:var(--felt);color:white;}
  .btn-red{background:var(--red);color:white;}
  .btn-blue{background:var(--blue);color:white;}
  .btn-dark{background:var(--surface);color:var(--text-label);border:1px solid var(--border-strong);}
  .btn:disabled{opacity:0.35;cursor:not-allowed;}
  .btn-sm{font-size:0.82rem;padding:7px 14px;width:auto;display:inline-block;margin-bottom:0;}
  label{display:block;font-size:0.85rem;color:var(--text-label);margin-bottom:4px;}
  .toggle-row{display:flex;align-items:center;gap:10px;margin-bottom:10px;}
  .toggle-row span{font-size:0.9rem;flex:1;}
  input[type=checkbox]{width:18px;height:18px;margin:0;}

  /* ── LOGO SVG ── */
  .home-logo{text-align:center;padding:10px 0 8px;}
  .home-logo svg{width:110px;height:110px;filter:drop-shadow(0 4px 16px rgba(0,0,0,0.5));}
  .home-logo h1{font-size:2rem;font-weight:900;color:var(--gold);letter-spacing:-1px;margin-top:4px;}
  .home-logo-tracker{font-size:1.35rem;font-weight:800;color:var(--red);text-transform:uppercase;letter-spacing:2.5px;vertical-align:middle;position:relative;top:-2px;}
  .home-logo .tagline{font-size:0.9rem;color:var(--text-muted);margin-top:4px;}
  .home-logo .subtitle{font-size:0.78rem;color:var(--text-faint);margin-top:6px;max-width:300px;margin-left:auto;margin-right:auto;line-height:1.5;}

  /* ── AUTH ── */
  .auth-tabs{display:flex;gap:4px;margin-bottom:14px;background:var(--bg);border-radius:10px;padding:4px;}
  .auth-tab{flex:1;padding:8px;text-align:center;border-radius:8px;cursor:pointer;font-size:0.9rem;font-weight:600;color:var(--text-label);border:none;background:transparent;}
  .auth-tab.active{background:var(--felt);color:white;}
  .btn-link{background:none;border:none;color:var(--gold);font-size:0.85rem;font-weight:600;cursor:pointer;padding:4px;margin:0;text-decoration:underline;}
  .pin-boxes{display:flex;gap:8px;justify-content:center;margin:12px 0;}
  .pin-boxes input{width:44px;height:52px;text-align:center;font-size:1.4rem;font-weight:700;padding:4px;margin:0;border-radius:8px;}
  .account-matches-list{list-style:none;}
  .account-matches-list li{display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--bg);border-radius:8px;margin-bottom:6px;cursor:pointer;}
  .account-matches-list li:hover{background:var(--surface);}
  .match-status-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;}
  .dot-lobby{background:var(--text-muted);}
  .dot-playing{background:var(--success);box-shadow:0 0 6px rgba(74,222,128,0.6);}
  .dot-ended{background:var(--text-dim);}

  /* ── POT ALERT ── */
  #potAlertOverlay{position:fixed;inset:0;z-index:300;display:flex;align-items:center;justify-content:center;padding:20px;pointer-events:none;}
  #potAlertOverlay.active{pointer-events:all;cursor:pointer;}
  #potAlertBox{background:var(--surface);border:3px solid var(--gold);border-radius:16px;padding:22px 28px;text-align:center;max-width:320px;width:100%;pointer-events:none;opacity:0;transform:scale(0.85);transition:opacity 0.22s,transform 0.22s;box-shadow:0 8px 40px rgba(0,0,0,0.6);}
  #potAlertOverlay.active #potAlertBox{pointer-events:all;}
  #potAlertBox.show{opacity:1;transform:scale(1);}
  .pa-title{font-size:1.25rem;font-weight:900;color:var(--gold);margin-bottom:6px;}
  .pa-line{font-size:0.92rem;color:var(--text-soft);margin-bottom:3px;}
  .pa-line strong{color:white;}

  /* ── WHO POTTED ── */
  .modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.75);z-index:200;display:flex;align-items:flex-end;justify-content:center;}
  /* Item 14 (Ryan, 2026-06-25): alertModal/confirmModal/numberPromptModal are
     generic utility popups called FROM other modals (e.g. confirmRenameGroup's
     alertModal() call while renameGroupModal is still open underneath it) — they
     need to always render on top of every feature-specific modal, not just
     whichever one happens to be later in the DOM. Every .modal-overlay shares
     the same z-index above, so two simultaneously-open modals stack purely by
     DOM order — alertModal is defined early in the markup (so plenty of other
     modals, like renameGroupModal, sit later and were rendering OVER it despite
     alertModal's own display:flex being set after). Bumping these three above
     the shared 200 makes them correctly float above ANY other modal regardless
     of markup position, matching what "this is an interrupt, acknowledge it"
     popups are supposed to do everywhere they're used, not just this one case. */
  #alertModal,#confirmModal,#numberPromptModal{z-index:300;}
  .modal-sheet{background:var(--surface);border-radius:16px 16px 0 0;border-top:3px solid var(--gold);padding:20px 20px 32px;width:100%;max-width:600px;}
  .modal-sheet h3{color:var(--gold);margin-bottom:4px;font-size:1.1rem;}
  /* Add Guest, reparented inline on a small club's Club screen (Ryan,
     2026-07-10) -- strip the modal-sheet's own card chrome (gold top border,
     padding, width cap/centering) so it reads as plain in-page content
     matching the Club Members header, not a floating modal card. Only
     applies while it's actually sitting in the inline slot; back in its
     real #addGuestModal overlay it keeps the normal modal-sheet look. */
  #groupInlineGuestSlot .modal-sheet{border-top:none;padding:0;max-width:none !important;margin:0 !important;background:transparent;}
  #groupInlineGuestSlot .modal-sheet h3{display:none;}
  #groupInlineGuestSlot #newGuestNameStatus{min-height:0 !important;margin-top:0 !important;}
  /* Bug fix (Ryan, 2026-07-13): <3-member Club Page inline Add Guest box had
     the "+ New Guest" button stacked on its own line below the explanation
     paragraph -- put them in the same row instead (list of any existing
     guests, if present, still gets its own full-width row below). Scoped to
     the inline slot only; the real #addGuestModal popup keeps its normal
     stacked layout. */
  #groupInlineGuestSlot .modal-sheet{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px;}
  #groupInlineGuestSlot .modal-sheet > p:first-of-type{flex:1;min-width:0;margin:0;}
  #groupInlineGuestSlot #addGuestList{width:100%;order:1;}
  #groupInlineGuestSlot #maxGuestsMsg{width:100%;order:1;}
  #groupInlineGuestSlot #newGuestFormRow{width:100%;order:2;}
  #groupInlineGuestSlot #newGuestNameStatus{width:100%;order:3;}
  #groupInlineGuestSlot #addGuestModalDoneBtn{width:100%;order:4;}
  /* Bug fix (Ryan, 2026-07-14): margin alone wasn't enough -- an empty
     addGuestList div still counted as its own line in the inline slot's
     flex-wrap layout, so its row-gap contributed blank space even with
     nothing visible inside it. display:none removes it from the flex
     layout entirely when there is nothing to show (harmless in the real
     modal too, where it was already invisible either way). */
  #addGuestList:empty{margin-bottom:0;display:none;}
  .modal-sub{color:var(--text-muted);font-size:0.85rem;margin-bottom:16px;}
  .player-pick-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:14px;}
  .ppbtn{padding:14px 10px;border-radius:10px;border:2px solid var(--border);background:var(--bg);color:var(--text);font-size:1rem;font-weight:700;cursor:pointer;text-align:center;transition:all 0.15s;}
  .ppbtn:active{transform:scale(0.95);}
  .ppbtn.selected{border-color:var(--gold);background:#2a1e00;color:var(--gold);}
  /* Item (Ryan, 2026-07-13): compact button-row variant for Account Settings
     (Theme/Date Format/Time Format) — same selected/active look as .ppbtn,
     just smaller so 3 fit alongside their label on one row on mobile. */
  .setting-btn{padding:7px 3px;border-radius:8px;border:2px solid var(--border);background:var(--bg);color:var(--text);font-size:0.72rem;font-weight:600;cursor:pointer;text-align:center;transition:all 0.15s;white-space:nowrap;}
  .setting-btn:active{transform:scale(0.95);}
  .setting-btn.selected{border-color:var(--gold);background:#2a1e00;color:var(--gold);}
  [data-theme="light"] .setting-btn.selected{background:#f7ecc9;}
  /* Theme buttons preview their OWN theme's colors via inline style (set in
     index.html), so this only handles shape/selected-border, not color. */
  .theme-swatch-btn{padding:7px 3px;border-radius:8px;border:2px solid transparent;font-size:0.72rem;font-weight:600;cursor:pointer;text-align:center;transition:all 0.15s;white-space:nowrap;}
  .theme-swatch-btn:active{transform:scale(0.95);}
  .theme-swatch-btn.selected{border-color:var(--gold);}

  /* ── INFO TOOLTIP ── */
  .info-btn{background:none;border:1px solid var(--border-strong);color:var(--text-muted);border-radius:50%;width:18px;height:18px;font-size:0.7rem;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;margin-left:6px;vertical-align:middle;flex-shrink:0;}
  .info-btn:hover,.info-btn.active{background:var(--border);color:white;}
  .info-tooltip{position:relative;background:var(--surface-alt);border:1px solid var(--gold);border-radius:10px;padding:12px 30px 12px 14px;font-size:0.82rem;color:var(--text-soft);line-height:1.6;margin-top:8px;display:none;}
  .info-tooltip.visible{display:block;}
  .info-tooltip-close{position:absolute;top:8px;right:8px;background:none;border:none;color:var(--text-muted);width:20px;height:20px;border-radius:50%;font-size:0.85rem;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;}
  .info-tooltip-close:hover{background:var(--border);color:white;}

  /* ── TURN BANNER ── */
  #turnBanner{border-radius:14px;margin-bottom:14px;border:3px solid transparent;overflow:hidden;}
  #turnBanner.my-turn{border-color:var(--gold);animation:glow 1.2s ease-in-out infinite alternate;}
  #turnBanner.other-turn{border-color:var(--border);animation:none;}
  #turnBanner.locked{border-color:var(--red);animation:none;}
  @keyframes glow{from{box-shadow:0 0 8px rgba(245,197,24,0.5);}to{box-shadow:0 0 28px rgba(245,197,24,0.95);}}
  /* ── Busy button + overlay (Ryan, 2026-07-09) — used while a button's
     request is in flight, e.g. Send Invite / Send Reset Link, so the click
     registers instantly and the person can't tap anything else mid-request.
     Bug #7 follow-up (Ryan, 2026-07-09): Cancel used to be a separate pill
     docked to the bottom of the screen, disconnected from the button that
     triggered it and easy to read as a stray toast. It's now built into the
     busy button itself — tapping the button while it's showing the
     spinner/label cancels the in-flight request, so there's only ever one
     control to look at. #busyOverlay stays as a full-screen transparent
     click-blocker for everything else on the page; .btn-busy-cancel-active
     lifts the busy button's own stacking order above it (z-index) so clicks
     still land on the button instead of being swallowed by the overlay. */
  #busyOverlay{position:fixed;inset:0;z-index:9999;background:transparent;cursor:wait;}
  .btn-busy-cancel-active{position:relative;z-index:10000;cursor:pointer!important;}
  .btn-cancel-hint{opacity:0.85;font-size:0.8em;margin-left:6px;text-decoration:underline;text-underline-offset:2px;}
  .btn-spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(255,255,255,0.4);border-top-color:#fff;border-radius:50%;animation:btnspin 0.7s linear infinite;vertical-align:-2px;margin-right:7px;}
  @keyframes btnspin{to{transform:rotate(360deg);}}
  #turnBannerTop{padding:16px 16px 12px;display:flex;align-items:center;gap:14px;}
  #turnBanner.my-turn #turnBannerTop{background:#3a2800;}
  #turnBanner.other-turn #turnBannerTop{background:#152a1e;}
  #turnBanner.locked #turnBannerTop{background:#2a1010;}
  .turn-avatar{width:54px;height:54px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.45rem;font-weight:900;flex-shrink:0;text-transform:uppercase;}
  #turnBanner.my-turn .turn-avatar{background:var(--gold);color:var(--on-accent);}
  #turnBanner.other-turn .turn-avatar{background:var(--felt);color:white;}
  #turnBanner.locked .turn-avatar{background:var(--red);color:white;}
  .turn-text-col{flex:1;min-width:0;}
  .turn-label{font-size:0.68rem;text-transform:uppercase;letter-spacing:1.5px;color:var(--text-muted);margin-bottom:2px;}
  .turn-name{font-size:1.65rem;font-weight:900;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.1;color:var(--gold);}
  #turnBanner.locked .turn-name{color:var(--red);}
  .turn-sub{font-size:0.88rem;font-weight:700;margin-top:3px;}
  .turn-sub.is-you{color:var(--gold);}
  .turn-sub.is-other{color:var(--text-muted);}
  #turnBannerBottom{padding:10px 16px;border-top:1px solid rgba(255,255,255,0.06);}
  #turnBanner.my-turn #turnBannerBottom{background:#2a1e00;}
  #turnBanner.other-turn #turnBannerBottom{background:var(--bg);}
  #turnBanner.locked #turnBannerBottom{background:#1a0808;}
  #skipTurnBtn{width:100%;padding:12px;border-radius:8px;border:none;font-size:1rem;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:10px;transition:all 0.15s;}
  #skipTurnBtn:not(:disabled){background:#2d6e4e;color:white;}
  #skipTurnBtn:not(:disabled):active{transform:scale(0.97);}
  #skipTurnBtn:disabled{background:var(--surface);color:var(--text-dim);cursor:not-allowed;}
  .cd-wrap{position:relative;width:26px;height:26px;flex-shrink:0;}
  .cd-wrap svg{position:absolute;top:0;left:0;transform:rotate(-90deg);}
  .cd-num{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:0.75rem;font-weight:900;color:var(--red);}

  /* ── LOBBY ── */
  .player-list{list-style:none;}
  .player-list li{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--bg);border-radius:8px;margin-bottom:6px;}
  .player-list .pname{flex:1;font-weight:600;}
  .kick-btn{font-size:0.75rem;padding:4px 10px;background:var(--red);color:white;border:none;border-radius:6px;cursor:pointer;}
  /* Item 2 (Ryan, 2026-06-26): compact handicap +/- control, sized to sit
     inline in a player-list row alongside the name and Kick button without
     forcing the row to wrap on narrow screens. */
  .handicap-stepper{display:inline-flex;align-items:center;gap:4px;background:var(--surface-deep);border:1px solid var(--border);border-radius:6px;padding:2px 4px;flex-shrink:0;}
  .hc-btn{width:20px;height:20px;line-height:1;font-size:0.85rem;font-weight:700;padding:0;background:var(--surface-alt);color:var(--text-soft);border:none;border-radius:4px;cursor:pointer;}
  .hc-btn:disabled{opacity:0.35;cursor:default;}
  .hc-val{font-size:0.78rem;font-weight:700;color:var(--gold);min-width:20px;text-align:center;}
  /* Bug #1 (Ryan, 2026-07-06): bigger variant of .hc-btn/.hc-val for the
     Hide After stepper in User Settings — the default size was too small
     to tap comfortably, and the 1-digit value shifted the + button left
     of where it sat once the count hit 10 (fixed by zero-padding the
     value to 2 digits in JS, paired with a fixed min-width here). */
  .hc-btn-lg{width:32px;height:32px;font-size:1.2rem;}
  .hc-val-lg{font-size:1.1rem;min-width:34px;}
  .handicap-badge{font-size:0.74rem;font-weight:700;color:var(--gold);background:var(--surface-deep);border:1px solid var(--border);border-radius:6px;padding:2px 7px;flex-shrink:0;}
  .truncated-name{cursor:pointer;}
  .join-badge{background:var(--red);color:white;border-radius:50%;width:20px;height:20px;display:inline-flex;align-items:center;justify-content:center;font-size:0.75rem;font-weight:700;margin-left:6px;vertical-align:middle;}
  .pending-join-item{display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--bg);border-radius:8px;margin-bottom:8px;}
  .pending-join-item .pj-name{flex:1;font-weight:600;}

  /* ── TABLE ── */
  .table-felt{background:var(--felt);border-radius:16px;padding:18px;border:4px solid #5c3317;box-shadow:inset 0 0 30px rgba(0,0,0,0.4);}
  .balls-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px 12px;justify-items:center;}
  .ball-wrap{display:flex;flex-direction:column;align-items:center;gap:2px;cursor:pointer;user-select:none;}
  .ball-wrap.disabled{pointer-events:none;}
  .ball{width:56px;height:56px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.1rem;font-weight:900;color:white;text-shadow:0 1px 3px rgba(0,0,0,0.6);box-shadow:inset -4px -4px 8px rgba(0,0,0,0.3),inset 2px 2px 6px rgba(255,255,255,0.2),0 4px 10px rgba(0,0,0,0.5);transition:transform 0.15s;position:relative;border:2px solid rgba(255,255,255,0.15);background:var(--ballclr,#888);overflow:hidden;isolation:isolate;}
  .ball:active{transform:scale(0.88);}
  /* Item (Ryan, 2026-07-03 follow-up): highlights whichever ball a
     potted-by tooltip is currently open for, so it's obvious which ball
     you actually tapped/hovered when there are several in a tight row. */
  .ball-highlighted{box-shadow:0 0 0 3px var(--gold),0 0 12px rgba(245,197,24,0.7)!important;transform:scale(1.1);}
  .ball-dot.ball-highlighted{box-shadow:0 0 0 2px var(--gold),0 0 8px rgba(245,197,24,0.7)!important;transform:scale(1.25);}
  /* Item (Ryan, 2026-07-10): same idea as ball-highlighted above, but for
     the live status-bar rows (Game Mode, Frame, Players remaining, Balls
     potted, etc.) — marks the label+value text while its tooltip is open. */
  .stat-highlighted{color:var(--gold)!important;}
  /* Item (Ryan, 2026-07-06, per Ryan): authentic billiard-ball look -- solid
     balls (1-8) are a coloured ball with a white centre circle behind the
     number; striped balls (9-15) are a white ball with a coloured band
     through the middle, same white centre circle on top. 6 and 9 get an
     underline on the number so they're never ambiguous upside down. */
  .ball::before{content:'';position:absolute;width:48%;height:48%;top:26%;left:26%;border-radius:50%;background:#fff;box-shadow:inset 0 1px 3px rgba(0,0,0,0.25);z-index:1;}
  /* Item (Ryan, 2026-07-06, per Ryan): band now painted as a single vertical
     gradient across the ball's own background (not a separate white base +
     coloured overlay span). A separate overlay only ever reached the clipped
     content area, not the 2px border ring around it, so the ball's own white
     base colour was peeking through as a faint ring on the left/right of the
     band -- the gradient covers the whole box, ring included, so the band
     genuinely reaches the ball's true edge with no seam, while still leaving
     proper white caps top and bottom (16%/84% split, per Ryan). */
  .ball.stripe{background:linear-gradient(to bottom, #fff 0%, #fff 16%, var(--ballclr) 16%, var(--ballclr) 84%, #fff 84%, #fff 100%);}
  .ball span{position:relative;z-index:2;color:#111;text-shadow:none;font-weight:800;}
  .ball span.num-underline{text-decoration:underline;text-underline-offset:2px;}
  .ball.out{opacity:0.22;box-shadow:none;transform:none !important;}
  /* Item 1 (Ryan, 2026-06-26): subtle green ring, not a colour swap — the ball's
     own number colour (b1..b16) still has to read normally, this is purely an
     "extra signal" overlay, same spirit as .out using opacity rather than
     recolouring the ball itself. */
  .ball.unassigned-ball{box-shadow:0 0 0 3px #3ddc84,inset -4px -4px 8px rgba(0,0,0,0.3),inset 2px 2px 6px rgba(255,255,255,0.2),0 4px 10px rgba(0,0,0,0.5);}
  .unassigned-label{color:var(--success);font-weight:700;}
  .ball-attr{position:absolute;inset:0;border-radius:50%;background:rgba(0,0,0,0.65);display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3px;gap:1px;}
  .ball-attr .ba-potter{font-size:0.38rem;color:var(--gold);font-weight:700;text-align:center;line-height:1.2;max-width:52px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .ball-attr .ba-owner{font-size:0.33rem;color:rgba(255,255,255,0.75);text-align:center;line-height:1.2;max-width:52px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .ball-label{font-size:0.62rem;color:rgba(255,255,255,0.9);text-align:center;max-width:62px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-height:0px;display:none;}
  .ball-label.mine{color:var(--gold);font-weight:700;}
  .b1{--ballclr:#f5c518}.b2{--ballclr:#1a5cb5}.b3{--ballclr:#c0392b}.b4{--ballclr:#7d3c98}
  .b5{--ballclr:#e67e22}.b6{--ballclr:#27ae60}.b7{--ballclr:#922b21}.b8{--ballclr:#111}
  .b9{--ballclr:#f5c518}.b10{--ballclr:#1a5cb5}.b11{--ballclr:#c0392b}.b12{--ballclr:#7d3c98}
  .b13{--ballclr:#e67e22}.b14{--ballclr:#27ae60}.b15{--ballclr:#922b21}
  .b16{background:white;color:#333;border:2px solid #ccc;}
  .ball.b16::before{content:none;}

  /* ── WHITE BALL TOGGLE (replaces plain checkbox) ── */
  /* Fixed two-line layout on every browser/width, not just narrow phones: line 1 is
     always icon + text together (wrapping together as a unit if needed), line 2 is
     always the Yes/No buttons at full width. Previously .wb-text used flex:1 1 auto
     inside a single flex-wrap row shared with .wb-yn, so whether Yes/No ended up on
     line 1 or wrapped to line 2 depended on available width — consistent only below
     the 480px mobile override, inconsistent above it (varying by viewport/zoom). */
  .wb-toggle{display:flex;flex-wrap:wrap;align-items:center;align-content:center;gap:10px;background:var(--surface-deep);border:1px solid var(--border);border-radius:8px;padding:7px 10px;margin-bottom:10px;user-select:none;}
  .wb-icon{width:30px;height:30px;border-radius:50%;flex-shrink:0;background:radial-gradient(circle at 32% 28%,#fff,#dcdcdc);border:1px solid #bbb;box-shadow:inset -3px -3px 6px rgba(0,0,0,0.15),0 2px 4px rgba(0,0,0,0.4);transition:opacity 0.2s,filter 0.2s,transform 0.2s;}
  .wb-toggle.off .wb-icon{opacity:0.25;filter:grayscale(1);box-shadow:none;transform:scale(0.85);}
  .wb-text{flex:1 1 auto;min-width:0;font-size:0.82rem;color:var(--text-soft);line-height:1.3;transition:opacity 0.2s;}
  .wb-text .wb-sub{color:var(--text-muted);}
  .wb-toggle.off .wb-text{opacity:0.6;}
  .wb-yn{display:flex;flex-basis:100%;background:var(--bg);border:1px solid var(--border);border-radius:20px;padding:2px;gap:2px;}
  .wb-yn-btn{flex:1;border:none;background:transparent;font-size:0.82rem;font-weight:700;padding:8px 12px;border-radius:18px;cursor:pointer;color:var(--text-muted);transition:background 0.15s,color 0.15s;}
  .wb-yn-btn:active{transform:scale(0.94);}
  .token-stepper-row{display:flex;gap:6px;justify-content:center;}
  .token-stepper-btn{flex:1;margin:0;padding:10px 0;font-size:0.95rem;font-weight:900;}
  .token-stepper-btn:active{transform:scale(0.92);}
  #connectionToast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:#3a3a3a;color:white;padding:10px 20px;border-radius:30px;font-size:0.9rem;display:none;z-index:999;box-shadow:0 4px 20px rgba(0,0,0,0.4);white-space:nowrap;}
  .wb-toggle.off .wb-yn-btn.no{background:#c0392b;color:#fff;}
  .wb-toggle:not(.off) .wb-yn-btn.yes{background:var(--gold);color:var(--surface);}
  .wb-toggle.locked{opacity:0.7;}
  .wb-toggle.locked .wb-yn-btn{cursor:not-allowed;pointer-events:none;}
  .wb-lock-note{font-size:0.74rem;color:var(--text-muted);flex-basis:100%;margin-top:2px;}
  /* Narrower column on Next Frame Setup (shares the row with Stake per frame) needs
     a smaller icon and tighter gap so "White ball included?" fits on one line
     instead of wrapping mid-phrase at that width — text also gets nowrap+ellipsis
     as a safety net rather than letting it ever push the icon to its own line. */
  .wb-toggle-compact{gap:5px;padding:6px 7px;}
  .wb-toggle-compact .wb-icon{width:16px;height:16px;flex-shrink:0;}
  .wb-toggle-compact .wb-text{font-size:0.7rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
  .order-btn.selected{background:var(--gold);color:var(--surface);}
  .wb-toggle-compact .wb-yn{flex-basis:100%;}

  /* ── SETTINGS GRID (Item 3, Ryan 2026-06-27) ──
     Unifies Balls per player / White ball / Auto-show unassigned / Token
     stake into one label-left, control-right grid. Label column is a fixed
     small width so it reads as a heading, not a form label competing for
     space; control column takes the rest of the row. Balls per player's
     control is the one exception with real height (ball-picker + Max
     Possible stacked), so .set-ctrl just lets content grow naturally - the
     fixed width is only enforced on the label side. */
  .set-grid{display:flex;flex-direction:column;gap:10px;}
  .set-row{display:flex;align-items:flex-start;gap:10px;}
  /* Item 3 follow-up (Ryan, 2026-06-27): Balls per player needs the FULL card
     width for its 8-ball row, not just the control column - label moves to
     its own line above instead of sharing a row with the control. */
  .set-row-stacked{display:flex;flex-direction:column;gap:6px;}
  .set-row-stacked .set-label{width:auto;flex:none;padding-top:0;}
  .set-label{flex:0 0 150px;width:150px;font-size:0.78rem;color:var(--text-label);line-height:1.3;padding-top:8px;display:flex;align-items:flex-start;gap:4px;white-space:nowrap;}
  .set-label .info-btn{flex-shrink:0;margin-top:-2px;}
  .set-ctrl{flex:1;min-width:0;}
  .set-ctrl-checkbox{display:flex;align-items:center;min-height:36px;}
  /* Themed checkbox (Ryan, 2026-06-27 follow-up): native checkbox replaced with
     a custom gold-filled square + check glyph so it matches the app's other
     selected-state treatments (.bp-ball.sel, .gm-option.selected) instead of
     looking like a bare unstyled browser default against the dark card. */
  .gui-checkbox{position:relative;display:inline-flex;width:26px;height:26px;flex-shrink:0;cursor:pointer;}
  .gui-checkbox input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer;}
  .gui-checkbox-box{width:26px;height:26px;border-radius:6px;background:var(--bg);border:2px solid var(--border);display:flex;align-items:center;justify-content:center;transition:background 0.15s,border-color 0.15s;pointer-events:none;}
  .gui-checkbox input:checked ~ .gui-checkbox-box{background:var(--gold);border-color:var(--gold);}
  .gui-checkbox-box svg{width:16px;height:16px;opacity:0;transition:opacity 0.1s;stroke:var(--surface);stroke-width:3;fill:none;stroke-linecap:round;stroke-linejoin:round;}
  .gui-checkbox input:checked ~ .gui-checkbox-box svg{opacity:1;}
  .set-ctrl-note{font-size:0.78rem;color:var(--text-muted);display:flex;align-items:center;min-height:36px;}
  .set-ctrl .wb-toggle{margin-bottom:0;}
  /* Item (Ryan, 2026-07-02, per Ryan): now spans the full control column
     width instead of shrinking to the pill's own natural width — was
     narrow/inline before. */
  .set-ctrl .wb-toggle{display:flex;width:100%;}
  .set-ctrl .wb-toggle .wb-yn{flex-basis:100%;}
  .set-ctrl .wb-toggle .wb-yn-btn{padding:8px 16px;}
  /* Token stake stepper (Ryan, 2026-06-27 follow-up): replaces the bare number
     input with the same +/-1/5/10 stepper pattern already used for treasury
     give/take, so adjusting a stake feels consistent with the rest of the app
     instead of needing the OS numeric keypad for a value that's almost always
     a round number anyway. */
  .stake-stepper{display:flex;flex-direction:column;gap:8px;}
  .stake-stepper-display{display:flex;align-items:center;justify-content:center;gap:6px;background:var(--surface-deep);border:1px solid var(--border);border-radius:8px;padding:8px 0;font-size:1rem;font-weight:900;color:var(--gold);}
  @media (max-width:480px){
    .set-label{flex:0 0 110px;width:110px;font-size:0.72rem;white-space:normal;}
    .set-row-stacked .set-label{width:auto;}
  }

  /* ── GAME MODE PICKER ── */
  .gm-options{display:flex;flex-direction:column;gap:8px;}
  .gm-option{display:flex;align-items:center;gap:10px;background:var(--surface-deep);border:1px solid var(--border);border-radius:8px;padding:10px 12px;cursor:pointer;user-select:none;transition:border-color 0.15s,background 0.15s;}
  .gm-option input[type="radio"]{width:18px;height:18px;flex-shrink:0;accent-color:var(--gold);cursor:pointer;}
  .gm-option-body{flex:1;min-width:0;}
  .gm-option-title{font-size:0.88rem;font-weight:700;color:var(--text-soft);}
  .gm-option-sub{font-size:0.76rem;color:var(--text-muted);margin-top:1px;}
  .gm-option.selected{border-color:var(--gold);background:rgba(245,197,24,0.08);}
  .gm-option.selected .gm-option-title{color:var(--gold);}
  .gm-option .info-btn{flex-shrink:0;}
  /* Balls-per-player picker visually disabled when Traditional mode forces it to 1 */
  .set-ctrl.bp-locked{opacity:0.45;pointer-events:none;}

  /* ── BALLS-PER-PLAYER PICKER ── */
  .bp-col{padding-right:14px;}
  .bp-row{margin-top:6px;margin-bottom:6px;background:var(--surface-deep);border:1px solid var(--border);border-radius:8px;padding:10px;display:flex;flex-direction:column;justify-content:center;}
  .bp-balls-row{display:flex;justify-content:space-between;flex-wrap:wrap;align-items:center;}
  /* Second row: "Max possible" sits centred via the side-spacers, and the ? sits
     pinned to the far right — placing it level with this whole row (rather than next
     to just the chip) signals it explains the balls-per-player section as a whole,
     not the Max possible option specifically. */
  .bp-second-row{display:flex;align-items:center;margin-top:8px;}
  .bp-second-row-spacer{flex:1;}
  /* Sits in the second row's normal flow but is pulled upward via negative margin
     so it visually straddles the gap between row 1 and row 2 — reading as "this ?
     explains the whole balls-per-player section" rather than belonging to either
     row specifically. */
  .bp-info-btn{margin-top:-13px;align-self:flex-start;}
  .bp-ball{width:38px;height:38px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:0.85rem;font-weight:900;color:white;text-shadow:0 1px 2px rgba(0,0,0,0.6);box-shadow:inset -3px -3px 6px rgba(0,0,0,0.3),inset 1px 1px 4px rgba(255,255,255,0.2),0 2px 6px rgba(0,0,0,0.5);border:2px solid rgba(255,255,255,0.15);cursor:pointer;transition:transform 0.15s,opacity 0.15s,box-shadow 0.15s;position:relative;flex-shrink:0;background:var(--ballclr,#888);}
  .bp-ball:active{transform:scale(0.9);}
  .bp-ball.dim{opacity:0.55;}
  .bp-ball.unavail{opacity:0.18;filter:grayscale(1);box-shadow:none;cursor:not-allowed;pointer-events:none;}
  .bp-ball.unavail::after{content:'';position:absolute;width:120%;height:2px;background:#c0392b;top:50%;left:-10%;transform:rotate(-35deg);border-radius:2px;}
  .bp-ball.sel{opacity:1;transform:scale(1.12);border-color:var(--gold);box-shadow:0 0 0 2px var(--gold),0 0 10px rgba(245,197,24,0.6),inset -3px -3px 6px rgba(0,0,0,0.3),inset 1px 1px 4px rgba(255,255,255,0.2);}
  .bp-max{height:38px;display:flex;align-items:center;justify-content:center;padding:0 12px;border-radius:19px;background:var(--bg);border:2px solid var(--border);color:var(--text-muted);font-size:0.74rem;font-weight:700;cursor:pointer;flex-shrink:0;transition:background 0.15s,color 0.15s,border-color 0.15s,transform 0.15s;}
  .bp-max:active{transform:scale(0.94);}
  .bp-max.sel{background:var(--gold);color:var(--surface);border-color:var(--gold);}
  /* Next Frame Setup + lobby Match Settings: Max Possible spans the full width of
     its own row, below the two rows of 4 balls. The balls row above uses
     justify-content:space-between to fill the column's full width, so this pill
     just matches at width:100% — no fixed cap needed since both now track the
     column's actual width together. */
  .bp-max-row{margin-top:6px;}
  .bp-max-row .bp-max{width:100%;}

  /* ── MY BALLS ── */
  .my-balls-card{background:var(--surface-deep);border:2px solid #2a5a3a;border-radius:12px;padding:10px;flex:1 1 0;min-width:0;min-height:64px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:6px;}
  .my-balls-card h3{color:var(--gold);font-size:0.85rem;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px;}
  .my-balls-display{display:flex;gap:10px;flex-wrap:wrap;}
  .my-balls-toggle{cursor:pointer;user-select:none;-webkit-user-select:none;text-align:center;font-weight:800;font-size:1.05rem;letter-spacing:0.5px;color:var(--gold);}
  #eliminatedBallsRow{display:flex;gap:10px;align-items:stretch;margin-bottom:14px;flex-wrap:nowrap;}
  #eliminatedBallsRow #eliminatedCard{margin-bottom:0;flex:1 1 0;min-width:0;}
  #eliminatedBallsRow #myBallsCard{margin-bottom:0;}
  /* Item (Ryan, 2026-07-02): "Mine" label above a player's own ball in the
     main table grid, shown only while revealed (or always once eliminated) —
     the ball itself also gets a slightly bigger/glowing treatment via
     #ballsGrid.reveal-mine below. */
  .mine-grid-label{color:var(--gold);font-weight:800;display:inline-block;}
  #ballsGrid.reveal-mine .ball-wrap[data-mine] .ball{box-shadow:0 0 0 3px var(--gold),0 0 10px rgba(245,197,24,0.6);}

  /* ── PLAYERS TAB ── */
  .turn-list{list-style:none;}
  .turn-list li{display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--bg);border-radius:8px;margin-bottom:6px;}
  .turn-list li.current{background:var(--surface-hover);border:2px solid var(--gold);}
  .turn-list li.eliminated{opacity:0.35;text-decoration:line-through;}
  .turn-num{width:26px;height:26px;border-radius:50%;background:var(--felt);color:white;display:flex;align-items:center;justify-content:center;font-size:0.78rem;font-weight:700;flex-shrink:0;}
  .turn-list li.current .turn-num{background:var(--gold);color:var(--on-accent);}
  .balls-mini{display:flex;gap:3px;flex-wrap:wrap;}
  .ball-dot{width:22px;height:22px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;font-size:0.62rem;font-weight:700;color:white;flex-shrink:0;}
  .ball-dot.hidden-ball{background:var(--border);color:var(--text-muted);}
  .ball-dot.out{opacity:0.2;}

  /* ── GAME OVER ── */
  .podium{display:flex;align-items:flex-end;justify-content:center;gap:10px;margin:16px 0;}
  .podium-spot{text-align:center;}
  .pos-box{border-radius:8px 8px 0 0;display:flex;align-items:center;justify-content:center;font-size:1.4rem;font-weight:900;}
  .p1 .pos-box{height:80px;width:80px;background:var(--gold);color:var(--on-accent);}
  .p2 .pos-box{height:60px;width:70px;background:var(--felt);color:white;}
  .p3 .pos-box{height:45px;width:70px;background:var(--border);color:var(--text-label);}
  .podium-name{font-size:0.78rem;margin-top:4px;max-width:80px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .result-row{display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--bg);border-radius:8px;margin-bottom:6px;}
  .fun-facts{background:var(--surface-alt);border:1px solid var(--border);border-radius:10px;padding:14px;margin-top:12px;}
  .fun-facts h3{color:var(--gold);font-size:0.85rem;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px;}
  .fact-row{display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--surface);font-size:0.88rem;}
  .fact-row:last-child{border-bottom:none;}
  .fact-val{color:var(--gold);font-weight:700;}

  /* ── MATCH HISTORY ── */
  .mh-table{width:100%;border-collapse:collapse;font-size:0.78rem;}
  .mh-table tr.eliminated-row td{opacity:0.55;}
  .mh-table td.balls-col{text-align:left;}
  .mh-table .balls-mini-row{display:flex;gap:4px;flex-wrap:wrap;align-items:center;}
  .mh-table th{background:var(--surface-alt);padding:7px 8px;text-align:center;color:var(--text-muted);font-weight:600;border-bottom:1px solid var(--border);}
  .mh-table th.name-col{text-align:left;}
  .mh-table td{padding:7px 8px;border-bottom:1px solid var(--surface);text-align:center;}
  .mh-table td.name-col{text-align:left;font-weight:600;}
  /* Item (Ryan, 2026-07-10, per Ryan): compact modifier bringing the Frame
     Over / Match History screen's tables (Match History, Frame Results,
     Token History) to the same tight mobile sizing already used on the Club
     Matches panels (Summary/Frames/Tokens toggles) — smaller font, tighter
     padding, uppercase abbreviated headers. Applied alongside .mh-table
     rather than replacing it, so untouched .mh-table usages elsewhere (live
     Match History tab, turnList, etc.) keep their existing looser sizing. */
  .mh-table-compact th{padding:5px 6px;font-size:0.66rem;text-transform:uppercase;letter-spacing:0.4px;text-align:left;}
  .mh-table-compact td{padding:5px 6px;font-size:0.76rem;text-align:left;}
  .pos-win{color:var(--gold);font-weight:700;}
  .pos-out{color:var(--text-muted);}
  .pos-absent{color:var(--text-dim);}
  .pos-warn{color:#f5a623;}
  /* Frame-by-frame breakdown: sticky Player column + themed scrollbar. Scoped to
     .mh-breakdown-wrap specifically (not all .mh-table usages) since this is the only
     table wide enough to need horizontal scroll — one column per frame played. */
  .mh-breakdown-wrap{overflow-x:auto;border-radius:8px;scrollbar-width:thin;scrollbar-color:var(--gold) var(--surface);}
  .mh-breakdown-wrap::-webkit-scrollbar{height:8px;}
  .mh-breakdown-wrap::-webkit-scrollbar-track{background:var(--surface);border-radius:8px;}
  .mh-breakdown-wrap::-webkit-scrollbar-thumb{background:var(--gold);border-radius:8px;opacity:0.7;}
  .mh-breakdown-wrap::-webkit-scrollbar-thumb:hover{background:var(--gold);opacity:1;}
  .mh-breakdown-wrap .mh-table th.name-col,.mh-breakdown-wrap .mh-table td.name-col{position:sticky;left:0;z-index:1;}
  .mh-breakdown-wrap .mh-table th.name-col{background:var(--surface-alt);}
  .mh-breakdown-wrap .mh-table td.name-col{background:var(--surface);box-shadow:1px 0 0 var(--border);}
  /* Frame History modal table (Ryan, 2026-07-09 mobile fit): this renders
     inside the 380px-wide joinErrorModal shell on the Club Matches "Frames"
     panel, so it needs tighter padding/font than the base .mh-table used
     elsewhere (e.g. the live Match's own History tab, which has more room). */
  /* Match History stats table (Ryan, 2026-07-14 bug fix): F/W/FO/Elim/Pot/Avg
     cells were wrapping (e.g. "17/24" splitting across 2 lines) because the
     Player column was soaking up the width in this narrow auto-layout table.
     Cap Player's width and stop the stat cells from ever wrapping. */
  #mhStatsTable th.name-col,#mhStatsTable td.name-col{width:24%;}
  #mhStatsTable th:not(.name-col),#mhStatsTable td:not(.name-col){white-space:nowrap;}
  .fh-modal-table{font-size:0.7rem;}
  .fh-modal-table th{padding:5px 4px;font-size:0.62rem;}
  .fh-modal-table td{padding:5px 4px;}
  /* BUGFIX (Ryan, 2026-07-14): "By" column names (already truncated to 8
     chars in app.js) were still wrapping to 2 lines in this cramped 380px
     modal because the name-col cells had no white-space:nowrap -- the
     column was rendering narrower than even the truncated text needed. */
  .fh-modal-table td.name-col,.fh-modal-table th.name-col{white-space:nowrap;}
  .game-summary-row{display:flex;gap:8px;padding:6px 0;border-bottom:1px solid var(--surface);font-size:0.82rem;align-items:center;}
  .game-summary-row:last-child{border-bottom:none;}

  /* ── SETTLEMENT ── */
  .settlement-card{background:var(--surface-alt);border:2px solid var(--gold);border-radius:12px;padding:16px;margin-top:12px;}
  .settlement-card h3{color:var(--gold);font-size:1rem;margin-bottom:12px;}
  .settlement-row{display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--surface);border-radius:8px;margin-bottom:8px;font-size:0.95rem;}
  .settlement-row:last-child{margin-bottom:0;}
  .settlement-amount{font-size:1.2rem;font-weight:900;color:var(--gold);flex-shrink:0;}
  .balance-row{display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--surface);font-size:0.85rem;}
  .balance-row:last-child{border-bottom:none;}
  .balance-pos{color:var(--success);font-weight:700;}
  .balance-neg{color:var(--red);font-weight:700;}
  .balance-zero{color:var(--text-muted);}

  /* ── NEXT GAME ── */
  .between-games-player{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--bg);border-radius:8px;margin-bottom:6px;}
  .bgp-name{flex:1;font-weight:600;font-size:0.86rem;}
  .drag-item{background:var(--bg);border-radius:8px;padding:8px 10px;margin-bottom:6px;display:flex;align-items:center;gap:8px;border:1px solid transparent;cursor:grab;}
  .drag-item.drag-over{border-color:var(--gold);}
  .drag-item.removed{opacity:0.35;text-decoration:line-through;cursor:default;}
  /* Item (Ryan, 2026-07-10, per Ryan): sitting-out players on Match Setup
     (lobby) and Frame Over were only getting their NAME struck through —
     the handicap column had no strikethrough at all. Applying the line at
     the row level (rather than per-field) draws it across every bit of text
     in the row, handicap included, and it's set a bit thicker than a normal
     strikethrough so a sat-out row reads clearly at a glance. Used on both
     the admin .drag-item rows and the non-admin read-only <tr> rows. */
  /* Item (Ryan, 2026-07-10, per Ryan): text-decoration:line-through only ever
     drew through actual text runs — buttons (Kick/Sit Out) and the gaps
     between grid cells never got a line through them, so the row looked
     "half struck-through" at best. Switched to a real overlay line
     (::after, absolutely positioned edge-to-edge, pointer-events:none so
     the Sit Out button stays clickable) so the strike genuinely spans the
     whole row — buttons included — and made it a touch thicker (3px). */
  .row-sitting-out{position:relative;}
  .row-sitting-out::after{content:'';position:absolute;left:0;right:0;top:50%;height:3px;background:var(--text-muted);transform:translateY(-50%);pointer-events:none;}
  /* Item (Ryan, 2026-07-10, per Ryan): on the admin roster grid (Match Setup
     lobby + Frame Over's Next Frame Setup), the strike was spanning the
     WHOLE row including the Kick (✕) and Sit Out (🪑/✓) buttons, making them
     look struck-through/disabled even though they're still live controls.
     Grid columns are 20px(handle) 28px(pos) 1fr(name) 92px(handicap)
     38px(kick) 38px(sitout) with 8px gaps — stop the line at the right edge
     of the Handicap column, i.e. before the Kick/Sit Out buttons:
     38+8+38+8 = 92px in from the row's right edge. The non-admin read-only
     <tr> rows (no Kick/Sit Out columns) keep the full-width line above. */
  #nextGameRoster .drag-item.row-sitting-out::after,
  #lobbyOrder .drag-item.row-sitting-out::after{right:92px;}
  /* Item (Ryan, 2026-07-02, per Ryan): Next Frame Setup's player roster
     turned into a standard columned grid (drag handle / Pos / Player /
     Handicap / Kick / Sit Out) instead of everything crammed into one flex
     row. Item (Ryan, 2026-07-02, per Ryan): now also applied to the Lobby's
     own #lobbyOrder roster, which shares the same lobbyOrderRowInner markup
     for consistency between the two screens. */
  .roster-grid-header{display:grid;grid-template-columns:20px 28px 1fr 92px 38px 38px;gap:8px;align-items:center;padding:0 10px;margin-bottom:6px;font-size:0.66rem;text-transform:uppercase;letter-spacing:0.05em;color:var(--text-muted);}
  .roster-grid-header span{text-align:center;}
  .roster-grid-header span:first-child, .roster-grid-header span:nth-child(2), .roster-grid-header span:nth-child(3){text-align:left;}
  .roster-header-info-btn{width:14px;height:14px;font-size:0.6rem;margin-left:3px;}
  #nextGameRoster .drag-item, #lobbyOrder .drag-item{display:grid;grid-template-columns:20px 28px 1fr 92px 38px 38px;gap:8px;}
  /* Ryan, 2026-07-14: match the plain-table look used elsewhere (e.g. Club
     Members) instead of the rounded "card" look .drag-item has by default —
     thin row divider, no radius, no per-row background/margin. Drag/grid
     functionality (columns, drag-and-drop) is unchanged, this is visual only. */
  #nextGameRoster .drag-item, #lobbyOrder .drag-item{background:transparent;border-radius:0;margin-bottom:0;border:none;border-bottom:1px solid var(--border);padding:8px;font-size:0.82rem;}
  #nextGameRoster .drag-item.drag-over, #lobbyOrder .drag-item.drag-over{border-bottom-color:var(--gold);}
  .roster-grid-header{background:var(--surface-alt);padding:7px 8px;border-bottom:1px solid var(--border);}
  /* Compact icon-only Kick/Sit-Out buttons for the Frame Over roster row
     (Ryan, 2026-06-27 follow-up) — name + handicap stepper + 2 buttons was
     overflowing .btn-sm's normal padding/wordy labels onto a second line. */
  .roster-action-btn{width:30px;height:30px;padding:0;font-size:0.85rem;flex-shrink:0;}

  /* ── MISC ── */
  .info-row{display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--surface);font-size:0.88rem;}
  .info-row:last-child{border-bottom:none;}
  .tabs{display:flex;gap:4px;margin-bottom:14px;background:var(--bg);border-radius:10px;padding:4px;}
  .tab{flex:1;padding:8px;text-align:center;border-radius:8px;cursor:pointer;font-size:0.85rem;font-weight:600;color:var(--text-label);border:none;background:transparent;transition:all 0.2s;}
  .tab.active{background:var(--felt);color:white;}
  .stats-table{width:100%;border-collapse:collapse;font-size:0.82rem;}
  .stats-table th{background:var(--green);padding:8px;text-align:left;color:var(--gold);}
  .stats-table td{padding:7px 8px;border-bottom:1px solid var(--border);}
  .game-result-row{display:flex;align-items:center;gap:8px;padding:7px 10px;background:var(--bg);border-radius:8px;margin-bottom:5px;font-size:0.88rem;}
  .text-muted{color:var(--text-muted);font-size:0.85rem;}
  .badge{display:inline-block;padding:2px 8px;border-radius:20px;font-size:0.72rem;font-weight:700;}
  .badge-gold{background:var(--gold);color:var(--on-accent);}
  .badge-red{background:var(--red);color:white;}
  .section-title{font-size:0.72rem;text-transform:uppercase;letter-spacing:1px;color:var(--text-muted);margin-bottom:8px;}
  .winner-badge{background:var(--gold);color:var(--on-accent);padding:2px 8px;border-radius:20px;font-size:0.75rem;font-weight:700;}
  .elim-attr{font-size:0.72rem;color:var(--text-muted);margin-top:2px;}
  /* players tooltip */
  /* BUGFIX (Ryan, 2026-07-13): z-index was 50 - below .modal-overlay's 200 -
     so this tooltip rendered invisibly behind any open modal (e.g. tapping a
     truncated name in the Frame Summary popup). Matches .mh-stat-tooltip's
     500 so it's consistently above every modal. */
  .players-tooltip{position:absolute;background:var(--surface-alt);border:1px solid var(--gold);border-radius:10px;padding:10px 14px;font-size:0.85rem;color:var(--text-soft);z-index:500;max-width:min(360px,calc(100vw - 32px));box-shadow:0 4px 16px rgba(0,0,0,0.5);line-height:1.8;pointer-events:none;}
  .players-tooltip-name{display:flex;align-items:center;gap:6px;}
  .divider{text-align:center;color:var(--border-strong);font-size:0.8rem;margin:6px 0;}
  /* Match history stat tooltip */
  .mh-stat-tooltip{position:absolute;background:var(--surface-alt);border:1px solid var(--gold);border-radius:10px;padding:10px 14px;font-size:0.85rem;color:var(--text-soft);z-index:500;width:220px;box-shadow:0 4px 16px rgba(0,0,0,0.6);cursor:pointer;}
  .mh-tt-title{font-size:0.72rem;text-transform:uppercase;letter-spacing:1px;color:var(--text-muted);margin-bottom:4px;}
  .mh-tt-summary{font-weight:700;color:var(--gold);margin-bottom:4px;font-size:0.95rem;}
  .mh-tt-detail{font-size:0.78rem;color:var(--text-muted);line-height:1.5;}
  .mh-tt-row{font-size:0.82rem;color:var(--text-soft);line-height:1.6;padding:1px 0;}
  /* Collapsible sections */
  .collapsible-header:hover{background:rgba(255,255,255,0.04);}
  .collapsible-header.open .collapse-arrow{transform:rotate(180deg);}
  /* Live match banner in header */
  .header-live-pill{background:#1a4a28;border:1px solid var(--gold);color:var(--gold);font-weight:700;padding:4px 12px;border-radius:20px;font-size:0.82rem;cursor:pointer;text-decoration:none;display:inline-block;}
  .header-live-pill:hover{background:#2a6a38;}

  /* ── BALL LABELS outside ball ── */
  .ball-potter-label{font-size:0.58rem;color:var(--gold);font-weight:700;text-align:center;min-height:14px;max-width:66px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3;}
  .ball-owner-label{font-size:0.54rem;color:rgba(255,255,255,0.85);text-align:center;min-height:14px;max-width:66px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.3;}

  /* ── ELIMINATED OVERLAY ── */
  #eliminatedCard{display:none;background:linear-gradient(135deg,#2a0000,#4a0f0f);border:2px solid #c0392b;border-radius:12px;padding:10px 16px;margin-bottom:14px;text-align:center;min-height:64px;align-items:center;justify-content:center;}
  #eliminatedCard.show{display:flex;}
  /* Item 2 fix (Ryan, 2026-06-25 retry): the previous approach dimmed #page-game
     itself with opacity+filter, then tried to cancel that on #sitOutToggleSection
     with opacity:1/filter:none. That can NEVER work — opacity on a parent creates
     a stacking context and composites the ENTIRE subtree at that opacity; a child's
     own opacity:1 only affects how IT blends within the already-dimmed parent layer,
     it can't undo the parent's compositing. Same story for filter. So instead of
     dimming the whole #page-game, dim only the specific things that should look
     greyed out, and simply never touch #sitOutToggleSection (or eliminatedCard /
     myBallsCard, which need the same exemption). */
  body.eliminated-mode #page-game .tabs,
  body.eliminated-mode #page-game #tabContent-table,
  body.eliminated-mode #page-game #tabContent-history,
  body.eliminated-mode #page-game #adminGameControls{opacity:0.45;filter:grayscale(0.6);}
  /* Ryan (2026-06-25, follow-up): not being dimmed isn't the same as looking
     "illuminated" — #sitOutToggleSection only ever had the plain .card border
     (1px, barely lighter than the page background), so next to the dimmed
     roster above it and the bright-bordered eliminatedCard/myBallsCard above
     THAT, it still read as inactive even at full opacity. Give it the same
     glowing-border treatment those cards use, but only in eliminated-mode —
     this is the one control that still matters once you're out, so it should
     visually say so. */
  body.eliminated-mode #page-game #sitOutToggleSection{border-color:#3ddc84;box-shadow:0 0 0 1px #3ddc84,0 0 14px rgba(61,220,132,0.35);background:#2a5e3e;}

  /* ── JOIN NOTIFICATION BANNER ── */
  #joinNotifBanner{position:fixed;top:60px;left:50%;transform:translateX(-50%);z-index:400;background:var(--red);color:white;padding:12px 24px;border-radius:10px;font-weight:700;font-size:0.95rem;box-shadow:0 4px 20px rgba(0,0,0,0.5);display:none;cursor:pointer;white-space:nowrap;}
  #joinNotifBanner.show{display:block;animation:slideDown 0.3s ease;}
  @keyframes slideDown{from{top:40px;opacity:0;}to{top:60px;opacity:1;}}

  /* ── FRAME CONFIRM PAGE ── */
  .frame-confirm-card{background:var(--surface);border:2px solid var(--gold);border-radius:16px;padding:24px;margin:20px auto;max-width:380px;text-align:center;}
  .frame-confirm-stake{font-size:2rem;font-weight:900;color:var(--gold);margin:12px 0;}

  /* ── ABOUT PAGE: TEMPORARY placeholder mockups ──
     Swap for real <img> screenshots later; these are simplified illustrative
     stand-ins built from plain divs so no actual screenshot assets are needed yet. */
  .about-step{display:flex;gap:14px;align-items:flex-start;margin:0 0 20px;}
  /* Corner rounding is baked into the PNGs themselves (Ryan, 2026-07-17) so
     they read as phone screens — no CSS border/radius, it'd fight the
     transparent corners. Tapping any shot opens it in #aboutLightbox. */
  .about-shot{width:120px;flex-shrink:0;display:block;cursor:zoom-in;}
  /* Wide desktop/TV screenshots (Venue Display etc) — unlike the phone shots
     above, these have no baked-in rounding, so it's done in CSS here. */
  .about-hero-shot{width:100%;max-width:640px;display:block;margin:0 auto 14px;border-radius:10px;border:1px solid var(--border);cursor:zoom-in;box-shadow:0 4px 18px rgba(0,0,0,0.35);}
  /* Play Store screenshot strip (Ryan, 2026-07-24) — the 8 real screenshots
     from the Google Play listing, small and horizontally scrollable at the
     top of the About page. Unlike .about-shot above, these are plain
     rectangular phone captures (no baked-in rounding), so the rounding +
     border + shadow are done here in CSS instead. */
  .about-play-strip{display:flex;gap:10px;overflow-x:auto;padding-bottom:4px;-webkit-overflow-scrolling:touch;}
  .about-play-shot{width:74px;flex-shrink:0;display:block;border-radius:8px;border:1px solid var(--border);cursor:zoom-in;box-shadow:0 2px 8px rgba(0,0,0,0.3);}
  /* Prev/Next arrows overlaid on #aboutLightbox — only shown while browsing
     the Play Store strip's gallery (aboutOpenPlayGallery), hidden for every
     other single-shot use of the same lightbox (plain aboutOpenShot calls
     leave these display:none). .modal-overlay is position:fixed, which
     already gives absolutely-positioned children a viewport-anchored
     containing block, so no extra positioning context needed here. */
  .about-lightbox-nav{position:absolute;top:50%;transform:translateY(-50%);background:rgba(0,0,0,0.55);color:#fff;border:none;width:44px;height:44px;border-radius:50%;font-size:1.6rem;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:5;}
  .about-lightbox-nav:active{background:rgba(0,0,0,0.8);}
  .about-lightbox-counter{position:absolute;bottom:16px;left:50%;transform:translateX(-50%);color:#fff;font-size:0.8rem;background:rgba(0,0,0,0.55);padding:3px 12px;border-radius:12px;z-index:5;}
  .about-toc-link{padding:2px 0;color:var(--gold);cursor:pointer;}
  .about-nav-row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:14px;}
  .about-toc-link:hover{text-decoration:underline;}
  .about-step-title{font-weight:700;color:var(--text-soft);margin-bottom:4px;font-size:0.92rem;}
  @media (max-width:480px){
    .about-step{flex-direction:column;}
    .about-shot{width:150px;margin:0 auto;}
  }

  /* ── MOBILE TIGHTENING (phones, ~480px and under) ── */
  @media (max-width:480px){
    .page{padding:10px;}
    .card{padding:12px;margin-bottom:10px;}
    .modal-sheet{padding:16px 16px 24px;}

    /* Header: with a 15-char display name (the server-enforced max) and a
       3-digit token balance both showing at once, the row is tight on a
       360-390px phone. Shrinking the logo text/padding and the pills'
       font-size+padding buys back enough width that, combined with the
       flex-wrap fallback above, things fit on one line in the common case
       and wrap cleanly rather than overlap in the worst case. */
    header{padding:8px 10px;gap:5px 6px;}
    .header-logo-img{width:30px;height:30px;}
    .header-logo-text{font-size:1.02rem;}
    .header-logo-sub{font-size:0.56rem;}
    .header-right{gap:5px;}
    #headerRight{gap:6px;}
    .pill{font-size:0.74rem;padding:4px 10px;}
    .account-btn{font-size:0.72rem;padding:4px 9px;max-width:92px;}

    /* Reclaim vertical space on the home screen so more fits above the fold */
    .home-logo{padding:6px 0 4px;}
    .home-logo svg{width:80px;height:80px;}
    .home-logo h1{font-size:1.6rem;margin-top:2px;}
    .home-logo-tracker{font-size:1.1rem;letter-spacing:1.8px;}
    .home-logo .tagline{font-size:0.82rem;}
    .home-logo .subtitle{font-size:0.74rem;margin-top:4px;}

    /* Gameplay screen: shrink the stack above the table so the table itself is visible
       without scrolling on a typical phone viewport */
    .my-balls-card{padding:8px;}
    .bgp-name{font-size:0.78rem;}
    .tabs{margin-bottom:10px;padding:3px;}
    .tab{padding:6px 4px;font-size:0.78rem;}
    .table-felt{padding:10px;}
    .balls-grid{gap:6px 8px;}
    .ball{width:44px;height:44px;font-size:0.92rem;}

    /* Balls-per-player picker: the lobby/frame-over card has plenty of width, so keep
       the balls a comfortable tap size. Uses the same justify-content:space-between
       as desktop so the row always fills exactly whatever width the box gives it -
       no fixed gap to guess, and no risk of the 4th ball overflowing onto its own
       line on a narrower phone the way a fixed-px gap previously did. */
    .bp-balls-row{row-gap:8px;}
    .bp-ball{width:32px;height:32px;font-size:0.75rem;}
    .bp-max{height:32px;padding:0 10px;font-size:0.68rem;}
    /* The boxed frame's own padding (10px each side on desktop) eats into the
       already-tight mobile column width on top of the column's padding-right:14px,
       which is what pushed the 4th ball onto its own line. Tighten box padding and
       drop the column's right-padding (a desktop-only 50/50-split spacer, not needed
       once columns stack/shrink at this width) to claw that space back. */
    .bp-row{padding:6px 4px;}
    .bp-col{padding-right:0;}


    /* Podium + general text that can overflow narrow screens */
    .podium{gap:6px;}
    .podium-name{max-width:64px;font-size:0.72rem;}

    /* Status bar under the table — tighten rows so it takes less vertical space */
    .info-row{padding:4px 0;font-size:0.82rem;}
    .card h2{font-size:1.15rem;margin-bottom:8px;}
    .card h3{font-size:0.85rem;}
  }

/* Item (Ryan, 2026-07-05): browser/password-manager autofill (Chrome autofill,
   LastPass, etc.) injects its own UA background on the field — historically
   a pale yellow, but modern Chrome just forces plain white — which stomps
   right over the theme's --bg no matter what background/color rules already
   exist above. Only -webkit-box-shadow (an inset shadow the size of the
   whole field, drawn UNDER the browser's forced background layer) can push
   the theme color back in; a plain background/background-color override is
   not enough. -webkit-text-fill-color does the equivalent job for the text
   color, which -webkit-autofill also otherwise forces. Applies across all
   three themes since it reads the same --bg/--text variables every other
   input rule above already uses. The long transition delay is the standard
   trick to stop Chrome re-flashing its own background for a few seconds
   right after the field is autofilled.*/
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
  -webkit-box-shadow: 0 0 0 1000px var(--bg) inset !important;
  box-shadow: 0 0 0 1000px var(--bg) inset !important;
  -webkit-text-fill-color: var(--text) !important;
  caret-color: var(--text);
  transition: background-color 5000s ease-in-out 0s;
}

/* Feedback Inbox webmail page (Ryan, 2026-07-23) — two panes side by side on
   wide screens; on narrow screens collapse to a single column that swaps
   between the thread list and the reading pane (mail-app style), with the
   pane's "← Inbox" back button revealed. */
@media (max-width:760px){
  #feedbackInboxContainer{flex-direction:column;height:auto !important;min-height:70vh}
  #feedbackListPane{width:auto !important;flex-shrink:1 !important;border-right:none !important;border-bottom:1px solid var(--border);max-height:none !important}
  #feedbackReadingPane{max-height:none !important;min-height:60vh}
  #feedbackInboxContainer.reading-active #feedbackListPane{display:none}
  #feedbackInboxContainer:not(.reading-active) #feedbackReadingPane{display:none}
  #feedbackInboxContainer .fb-back-btn{display:inline-flex !important}
}
