/* DeNiro Card — rebuilt theme (dark card-room, gold accent) */
:root{
  --bg:#0e0f13;
  --bg2:#14161d;
  --panel:#171a22;
  --line:#262b36;
  --text:#f4f5f8;
  --muted:#9aa2b1;
  --gold:#d8b45a;
  --gold-deep:#b8963f;
  --felt:#16513a;
  --red:#e05656;
  --radius:14px;
}
*{box-sizing:border-box}
html,body{margin:0;touch-action:manipulation} /* manipulation: pan/pinch handled by pages, double-tap-zoom killed (native feel) */
/* Deep-purple "casino room" backdrop (the reference tables) — a faint damask-ish
   dot weave over a dark violet fall-off. Pure gradients: no images, no repaints. */
body{background:
    radial-gradient(1200px 600px at 50% -10%,rgba(124,82,186,.20),transparent 60%),
    repeating-radial-gradient(circle at 24% 18%,rgba(255,255,255,.022) 0 2px,transparent 2px 26px),
    repeating-radial-gradient(circle at 78% 64%,rgba(255,255,255,.015) 0 2px,transparent 2px 34px),
    linear-gradient(180deg,#231533,#130d1f);
  color:var(--text);
  font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;min-height:100vh;
  -webkit-text-size-adjust:100%}
a{color:inherit;text-decoration:none}
.wrap{max-width:1080px;margin:0 auto;padding:0 22px}

header{display:flex;align-items:center;justify-content:space-between;padding:calc(18px + env(safe-area-inset-top)) 22px 18px;max-width:1080px;margin:0 auto}
.brand{display:flex;align-items:center;gap:12px;font-weight:800;font-size:20px;letter-spacing:.01em}
/* Brand tile = the app icon look: gold DC-rose on rich royal blue (icons/dc-mark.png,
   derived from the 1024px AppIcon). Gold ring via box-shadow so nothing dims the tile. */
.brand .rose{width:42px;height:42px;border-radius:11px;flex:0 0 auto;
  background:#1E2C74 center/cover no-repeat url(icons/dc-mark.png);
  box-shadow:0 0 0 1px rgba(216,180,90,.55),0 2px 8px rgba(0,0,0,.4);
  font-size:0;line-height:0;overflow:hidden}
.brand b{color:var(--gold)}
.header-cta{display:flex;gap:10px}
.btn{border:0;cursor:pointer;font-weight:700;border-radius:10px;padding:10px 16px;font-size:14px;transition:filter .15s,transform .1s;-webkit-tap-highlight-color:transparent}
.btn-gold{background:linear-gradient(180deg,var(--gold),var(--gold-deep));color:#241c07;box-shadow:0 2px 10px rgba(216,180,90,.28)}
.btn-line{background:transparent;border:1px solid var(--line);color:var(--text)}
.btn-ghost{background:var(--bg2);color:var(--muted);border:1px solid var(--line)}
@media (hover:hover) and (pointer:fine){ .btn:hover{filter:brightness(1.06)} }
.btn:active{transform:translateY(1px)}
/* A disabled control must LOOK disabled and must not react — several games gray
   out Undo/Hint/Deal mid-turn and taps on them were still flashing/lifting. */
.btn:disabled,.btn[disabled]{opacity:.45;cursor:default;pointer-events:none;transform:none;filter:none}

.hero{text-align:center;padding:40px 22px 10px}
.hero .eyebrow{color:var(--gold);font-weight:700;letter-spacing:.16em;text-transform:uppercase;font-size:12px}
.hero h1{font-size:clamp(34px,6vw,58px);margin:.25em 0 .2em;font-weight:800;letter-spacing:-.02em}
.hero p{color:var(--muted);max-width:620px;margin:0 auto;font-size:18px}

.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:16px;padding:28px 0 60px}
.game{background:linear-gradient(180deg,var(--panel),#12141b);border:1px solid var(--line);border-radius:16px;padding:18px;position:relative;transition:.15s;display:flex;flex-direction:column;gap:8px;min-height:150px}
@media (hover:hover) and (pointer:fine){ .game:hover{border-color:var(--gold-deep);transform:translateY(-2px)} }
.game:active{border-color:var(--gold-deep)}
.game[role="button"]:focus-visible{outline:3px solid var(--gold);outline-offset:3px}
.game .suit{font-size:26px}
.game h3{margin:2px 0 0;font-size:18px}
.game p{margin:0;color:var(--muted);font-size:13px;flex:1}
.game .play{color:var(--gold);font-weight:700;font-size:14px}
.game.soon{opacity:.72}
.game .badge{position:absolute;top:12px;right:12px;font-size:10px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius:99px}
.badge.live{background:rgba(216,180,90,.16);color:var(--gold)}
.badge.soon{background:#20242e;color:var(--muted)}

footer{border-top:1px solid var(--line);color:var(--muted);text-align:center;padding:22px;font-size:14px}

/* ============ table (game screens) ============ */
.table{min-height:100vh;min-height:100dvh;display:flex;flex-direction:column}
.topbar{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:calc(11px + env(safe-area-inset-top)) 18px 11px;
  border-bottom:1px solid var(--line);background:rgba(14,15,19,.72);backdrop-filter:blur(8px);
  position:sticky;top:0;z-index:20}
.topbar .brand{font-size:17px}
.topbar .score{color:var(--muted);font-size:13px;font-weight:700}
.topbar .score b{color:var(--gold)}
/* Native app only: game top bar reads as a native nav bar (back + title), not a
   website header. Guarded by is-native-game (added by native.js on device); the
   public web build is untouched. */
html.is-native-game .topbar{justify-content:flex-start;gap:8px;min-height:52px}
html.is-native-game .topbar .brand.dn-back{display:inline-flex;align-items:center;gap:3px;flex:0 0 auto;padding:2px 2px 2px 0}
html.is-native-game .topbar .brand.dn-back .dn-back-chev{font-size:30px;line-height:1;color:var(--gold);font-weight:400}
html.is-native-game .topbar .brand.dn-back .dn-back-label{font-size:15px;color:var(--muted);font-weight:600}
html.is-native-game .topbar .dn-gtitle{margin-right:auto;font-weight:700;font-size:17px;
  min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.shell-actions{display:flex;gap:8px;align-items:center;flex:0 0 auto}
.shell-game-meta{color:var(--gold);font-size:12px;font-weight:800;letter-spacing:.04em;
  white-space:nowrap;padding:5px 8px;border-left:1px solid rgba(216,180,90,.24)}
/* ---- SLIM top bar for the non-felt (board / solo / 1v1) games ----
   html.dn-flat is set by orientForTable for every game NOT in the landscape-table
   set. These games keep a normal (non-floating) bar, but slimmed: the oversized
   42px brand tile shrinks, padding tightens, and the felt's content centres
   vertically (safe = never clips tall boards) so there's no wasted full-width bar
   or dead green at the bottom. */
html.dn-flat .topbar{padding-top:calc(6px + env(safe-area-inset-top));padding-bottom:6px;gap:9px}
html.dn-flat .topbar .brand{font-size:15px}
html.dn-flat .topbar .brand .rose{width:30px;height:30px;border-radius:8px}
html.is-native-game.dn-flat .topbar{min-height:42px}
html.is-native-game.dn-flat .topbar .brand.dn-back .dn-back-chev{font-size:26px}
html.is-native-game.dn-flat .topbar .dn-gtitle{font-size:16px}
html.dn-flat .felt{margin-top:6px;justify-content:safe center !important}
.shell-new-icon{display:none}
/* Portrait phones use one compact native navigation rail. Practice mode remains in
   Settings and table items in the Shop, so no feature is lost — but the old second
   row no longer wastes card-table space or makes the controls jump between games. */
@media (max-width:520px){
  html.is-native-game .topbar{flex-wrap:nowrap;gap:5px;
    padding:calc(5px + env(safe-area-inset-top)) 10px 5px;min-height:44px}
  html.is-native-game .topbar .brand.dn-back{min-width:64px}
  html.is-native-game .topbar .dn-gtitle{font-size:15px;flex:1 1 0;margin-right:0}
  html.is-native-game .shell-actions{gap:4px}
  html.is-native-game .shell-game-meta{font-size:10px;padding:4px 5px}
  html.is-native-game .shell-actions .btn{width:34px;height:34px;min-height:34px;padding:0;
    display:grid;place-items:center;font-size:16px;white-space:nowrap}
  /* "New" is much clearer than a lone circular arrow: the old icon resembled a
     page refresh and hid the fact that it deals a new match. */
  html.is-native-game .shell-actions .shell-new-game{width:auto;min-width:44px;padding:0 7px}
  html.is-native-game .shell-new-game .shell-new-icon{display:none}
  html.is-native-game .shell-new-game .shell-new-copy{display:inline;font-size:0}
  html.is-native-game .shell-new-game .shell-new-copy::before{content:"New";font-size:12px}
  html.is-native-game .dn-coinhud{margin-left:0;gap:0}
  html.is-native-game .dn-coin{min-height:34px;padding:4px 7px 4px 9px;font-size:12px}
  html.is-native-game .dn-coin .dn-coin-plus{width:14px;height:14px;font-size:11px}
}
/* On a narrow mobile browser, the DC tile already identifies the app. Drop the
   repeated DeNiro wordmark on GAME pages so the actual game title gets first
   claim on the rail. The lobby keeps its full brand. */
@media (max-width:420px){
  html:not(.is-native-game) .table .topbar .brand{
    flex:0 0 44px;min-width:44px;min-height:44px;justify-content:center;
    gap:0;font-size:0 !important}
  html:not(.is-native-game) .table .topbar .brand .rose{
    width:32px;height:32px;border-radius:8px}
  html:not(.is-native-game) .topbar .brand b{display:none}
  .table .topbar .dn-gtitle{
    flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;
    text-overflow:clip;font-size:15px}
  .table .topbar .dn-gtitle.dn-phone-title-priority{
    flex:0 0 auto;overflow:visible}
  .table .topbar .dn-gtitle.dn-phone-title-pitch{font-size:0}
  .table .topbar .dn-gtitle.dn-phone-title-pitch::before{
    content:"Pitch";font-size:15px}
}
/* On the narrowest native phones, the back chevron still has its explicit
   “Back to games” accessibility label.  Hiding only the repeated visual word
   gives long game names their full, readable title instead of “Gin Rum…”. */
@media (max-width:430px){
  html.is-native-game .topbar .brand.dn-back{min-width:30px}
  html.is-native-game .topbar .brand.dn-back .dn-back-label{display:none}
}
/* ---- Landscape: slim the top bar so the table reclaims the height ----------
   The portrait compaction above is gated on max-width:520px, which NEVER matches a
   wide landscape viewport — so landscape games ran the full ~65px bar and squished
   the felt. In landscape (dn-land, or native force-rotate dn-rot) collapse it to
   ~40px: tight padding, compact icon buttons, a "New" chip, drop the redundant web
   wordmark (native already shows a back button). One rule → every game. */
/* LANDSCAPE: no full-width bar at all. The top bar becomes a transparent, pointer-
   through OVERLAY and its controls float into the top CORNERS over the felt — brand+
   title top-left, ?/⚙/Round/New/coins top-right — so the freed centre-top is live
   felt and the table extends to the very top. Cards then grow (see the hand rule).
   One rule → all 18 games. Portrait keeps its normal bar. */
html.dn-land .topbar, html.dn-rot .topbar{
  position:absolute;top:0;left:0;right:0;z-index:30;background:transparent;
  border-bottom:none;backdrop-filter:none;box-shadow:none;min-height:0;
  justify-content:flex-start;align-items:flex-start;gap:6px;
  padding:calc(5px + env(safe-area-inset-top))
    max(10px,env(safe-area-inset-right)) 0
    max(10px,env(safe-area-inset-left));pointer-events:none;
  text-shadow:0 1px 3px rgba(0,0,0,.75)}
html.dn-land .topbar > *, html.dn-rot .topbar > *{pointer-events:auto}
html.dn-land.is-native-game .topbar, html.dn-rot.is-native-game .topbar{min-height:0}
/* right cluster: push actions + coins to the top-right, kept together */
html.dn-land .shell-actions, html.dn-rot .shell-actions{margin-left:auto;gap:3px;
  background:rgba(10,12,18,.5);border-radius:999px;padding:2px 5px;backdrop-filter:blur(6px)}
html.dn-land .dn-coinhud, html.dn-rot .dn-coinhud{margin-left:5px}
/* web: keep only the 🌹 home mark, drop the "DeNiro Card" wordmark (inline style → !important) */
html.dn-land:not(.is-native-game) .topbar .brand, html.dn-rot:not(.is-native-game) .topbar .brand{font-size:0 !important;gap:0;padding:2px}
html.dn-land:not(.is-native-game) .topbar .brand .rose, html.dn-rot:not(.is-native-game) .topbar .brand .rose{font-size:20px !important}
html.dn-land .topbar .dn-gtitle, html.dn-rot .topbar .dn-gtitle{font-size:14px;margin-right:0;font-weight:800}
html.dn-land .topbar .brand.dn-back .dn-back-chev, html.dn-rot .topbar .brand.dn-back .dn-back-chev{font-size:22px}
html.dn-land .topbar .brand.dn-back .dn-back-label, html.dn-rot .topbar .brand.dn-back .dn-back-label{font-size:13px}
html.dn-land .shell-game-meta, html.dn-rot .shell-game-meta{font-size:12.5px;padding:2px 4px;border-left:none}
html.dn-land .shell-actions .btn, html.dn-rot .shell-actions .btn{
  width:27px;height:27px;min-height:27px;padding:0;display:grid;place-items:center;font-size:14px;white-space:nowrap}
html.dn-land .shell-actions .shell-new-game, html.dn-rot .shell-actions .shell-new-game{width:auto;min-width:32px;padding:0 6px}
html.dn-land .shell-new-game .shell-new-copy, html.dn-rot .shell-new-game .shell-new-copy{font-size:0}
html.dn-land .shell-new-game .shell-new-copy::before, html.dn-rot .shell-new-game .shell-new-copy::before{content:"New";font-size:12.5px}
html.dn-land .dn-coin, html.dn-rot .dn-coin{min-height:27px;padding:2px 6px;font-size:12.5px}
html.dn-land .dn-coin .dn-coin-plus, html.dn-rot .dn-coin .dn-coin-plus{width:14px;height:14px;font-size:11px}
/* felt now fills to the very top since the bar floats */
html.dn-land .felt, html.dn-rot .felt{margin-top:2px}

/* Portrait-only web games use this in-flow note on short landscape phones.
   game-shell.js only creates it when that recommendation is relevant; keeping
   it in normal flow means it never covers cards or controls. */
.dn-portrait-rotate{
  position:static;display:flex;align-items:center;justify-content:center;gap:8px;
  flex:0 0 auto;min-height:38px;
  margin:6px max(10px,env(safe-area-inset-right)) 0 max(10px,env(safe-area-inset-left));
  padding:7px 12px;border:1px solid rgba(216,180,90,.42);border-radius:12px;
  background:rgba(14,15,19,.9);color:#f4e7bd;font-size:13px;font-weight:750;
  line-height:1.3;text-align:center}
.dn-portrait-rotate-icon{font-size:18px;line-height:1}

/* Coin HUD — 🪙 balance + Practice toggle, top-right of every native game bar. */
.dn-coinhud{margin-left:auto;display:flex;align-items:center;gap:8px;flex:0 0 auto}
.dn-coin{display:inline-flex;align-items:center;gap:5px;font-weight:800;font-size:14px;color:var(--gold);
  background:rgba(216,180,90,.14);border:1px solid rgba(216,180,90,.42);border-radius:999px;padding:5px 9px 5px 11px;cursor:pointer}
.dn-coin b{color:var(--gold);font-weight:800}
.dn-coin .dn-coin-plus{display:inline-grid;place-items:center;width:16px;height:16px;border-radius:50%;
  background:var(--gold);color:#241c07;font-weight:900;font-size:13px;line-height:1;margin-left:3px}
.dn-coin:active{transform:scale(.96)}
.dn-practice-pill{font-weight:700;font-size:12.5px;color:var(--muted);background:var(--bg2);
  border:1px solid var(--line);border-radius:999px;padding:5px 12px;cursor:pointer;white-space:nowrap}
.dn-practice-pill.on{color:#12241b;background:linear-gradient(180deg,#9fe6c4,#4bbf8b);border-color:transparent}
.dn-practice-pill:active{transform:scale(.96)}
html.dn-practice .dn-coin{opacity:.45}

/* The whole play surface is tap-driven: long-press text selection / the iOS
   callout sheet on a seat name or status line is the loudest "this is a web
   page" tell. Lobby/marketing copy (outside .felt/.table) keeps selection. */
.felt,.felt *{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}
/* Felt colors as vars so the Table color setting swaps ONE place.
   Default = the reference tables' deep red; html.dn-felt-green / -blue override. */
:root{--felt-hi:#b3222d;--felt-mid:#8c1a24;--felt-lo:#5f1017;--felt-line:#3d0a10;
  --felt-etch:rgba(0,0,0,.16)}
html.dn-felt-green{--felt-hi:#227a55;--felt-mid:#175c40;--felt-lo:#0d3123;--felt-line:#0a281d}
html.dn-felt-blue{--felt-hi:#1f4f8a;--felt-mid:#173d6c;--felt-lo:#0d2342;--felt-line:#091a31}
/* premium felts (coin-shop cosmetics) */
html.dn-felt-royal{--felt-hi:#6a3fa0;--felt-mid:#52307d;--felt-lo:#37205a;--felt-line:#241542}
html.dn-felt-noir{--felt-hi:#3a3d46;--felt-mid:#26282f;--felt-lo:#131418;--felt-line:#0a0b0e;--felt-etch:rgba(255,255,255,.08)}
html.dn-felt-emerald{--felt-hi:#1d8f5f;--felt-mid:#146b46;--felt-lo:#0a3d28;--felt-line:#072b1d;--felt-etch:rgba(216,180,90,.28)}
html.dn-felt-crimson{--felt-hi:#a51d2d;--felt-mid:#7d1522;--felt-lo:#520d16;--felt-line:#38080e;--felt-etch:rgba(216,180,90,.35)}
html.dn-felt-sovereign{--felt-hi:#8e6b1d;--felt-mid:#684c12;--felt-lo:#3c2b09;--felt-line:#241805;--felt-etch:rgba(255,238,171,.36)}
/* rooms (body backdrops) */
html.dn-room-midnight body{background:
  radial-gradient(900px 500px at 50% -10%,rgba(63,81,181,.18),transparent 60%),
  repeating-radial-gradient(circle at 30% 20%,rgba(255,255,255,.03) 0 1.5px,transparent 1.5px 42px),
  repeating-radial-gradient(circle at 72% 55%,rgba(255,255,255,.02) 0 1px,transparent 1px 34px),
  linear-gradient(180deg,#0e1220,#070910)}
html.dn-room-ocean body{background:
  radial-gradient(1100px 600px at 50% -10%,rgba(46,160,180,.22),transparent 62%),
  radial-gradient(700px 300px at 20% 108%,rgba(30,120,140,.25),transparent 70%),
  linear-gradient(180deg,#0c2e38,#071820)}
html.dn-room-velvet body{background:
  radial-gradient(1100px 600px at 50% -10%,rgba(170,60,80,.16),transparent 60%),
  repeating-radial-gradient(circle at 26% 22%,rgba(255,255,255,.02) 0 2px,transparent 2px 28px),
  repeating-radial-gradient(circle at 76% 66%,rgba(255,255,255,.016) 0 2px,transparent 2px 36px),
  linear-gradient(180deg,#2c1119,#170810)}
.felt{flex:1;margin:16px auto;max-width:1000px;width:calc(100% - 28px);
  background:
    /* etched center ring — the reference felt's medallion, as a background layer
       (NOT a positioned element: it must never re-anchor a game's absolute UI) */
    radial-gradient(ellipse 44% 27% at 50% 45%,transparent 61%,var(--felt-etch) 62.5%,var(--felt-etch) 64%,transparent 65.5%),
    radial-gradient(120% 90% at 50% 0%,rgba(255,255,255,.07),transparent 60%),
    radial-gradient(900px 560px at 50% 44%,var(--felt-hi),var(--felt-mid) 66%,var(--felt-lo));
  border:1px solid var(--felt-line);border-radius:26px;
  /* Keep the felt's depth, but not a heavy black rail around the cards.  The
     table should read as one open playing surface rather than a tray holding a
     pile of cards. */
  box-shadow:
    0 18px 38px rgba(0,0,0,.42),
    inset 0 0 0 2px rgba(0,0,0,.30),
    inset 0 0 0 5px rgba(255,255,255,.03),
    inset 0 0 90px rgba(0,0,0,.30);
  padding:22px 16px;display:flex;flex-direction:column;gap:14px}

.seats{display:flex;align-items:flex-start;justify-content:space-around;gap:8px;flex-wrap:wrap}
.seat{display:flex;flex-direction:column;align-items:center;gap:5px;min-width:88px;
  padding:9px 12px;border-radius:16px;transition:.18s;
  background:transparent;border:0}
/* The avatar's existing halo is enough to show a turn.  Keeping the whole seat
   transparent stops player labels from becoming dark rectangles on the felt. */
.seat.turn{background:transparent;box-shadow:none}
.seat .name{font-weight:800;color:#eafff3;font-size:14px;display:flex;align-items:center;gap:5px}
.seat .name::before{display:none}   /* legacy dot — replaced by the real .seat-av avatar */
/* dealer marker: a small gold "D" chip beside the dealer's name, all hand long */
.dn-dealer-badge{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
  width:17px;height:17px;border-radius:50%;background:linear-gradient(180deg,#f0d488,#c99a3e);
  color:#2a1c05;font-weight:900;font-size:11px;line-height:1;letter-spacing:0;
  box-shadow:0 1px 3px rgba(0,0,0,.45),inset 0 1px 0 rgba(255,255,255,.4);vertical-align:middle}
/* real circular avatar with the player's initial (injected by Shell.decorateSeats) */
.seat-av{width:44px;height:44px;border-radius:50%;display:grid;place-items:center;
  font-weight:900;font-size:18px;color:#fff;flex:0 0 auto;
  background:radial-gradient(circle at 34% 28%,var(--av1,#3a4560),var(--av2,#232a3d));
  border:2px solid rgba(255,255,255,.22);
  box-shadow:0 3px 8px rgba(0,0,0,.4),inset 0 1px 2px rgba(255,255,255,.28);
  text-shadow:0 1px 2px rgba(0,0,0,.45);transition:.18s}
.seat.turn .seat-av{border-color:#4be3a5;
  box-shadow:0 0 0 3px rgba(75,227,165,.45),0 0 16px rgba(75,227,165,.5)}
/* meta = score/bid line under the name — CLEAN text on the felt (no pill; the
   dark bubbles read as clutter — Jesse 2026-07-16), shadowed for legibility */
.seat .meta{color:#dff5ea;font-size:11.5px;font-weight:700;text-align:center;line-height:1.35;
  padding:2px 4px;text-shadow:0 1px 2px rgba(0,0,0,.9),0 0 10px rgba(0,0,0,.45)}
.seat .meta:empty{display:none;padding:0}
/* the bottom "you" seat holds the hand — keep it transparent, avatar inline with name */
.seat.seat-you{background:transparent;border:0;padding:6px 0}
.seat.seat-you .name{flex-direction:row;gap:8px}
.seat.seat-you .seat-av{width:34px;height:34px;font-size:15px;color:#2a2410;text-shadow:none}

.hand{display:flex;flex-wrap:wrap;align-items:flex-start;gap:7px;justify-content:center;min-height:calc(clamp(46px,13vw,66px) * 1.4)}
.hand.mine{margin-top:2px}
.center-row{display:flex;align-items:center;justify-content:center;gap:clamp(10px,4vw,26px);flex:1;flex-wrap:wrap;min-height:96px}
.pile{display:flex;flex-direction:column;align-items:center;gap:6px;color:#dff5ea;font-size:12px;font-weight:600}
.pile .lbl{color:#a9d8c2;text-transform:uppercase;letter-spacing:.06em;font-size:12px}
.msg{text-align:center;color:#eafff3;min-height:22px;font-weight:600;font-size:15px;padding:2px 8px;
  text-shadow:0 1px 2px rgba(0,0,0,.9),0 0 12px rgba(0,0,0,.45)}
.msg.win{color:var(--gold)}

/* ============ cards ============ */
.card{
  position:relative;
  /* Let type scale from the actual card width.  Landscape tables deliberately
     shrink cards; viewport-based type made a 10's index bigger than its card. */
  container-type:inline-size;
  width:clamp(52px,14.7vw,76px);
  /* a card NEVER flex-squeezes: in a nowrap hand row, overlap (fanHand's negative
     margins) is how many cards fit — flex-shrink was crushing 12-card hands to
     ~22px slivers on pages whose first render beats the fan pass */
  flex:0 0 auto;
  aspect-ratio:5 / 7;
  border-radius:8px;
  /* The fronts need to read as one intentional physical deck at both hand and
     hero sizes.  The old flat grey ramp made number cards look like raw HTML
     tiles on a bright table.  Keep the paper clean, but give it a cool ivory
     stock, a crisp edge and a very subtle inner press line. */
  background:linear-gradient(158deg,#ffffff 0%,#f8f9fc 54%,#e6eaf1 100%);
  color:#1a1c22;
  border:1px solid #b9c1ce;
  box-shadow:0 2px 6px rgba(0,0,0,.30),inset 0 0 0 1px rgba(255,255,255,.82),inset 0 0 0 3px rgba(26,31,43,.025);
  user-select:none;cursor:default;
  font-weight:800;line-height:1;
  transition:transform .12s ease,box-shadow .12s ease,filter .12s ease;
}
.card.red{color:#d8283f}
/* A hairline printed frame gives every front a polished deck identity without
   competing with pips or court art. Backs own their own pseudo-elements. */
.card:not(.back)::after{content:"";position:absolute;inset:3.5%;border:1px solid rgba(26,31,43,.10);
  border-radius:calc(8px - 2px);pointer-events:none;box-shadow:inset 0 0 0 1px rgba(255,255,255,.45)}
.card.red:not(.back)::after{border-color:rgba(216,40,63,.14)}
/* Your own hand is the one you actually have to READ — give it the biggest cards.
   The shell's fanHand() overlaps them to keep a 13-card hand on one row. */
.hand.mine .card{width:clamp(55px,15vw,82px);
  /* the fan arc: fanHand() sets --fanr/--fany per card; every lifted state below
     re-composes these vars so a tilted card lifts along its own angle */
  transform:rotate(var(--fanr,0deg)) translateY(var(--fany,0px))}
/* MINI cards: the sizing rule lives further down (.card.mini width + corner/pip
   fonts); the landscape table's .dn-seat .card !important rule overrides it there.
   A duplicate width here was dead in every context and got removed — don't re-add. */
.card .corner{position:absolute;display:flex;flex-direction:column;align-items:center;line-height:.82;z-index:2;
  min-width:1.45em;font-variant-numeric:tabular-nums}
/* The rank is the first thing a player should see in a fan. Big index + one
   oversized centre suit is deliberately clearer than a miniature pip diagram. */
.card .corner .r{font-size:clamp(16px,5.1vw,28px);font-weight:900;letter-spacing:-.09em}
.card .corner .s{font-size:clamp(12px,3.9vw,20px);margin-top:1px;font-family:Georgia,"Times New Roman",serif}
/* Tap-twice guard (Shell setting "confirmTaps"): the first tap LIFTS and rings the
   card, the second actually plays it — so a stray tap can't throw the wrong card. */
.hand.mine .card.armed{
  transform:rotate(var(--fanr,0deg)) translateY(calc(var(--fany,0px) - 14px)) scale(1.06);
  box-shadow:0 12px 24px rgba(0,0,0,.5),0 0 0 3px var(--gold),inset 0 0 0 1px rgba(255,255,255,.7);
  z-index:60 !important;
}
.card .corner.tl{top:6%;left:8%}
.card .corner.br{bottom:6%;right:8%;transform:rotate(180deg)}
.card .pip{position:absolute;inset:0;display:grid;place-items:center;
  font-size:clamp(38px,12.5vw,64px);opacity:.97;z-index:1;font-family:Georgia,"Times New Roman",serif}
.card .pip .ace{font-size:clamp(44px,14vw,72px)}
/* A hero card can carry a dramatic centre suit. In a 10–13 card fan that same
   glyph would overlap the next card's index and make the hand look like a pile.
   Keep the ranks big (they do the scanning), then use a still-obvious but calmer
   centre suit just for cards the player is holding. */
.hand.mine .card .pip:not(.face){font-size:clamp(31px,9vw,50px)}
.hand.mine .card .pip .ace{font-size:clamp(37px,10.5vw,58px)}
/* Face cards render a real court illustration (inline SVG from Cards.faceArt).
   Let that printed artwork use more of the card while the oversized tl/br indices
   remain above it: the former narrow inset made K/Q/J faces look undersized. */
.card .pip.face{inset:5% 4% 3%;display:block;font-size:0}
.card .pip.face svg{width:100%;height:100%;display:block}
/* Crisp VECTOR suit marks (Cards.suitSvg) replace the old font glyph on the corner
   pip AND a number card's centre suit — clubs/spades no longer render heavy/blobby.
   Sized in em off the existing font-size clamps so they inherit the responsive +
   held-hand fan shrink. Small clean corner pips, one clearly dominant centre suit;
   all three symbols kept, per Jesse. */
.card .corner .s{line-height:0}
.card .corner .s .suit-mark{width:.82em;height:.82em;display:block}
.card .pip:not(.face) .suit-mark{width:.92em;height:.92em;display:block}
/* COURT corner index — the ONLY thing visible on a fanned court (the next card covers
   the rest), so it must read as instantly as a number card's. The figure fills the card,
   so the dark corner suit used to land on the dark robe and vanish. Fix: give the index
   its own soft WHITE clearing (a radial fade, so no sticker edge) so the rank+suit always
   sit on WHITE, never on the figure/robe. Sized a touch bigger than a number pip (a court
   has no big centre suit to fall back on) but kept clean, not clunky. Scales via cqw below;
   this is the vw fallback. */
.card.court .corner{border-radius:9px;padding:0 4px 2px;
  background:radial-gradient(116% 104% at 50% 46%, #fff 58%, rgba(255,255,255,.72) 78%, rgba(255,255,255,0) 100%)}
.card.court .corner .r{font-size:clamp(15px,5vw,26px)}
.card.court .corner .s{font-size:clamp(13px,4.6vw,24px);margin-top:1px}
.card.court .corner .s .suit-mark{width:1.06em;height:1.06em}
.card.court.mini .corner{background:none;padding:0}   /* opponent mini stays plain + small */
.card.court.mini .corner .s{font-size:clamp(8px,2.6vw,12px)}
.card.dim{opacity:.5;filter:grayscale(.3)}

/* Fallback above keeps older WebViews usable. Current native shells support
   container units, which keep a small landscape card's indexes and centre suit
   in separate visual lanes instead of letting the 8/10 collide with the suit. */
@supports (width:1cqw){
  .card .corner .r{font-size:clamp(15px,38cqw,25px)}
  .card .corner .s{font-size:clamp(10px,25cqw,17px)}
  .card .pip:not(.face){inset:25% 17% 20%;font-size:clamp(25px,66cqw,53px);line-height:.84}
  .hand.mine .card .pip:not(.face){inset:26% 18% 21%;font-size:clamp(24px,62cqw,49px)}
  /* court corner suit scales with the card (proportional at every fanned size) */
  .card.court .corner .r{font-size:clamp(15px,40cqw,26px)}
  .card.court .corner .s{font-size:clamp(13px,34cqw,24px)}
}

/* Dense portrait hands become orderly rows before their corner indexes could
   overlap.  This is intentionally scoped to a class applied by Shell.fanHand;
   normal short hands still get the elegant one-row fan. */
.hand.mine.dn-hand-grid{flex-wrap:wrap;align-content:flex-end;column-gap:6px;row-gap:6px;padding:0 4px}
.hand.mine.dn-hand-grid .card{margin-left:0!important;transform:none!important}

/* premium card back: gold diamond-lattice over deep indigo + a filigree frame and
   centered emblem. Pure CSS (no images), scales down cleanly for mini/opponent backs. */
.card.back{
  background:
    repeating-linear-gradient(45deg, transparent 0 8px, rgba(216,180,90,.09) 8px 9px),
    repeating-linear-gradient(-45deg, transparent 0 8px, rgba(216,180,90,.09) 8px 9px),
    radial-gradient(125% 95% at 50% 0%, #2b3668, #1a2247 68%, #131a37);
  border-color:#0e1330;color:transparent;
  box-shadow:0 2px 6px rgba(0,0,0,.4),
    inset 0 0 0 2px rgba(216,180,90,.34),
    inset 0 0 0 3px rgba(0,0,0,.4),
    inset 0 0 0 4px rgba(216,180,90,.13)}
.card.back::after{content:"";position:absolute;inset:15% 21%;border-radius:6px;
  border:1px solid rgba(216,180,90,.42);
  background:radial-gradient(circle at 50% 50%, rgba(216,180,90,.16), transparent 62%)}
.card.back::before{content:"";position:absolute;top:50%;left:50%;width:24%;height:24%;
  transform:translate(-50%,-50%) rotate(45deg);border-radius:3px;
  background:linear-gradient(135deg, rgba(216,180,90,.6), rgba(216,180,90,.22));
  border:1px solid rgba(216,180,90,.55);box-shadow:0 0 8px rgba(216,180,90,.3)}

/* ---- purchasable card-back deck themes (coinShop → Decks) ----
   Equipped globally via html.dn-back-<id> (applyCosmetics). Each theme also has a
   .dn-bk-<id> card-level twin so the shop can render a live mini swatch of ANY
   theme regardless of what's equipped. 'classic' is the free default above.
   Pure CSS + inline SVG data-URIs — no image files, scales to .card.mini.back. */
/* Rose Noir — near-black lacquer, faint gold weave, DC-rose monogram in gold */
html.dn-back-rose-noir .card.back,:root .card.back.dn-bk-rose-noir{
  background:
    repeating-radial-gradient(circle at 50% 50%,rgba(216,180,90,.05) 0 1px,transparent 1px 9px),
    radial-gradient(125% 95% at 50% 0%,#26272f,#131419 62%,#0a0b0e);
  border-color:#000;
  box-shadow:0 2px 6px rgba(0,0,0,.5),
    inset 0 0 0 2px rgba(216,180,90,.5),
    inset 0 0 0 3px rgba(0,0,0,.55),
    inset 0 0 0 4px rgba(216,180,90,.16)}
html.dn-back-rose-noir .card.back::after,:root .card.back.dn-bk-rose-noir::after{
  inset:11% 15%;border-color:rgba(216,180,90,.55);background:none;border-radius:7px}
html.dn-back-rose-noir .card.back::before,:root .card.back.dn-bk-rose-noir::before{
  width:62%;height:56%;border:0;border-radius:0;box-shadow:none;
  transform:translate(-50%,-50%);
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 84'%3E%3Cg fill='none' stroke='%23d8b45a' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M40 30c3-4-1-8-5-6-5 3-4 10 2 12 8 3 16-3 14-11-3-9-14-12-21-6-8 7-6 19 3 24 10 5 21 1 24-8'/%3E%3Cpath d='M24 44c-7 2-11 7-12 13 7-1 12-5 15-10'/%3E%3Cpath d='M56 44c7 2 11 7 12 13-7-1-12-5-15-10'/%3E%3Cpath d='M40 46v14'/%3E%3C/g%3E%3Ctext x='40' y='78' text-anchor='middle' font-family='Georgia,serif' font-style='italic' font-weight='700' font-size='17' fill='%23d8b45a'%3EDC%3C/text%3E%3C/svg%3E") center/contain no-repeat}
/* Gold Royale — gold-dominant lattice with the rose etched in deep bronze */
html.dn-back-gold-royale .card.back,:root .card.back.dn-bk-gold-royale{
  background:
    repeating-linear-gradient(45deg,rgba(120,86,22,.14) 0 7px,transparent 7px 14px),
    repeating-linear-gradient(-45deg,rgba(120,86,22,.10) 0 7px,transparent 7px 14px),
    radial-gradient(125% 95% at 50% 0%,#f0d48a,#d8b45a 58%,#a87f2c);
  border-color:#7c5f1e;
  box-shadow:0 2px 6px rgba(0,0,0,.4),
    inset 0 0 0 2px rgba(74,54,10,.6),
    inset 0 0 0 4px rgba(255,246,216,.55)}
html.dn-back-gold-royale .card.back::after,:root .card.back.dn-bk-gold-royale::after{
  inset:12% 16%;border-color:rgba(74,54,10,.55);
  background:radial-gradient(circle at 50% 50%,rgba(255,246,216,.25),transparent 62%)}
html.dn-back-gold-royale .card.back::before,:root .card.back.dn-bk-gold-royale::before{
  width:60%;height:56%;border:0;border-radius:0;box-shadow:none;
  transform:translate(-50%,-50%);
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80'%3E%3Cg fill='none' stroke='%23503c0c' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M40 32c3-4-1-8-5-6-5 3-4 10 2 12 8 3 16-3 14-11-3-9-14-12-21-6-8 7-6 19 3 24 10 5 21 1 24-8'/%3E%3Cpath d='M24 46c-7 2-11 7-12 13 7-1 12-5 15-10'/%3E%3Cpath d='M56 46c7 2 11 7 12 13-7-1-12-5-15-10'/%3E%3Cpath d='M40 48v16'/%3E%3C/g%3E%3C/svg%3E") center/contain no-repeat}
/* Crimson Velvet — quilted deep-red velvet with a gold tufted button center */
html.dn-back-crimson-velvet .card.back,:root .card.back.dn-bk-crimson-velvet{
  background:
    repeating-linear-gradient(45deg,transparent 0 9px,rgba(0,0,0,.28) 9px 10px),
    repeating-linear-gradient(-45deg,transparent 0 9px,rgba(0,0,0,.28) 9px 10px),
    radial-gradient(125% 95% at 50% 0%,#b02237,#7d1522 62%,#4a0c13);
  border-color:#320710;
  box-shadow:0 2px 6px rgba(0,0,0,.45),
    inset 0 0 0 2px rgba(216,180,90,.5),
    inset 0 0 0 3px rgba(0,0,0,.4),
    inset 0 0 0 4px rgba(216,180,90,.14)}
html.dn-back-crimson-velvet .card.back::after,:root .card.back.dn-bk-crimson-velvet::after{
  inset:14% 19%;border-color:rgba(216,180,90,.5);
  background:radial-gradient(circle at 50% 42%,rgba(255,220,150,.14),transparent 60%)}
html.dn-back-crimson-velvet .card.back::before,:root .card.back.dn-bk-crimson-velvet::before{
  width:20%;height:20%;box-shadow:0 0 10px rgba(216,180,90,.45)}
/* Emerald Lux — deep-green art-deco: gold scallop arcs rising from the foot */
html.dn-back-emerald-lux .card.back,:root .card.back.dn-bk-emerald-lux{
  background:
    repeating-radial-gradient(circle at 50% 130%,rgba(216,180,90,.18) 0 2px,transparent 2px 13px),
    repeating-linear-gradient(90deg,transparent 0 10px,rgba(255,255,255,.045) 10px 11px),
    radial-gradient(125% 95% at 50% 0%,#1e9161,#146b46 60%,#062c1c);
  border-color:#042315;
  box-shadow:0 2px 6px rgba(0,0,0,.45),
    inset 0 0 0 2px rgba(216,180,90,.5),
    inset 0 0 0 3px rgba(0,0,0,.4),
    inset 0 0 0 4px rgba(216,180,90,.14)}
html.dn-back-emerald-lux .card.back::after,:root .card.back.dn-bk-emerald-lux::after{
  inset:13% 17%;border-color:rgba(216,180,90,.5);
  background:radial-gradient(circle at 50% 50%,rgba(216,180,90,.12),transparent 62%)}
html.dn-back-emerald-lux .card.back::before,:root .card.back.dn-bk-emerald-lux::before{
  width:22%;height:22%;
  background:linear-gradient(135deg,#eede9d,#c99b2e);border-color:rgba(255,246,216,.7);
  box-shadow:0 0 10px rgba(216,180,90,.5),inset 0 0 0 2px rgba(6,44,28,.35)}

.card.playable{cursor:pointer;outline:2px solid var(--gold);outline-offset:0;
  box-shadow:0 0 0 2px rgba(216,180,90,.35),0 6px 16px rgba(216,180,90,.4)}
.card[role="button"]:focus-visible{outline:3px solid #fff;outline-offset:3px;z-index:110 !important;
  box-shadow:0 0 0 6px rgba(216,180,90,.82),0 8px 20px rgba(0,0,0,.48)}
.card.playable:active{transform:translateY(-7px)}
.hand.mine .card.playable:active{transform:rotate(var(--fanr,0deg)) translateY(calc(var(--fany,0px) - 8px))}
/* Hover lift ONLY where a real pointer hovers: on iOS/Android WebViews the
   emulated :hover STICKS after a tap, leaving the played-from spot's neighbour
   floating 14px up until the next tap. Touch keeps :active feedback instead. */
@media (hover:hover) and (pointer:fine){
  .card.playable:hover{transform:translateY(-14px)}
  .hand.mine .card.playable:hover{transform:rotate(var(--fanr,0deg)) translateY(calc(var(--fany,0px) - 14px))}
  .hand .card:hover{z-index:100 !important}
  .hand .card.playable:hover{box-shadow:0 0 0 2px rgba(216,180,90,.5),0 14px 26px rgba(0,0,0,.5)}
}
/* in an overlapping fan, the touched card must jump above its neighbours */
.hand .card{transition:transform .14s ease,box-shadow .14s ease,filter .14s ease}
.hand .card:active{z-index:100 !important}

.card.mini{width:clamp(30px,8vw,42px);border-radius:6px}
.card.mini .corner .r{font-size:clamp(8px,2.4vw,11px)}
.card.mini .corner .s{font-size:clamp(7px,1.9vw,9px)}
.card.mini .pip{font-size:clamp(13px,4vw,18px)}

/* compact tables on small screens: keep the opponent chips in ONE row */
@media (max-width:520px){
  /* Web tables use the same compact rail as the native build: no game page may
     create a sideways scroll just because Back/title/New game share one line. */
  .topbar{padding:8px 10px;gap:6px;overflow:hidden}
  .topbar .brand{font-size:15px;white-space:nowrap}
  .topbar > .brand + div{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .topbar .shell-actions{margin-left:auto;gap:4px}
  .topbar .shell-actions .btn{padding:7px 8px;font-size:13px;white-space:nowrap}
  .topbar .shell-new-game .shell-new-copy{font-size:0}
  .topbar .shell-new-game .shell-new-copy::before{content:"New";font-size:13px}
  .hand{gap:4px}
  .felt{padding:14px 8px;border-radius:18px}
  .seats{flex-wrap:nowrap;gap:6px}
  .seat{min-width:0;padding:7px 7px;gap:3px;border-radius:12px}
  .seat-av{width:34px;height:34px;font-size:14px;border-width:2px}
  .seat .name{font-size:12px;gap:3px}
  .seat .meta{font-size:12px;padding:2px 6px;line-height:1.25}
  .seat.seat-you .seat-av{width:30px;height:30px}
}

.controls{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;padding:4px 0}
.controls .btn{padding:11px 18px}
.suit-pick{display:flex;gap:10px;justify-content:center;flex-wrap:wrap}
.suit-pick button{width:52px;height:52px;font-size:24px;border-radius:12px;border:1px solid var(--line);
  background:linear-gradient(180deg,#fff,#eef0f5);cursor:pointer;transition:transform .1s,box-shadow .15s}
@media (hover:hover) and (pointer:fine){
  .suit-pick button:hover{transform:translateY(-2px);box-shadow:0 6px 14px rgba(0,0,0,.3)}
}
.suit-pick button:active{transform:translateY(1px)}
.suit-pick button.red{color:#d8283f}

.bid-pick{display:flex;gap:8px;justify-content:center;flex-wrap:wrap}
.bid-pick button{min-width:44px;height:44px;border-radius:11px;border:1px solid var(--line);
  background:var(--bg2);color:var(--text);font-weight:800;cursor:pointer;transition:.12s}
@media (hover:hover) and (pointer:fine){
  .bid-pick button:hover{border-color:var(--gold);color:var(--gold)}
}
.bid-pick button:active{border-color:var(--gold);color:var(--gold)}

/* ---- Combined bid pop-up (Shell.bidPanel) — one panel matching the reference
   Bid Whist bid pop-up: number row [+ direction row] + BID/PASS + countdown ring.
   Options at/below the current high bid are greyed and unselectable. Shared by
   every bidding game so the bid UX is identical on web / iOS / Android. ---- */
.dn-bidpanel{display:flex;flex-direction:column;align-items:center;gap:9px;
  background:linear-gradient(180deg,rgba(26,22,36,.975),rgba(13,11,19,.985));
  border:1px solid rgba(226,196,120,.38);border-radius:18px;padding:15px 18px 17px;
  box-shadow:0 16px 44px rgba(0,0,0,.6);position:relative;max-width:min(94vw,600px);margin:0 auto}
.dn-bidpanel .dn-bp-prompt{color:#eafff3;font-weight:700;font-size:14px;text-align:center;opacity:.95;line-height:1.3;margin-bottom:1px}
.dn-bidpanel .dn-bp-row{display:flex;gap:7px;justify-content:center;flex-wrap:wrap}
.dn-bidpanel .dn-bp-opt{min-width:46px;height:46px;padding:0 13px;border-radius:11px;
  border:1px solid rgba(255,255,255,.2);background:rgba(255,255,255,.07);color:#eef;
  font-weight:800;font-size:16px;cursor:pointer;transition:.12s;letter-spacing:.3px;line-height:1}
.dn-bidpanel .dn-bp-dir{font-size:13px;letter-spacing:.6px;min-width:92px}
.dn-bidpanel .dn-bp-opt.sel{background:#2f6df6;border-color:#7ea3ff;color:#fff;box-shadow:0 0 0 2px rgba(47,109,246,.45)}
.dn-bidpanel .dn-bp-opt.disabled{opacity:.3;filter:grayscale(.65);cursor:not-allowed;pointer-events:none}
@media (hover:hover) and (pointer:fine){ .dn-bidpanel .dn-bp-opt:not(.disabled):not(.sel):hover{border-color:var(--gold);color:var(--gold)} }
.dn-bidpanel .dn-bp-toggle{border:1px dashed rgba(226,196,120,.55);background:transparent;color:#e2c478;
  border-radius:10px;height:34px;padding:0 13px;font-weight:700;font-size:12.5px;cursor:pointer}
.dn-bidpanel .dn-bp-actions{display:flex;gap:11px;margin-top:3px}
.dn-bidpanel .dn-bp-bid,.dn-bidpanel .dn-bp-pass{height:46px;min-width:104px;border-radius:12px;
  font-weight:800;font-size:15px;cursor:pointer;border:none;letter-spacing:.6px}
.dn-bidpanel .dn-bp-bid{background:#2fae5e;color:#052e15}
.dn-bidpanel .dn-bp-bid.disabled{background:#39473f;color:#8aa295;cursor:not-allowed;pointer-events:none;opacity:.75}
.dn-bidpanel .dn-bp-pass{background:#c0392b;color:#fff}
.dn-bidpanel .dn-bp-ring{position:absolute;top:-15px;right:-13px;width:38px;height:38px;pointer-events:none}
.dn-bidpanel .dn-bp-ring svg{width:38px;height:38px;transform:rotate(-90deg)}
.dn-bidpanel .dn-bp-ring circle{fill:none;stroke-width:3.4}
.dn-bidpanel .dn-bp-ring circle.bg{stroke:rgba(255,255,255,.16)}
.dn-bidpanel .dn-bp-ring circle.fg{stroke:#e2c478;stroke-linecap:round;stroke-dasharray:100.53;
  animation:dn-bp-count linear forwards}
@keyframes dn-bp-count{from{stroke-dashoffset:0}to{stroke-dashoffset:100.53}}
/* the panel carries its own surface — neutralise the container's landscape pill */
#bidbar:has(.dn-bidpanel),.bid-pick:has(.dn-bidpanel){background:transparent!important;border:none!important;
  box-shadow:none!important;backdrop-filter:none!important;padding:0!important}
/* keep the pop-up's OWN button sizing in landscape (win over the compact
   .bid-pick/#bidbar button rules) and let it breathe in the centre band */
html.dn-land .felt.dn-table .dn-bidpanel{margin:6px auto 0;padding:11px 14px 13px;gap:7px}
html.dn-land .felt.dn-table .dn-bidpanel .dn-bp-opt{min-width:42px;height:40px;font-size:15px;padding:0 11px}
html.dn-land .felt.dn-table .dn-bidpanel .dn-bp-dir{min-width:86px;font-size:12.5px}
html.dn-land .felt.dn-table .dn-bidpanel .dn-bp-bid,
html.dn-land .felt.dn-table .dn-bidpanel .dn-bp-pass{height:40px;min-width:96px;font-size:14px}
html.dn-land .felt.dn-table .dn-bidpanel .dn-bp-prompt{font-size:12.5px}

.hidden{display:none}

/* entrance for dealt hands — a card slides up from the deck and settles INTO its
   fan position. Landing on the fan (rotate/translateY vars) instead of `none` matters
   because animation-fill-mode:both holds the last keyframe: a flat `transform:none`
   here was overriding fanHand's arc, leaving 13-card hands packed flat until a
   re-render. Non-fanned hands (vars unset → 0deg/0px) still settle upright. */
@keyframes dealIn{0%{opacity:0;transform:translateY(26px) scale(.86) rotate(-4deg)}
  60%{opacity:1}100%{opacity:1;transform:rotate(var(--fanr,0deg)) translateY(var(--fany,0px))}}
.hand .card{animation:dealIn .34s cubic-bezier(.22,.68,.28,1) both}
/* cards kept across a re-render (Shell.syncHand) never replay the entrance;
   genuinely new cards get their stagger via inline animation-delay */
.hand .card.dealt{animation:none}
/* reveal flip — applied by Shell.syncHand when a back card turns face up,
   or by games flipping a card in place */
@keyframes cardFlip{0%{transform:rotateY(90deg) scale(.96);opacity:.4}100%{transform:none;opacity:1}}
.card.card-flip,.hand .card.card-flip{animation:cardFlip .32s ease both}
/* pass-selection highlight (hearts) — class-based so syncHand carries it over */
.card.pass-sel{outline:3px solid #e11d48;outline-offset:0}

/* ============ shared shell (game-shell.js) ============ */
.shell-gear{font-size:16px;padding:9px 12px;line-height:1}

/* toast */
.shell-toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%) translateY(20px);
  background:rgba(14,16,22,.62);color:#fff;border:1px solid rgba(255,255,255,.08);border-radius:999px;
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  padding:9px 16px;font-weight:600;font-size:13.5px;z-index:120;opacity:0;pointer-events:none;
  transition:opacity .2s,transform .2s;max-width:86vw;text-align:center;box-shadow:0 6px 20px rgba(0,0,0,.35)}
.shell-toast.show{opacity:1;transform:translateX(-50%) translateY(0)}

/* modal / settings drawer */
.shell-modal-back{position:fixed;inset:0;background:rgba(6,7,10,.6);backdrop-filter:blur(4px);
  display:grid;place-items:center;z-index:130;padding:20px;animation:fadeIn .15s ease}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
.shell-modal{background:linear-gradient(180deg,var(--panel),#12141b);border:1px solid var(--line);
  border-radius:18px;padding:22px;width:min(380px,92vw);box-shadow:0 20px 60px rgba(0,0,0,.55);
  /* cap to the viewport and scroll internally so a long tutorial / rules / breakdown
     never pushes its action buttons off-screen (esp. in the short landscape table view). */
  max-height:calc(100dvh - 36px);overflow-y:auto;-webkit-overflow-scrolling:touch;
  animation:popIn .18s cubic-bezier(.2,.8,.3,1.1)}
@keyframes popIn{from{opacity:0;transform:translateY(10px) scale(.97)}to{opacity:1;transform:none}}
.shell-modal h3{margin:0 0 14px;font-size:20px}
.deniro-age-notice .shell-modal{max-width:400px}
.deniro-age-copy{margin:0;color:var(--muted);font-size:15px;line-height:1.55}
.deniro-age-actions{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:18px}
.deniro-age-actions .btn{text-decoration:none}
.shell-rows{display:flex;flex-direction:column;gap:4px}
.shell-row{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:11px 4px;border-bottom:1px solid rgba(255,255,255,.05);font-size:15px;font-weight:600}
.shell-switch{width:46px;height:27px;border-radius:99px;border:1px solid var(--line);background:#20242e;
  position:relative;cursor:pointer;transition:.16s;flex:0 0 auto}
.shell-switch::after{content:"";position:absolute;top:2px;left:2px;width:21px;height:21px;border-radius:50%;
  background:#8b93a3;transition:.16s}
.shell-switch.on{background:rgba(216,180,90,.3);border-color:var(--gold-deep)}
.shell-switch.on::after{left:21px;background:var(--gold)}
.shell-select{background:#20242e;color:var(--text);border:1px solid var(--line);border-radius:9px;
  padding:8px 10px;font-weight:600;font-size:14px;cursor:pointer}

/* result overlay */
.shell-result{text-align:center}
.shell-result-emoji{font-size:52px;margin-bottom:4px;display:inline-block}
/* the trophy pops + glows on a win */
.shell-result.win .shell-result-emoji{animation:trophyPop .6s cubic-bezier(.2,.9,.3,1.5) both;
  filter:drop-shadow(0 0 16px rgba(240,212,138,.75))}
@keyframes trophyPop{0%{transform:scale(0) rotate(-22deg)}55%{transform:scale(1.28) rotate(7deg)}100%{transform:scale(1) rotate(0)}}
.shell-result h3{font-size:26px;margin:2px 0}
.shell-result.win h3{background:linear-gradient(90deg,#f0d48a,#fff6d8,#e0b34e);-webkit-background-clip:text;
  background-clip:text;-webkit-text-fill-color:transparent}
.shell-result p{color:var(--muted);margin:6px 0 16px;font-size:15px;white-space:pre-line}

/* confetti + coin rain (win celebration) */
.shell-confetti{position:fixed;inset:0;pointer-events:none;z-index:140;overflow:hidden}
.shell-confetti i{position:absolute;top:-16px;width:9px;height:14px;border-radius:2px;opacity:.95;
  animation:confFall 2.8s linear forwards}
@keyframes confFall{to{transform:translateY(108vh) rotate(680deg);opacity:.7}}
.shell-confetti .coin{position:absolute;top:-30px;border-radius:50%;opacity:0;
  background:radial-gradient(circle at 34% 30%,#fff2c4,#e8bd57 58%,#b8862a);
  box-shadow:inset 0 0 0 2px rgba(255,240,190,.6),inset 0 -3px 5px rgba(120,80,20,.5),0 3px 7px rgba(0,0,0,.4);
  animation:coinFall 2.4s cubic-bezier(.3,.1,.5,1) forwards}
@keyframes coinFall{0%{transform:translateY(0) rotateY(0);opacity:0}
  9%{opacity:1}90%{opacity:1}100%{transform:translateY(114vh) rotateY(900deg);opacity:.6}}

/* turn indicator + scoreboard bits games can use */
.turn-dot{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--gold);
  margin-left:6px;box-shadow:0 0 8px var(--gold);animation:pulseDot 1s infinite}
@keyframes pulseDot{0%,100%{opacity:.4}50%{opacity:1}}
.card.hint{outline:2px dashed var(--gold);outline-offset:2px;animation:hintPulse 1s ease infinite}
@keyframes hintPulse{0%,100%{outline-color:rgba(216,180,90,.4)}50%{outline-color:rgba(216,180,90,1)}}

/* System reduced-motion is a release-wide contract, not a per-game courtesy.
   Suppress entrances, flips, modal/trophy motion, confetti, and continuous
   status pulses while leaving the final informational state fully visible. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    scroll-behavior:auto!important;
    transition-duration:.01ms!important;
    transition-delay:0s!important;
    animation-duration:.01ms!important;
    animation-delay:0s!important;
    animation-iteration-count:1!important}
  .shell-confetti{display:none!important}
  .hand .card,.card.card-flip,.hand .card.card-flip,
  .shell-modal-back,.shell-modal,.shell-result.win .shell-result-emoji,
  .turn-dot,.card.hint,.mp-timer.crit{
    animation:none!important}
  .hand .card{opacity:1}
}
.bagcount{display:inline-flex;align-items:center;gap:4px;font-size:12px;color:#bfe9d5}
.scorebar{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;font-size:13px;font-weight:700;color:#dff5ea}
.scorebar .me{color:var(--gold)}
.breakdown{background:rgba(0,0,0,.25);border:1px solid rgba(255,255,255,.08);border-radius:12px;
  padding:12px 14px;margin:8px auto;max-width:460px;font-size:13px;line-height:1.7;color:#eafff3}
.breakdown b{color:var(--gold)}

/* Joker cards use the same detailed printed treatment as the courts. Big Joker
   is crimson/gold; Little Joker is navy/silver. The J + star indices stay clear
   in a hand while the name prints along the portrait edge. */
.card.joker{background:linear-gradient(158deg,#fffdf5,#f2ead2);border-color:#d9c48a}
.card.joker.big .corner{color:#c6253d}
.card.joker:not(.big) .corner{color:#354c83}
.card.joker .pip.joker-pip{opacity:1}
.card.joker.big{box-shadow:0 2px 6px rgba(0,0,0,.32),inset 0 0 0 2px rgba(216,40,63,.5)}
.card.joker:not(.big){box-shadow:0 2px 6px rgba(0,0,0,.32),inset 0 0 0 2px rgba(150,150,160,.5)}

/* Engagement: win-streak badge + warm-up note (added by the engagement layer) */
.shell-streak{ display:inline-block; margin:6px auto 2px; padding:5px 12px; border-radius:999px;
  background:linear-gradient(90deg,#e0565622,#d8b45a33); color:#f0d48a; font-weight:800;
  letter-spacing:.4px; font-size:13px; }
.shell-note{ margin:8px 2px 0; font-size:12px; line-height:1.45; color:var(--muted,#9aa2b1); opacity:.85; }


/* ============================================================================
   Native landscape "REAL TABLE" — partner across the top, opponents left &
   right, YOU at the bottom, trick in the middle of an oval felt (matches the
   reference tables). Guarded by is-native-game (native app only) + landscape +
   the .dn-table tag game-shell.js adds ONLY to the 4-seat trick games.
   Portrait and the public web are completely unaffected.
   ============================================================================ */
/* Force-landscape: when a table game opens in portrait on the device, the webview
   won't honor screen.orientation.lock — so game-shell.js adds html.dn-rot and we
   rotate the WHOLE view 90°. Rotating <body> (not just .table) makes position:fixed
   modals — the age gate, settings drawer, toasts — relative to the rotated body, so
   they stay centered in the landscape frame instead of drifting off. */
html.dn-rot, html.dn-rot body { overflow: hidden; margin: 0; }
html.dn-rot body {
  position: fixed; top: 0; left: 100vw;
  width: 100vh; height: 100vw;
  transform-origin: 0 0; -webkit-transform-origin: 0 0;
  transform: rotate(90deg); -webkit-transform: rotate(90deg);
}
html.dn-rot .table { height: 100%; }

html.dn-land .table { height: 100dvh; overflow: hidden; }
html.dn-rot.dn-land .table { height: 100%; }
html.dn-land .felt.dn-table {
  position: relative; flex: 1; min-height: 0;
  /* Give the usable table back to the game.  The prior 90px/104px rails made a
     short landscape phone look like a giant empty oval with a tiny game inside. */
  max-width: none; width: calc(100% - 4px);
  margin: 4px auto; padding: 72px 16px 88px;
  border-radius: 36% / 28%;
  /* overflow VISIBLE on purpose: the felt's oval look comes from its own rounded
     background + inset shadows (clipped to border-radius automatically), but the
     pinned hand must be able to sit over the bottom rail WITHOUT being clipped to
     the oval curve — overflow:hidden here cut the bottoms off the cards. */
  overflow: visible;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  /* viewport-fit=cover (all pages, 2026-07-15) lets the table extend under the
     notch in real landscape — keep the side seats out from under it. max() keeps
     the old 18px on notchless devices; dn-rot (CSS-rotated portrait) unaffected. */
  padding-left: max(18px, env(safe-area-inset-left));
  padding-right: max(18px, env(safe-area-inset-right));
}
/* the West/North/East wrapper dissolves so its seats float on the oval rail */
html.dn-land .felt.dn-table .dn-seats-wrap { display: contents; }
html.dn-land .felt.dn-table .dn-seat {
  position: absolute; z-index: 6; margin: 0; min-width: 0;
  /* hard cap so an opponent's growing card-back fan (crazy-eights/go-fish/old-maid/
     tonk/gin) can't balloon the chip across the centre pile — fanHand then overlaps
     the backs to fit this width. Trick-game text seats are far narrower, unaffected. */
  max-width: 22vw; max-height: 30vh; overflow: hidden;
  /* NO chip box on the landscape table (Jesse 2026-07-16: clean felt) — avatars,
     names and fans float directly on the table; the size caps above still apply. */
  background: transparent; border: 0; border-radius: 13px; padding: 4px 8px; gap: 1px;
}
/* an opponent's card-backs in a seat chip are just a "how many cards" indicator — render
   them MINI so the chip stays a compact tile and never drops its full-height fan down
   into the centre play area (the stock/discard the human must read). */
html.dn-land .felt.dn-table .dn-seat .hand { align-items: center; }
html.dn-land .felt.dn-table .dn-seat .card {
  /* 16px backs read as dark slivers on a real phone, especially in Crazy Eights.
     These are still a compact count indicator, but are now visibly a branded deck
     rather than visual noise around the table. */
  width: clamp(22px, 3.4vw, 32px) !important; height: auto !important;
  /* the flex hand is align-items:stretch by default, which was stretching these
     narrow backs to the hand's full height (18x92 skinny slivers). Pin the card to its
     own aspect-ratio height and don't let it stretch. */
  align-self: center !important; aspect-ratio: 5 / 7;
  border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,.4);
}
html.dn-land .felt.dn-table .dn-seat .name { font-size: 12.5px; }
html.dn-land .felt.dn-table .dn-seat .meta { font-size: 12.5px; padding: 0 5px; margin-top: 0; }
html.dn-land .felt.dn-table .dn-seat .seat-av { width: 28px; height: 28px; font-size: 13px; }
html.dn-land .felt.dn-table .dn-dealer-badge {
  width:18px;height:18px;font-size:12.5px}
html.dn-land .felt.dn-table .dn-seat-top   { top: 22px; left: 50%; transform: translateX(-50%); }
html.dn-land .felt.dn-table .dn-seat-left  { left: max(6px,env(safe-area-inset-left));  top: 34%; transform: translateY(-50%); }
html.dn-land .felt.dn-table .dn-seat-right { right: max(6px,env(safe-area-inset-right)); top: 34%; transform: translateY(-50%); }
/* central play column (trick / message / bid picker) sits BETWEEN the side seats */
html.dn-land .felt.dn-table .dn-center,
html.dn-land .felt.dn-table .msg,
html.dn-land .felt.dn-table .bid-pick,
html.dn-land .felt.dn-table .breakdown,
html.dn-land .felt.dn-table .lasttrick { max-width: 60%; margin-left: auto; margin-right: auto; }
/* the trick area must COLLAPSE when empty (e.g. during bidding) — its per-game inline
   min-height:112px otherwise reserves a big empty band that flex then steals from the
   message + hand, crushing them. !important beats the inline min-height; the trick still
   grows to fit real cards when they're played. */
html.dn-land .felt.dn-table .dn-center { margin-top: 1.6vh; z-index: 3; min-height: 0 !important; }
/* A live trick is a four-seat conversation, not a horizontal pile.  The cards
   occupy the side they came from so West/North/East stay legible even while the
   cards overlap toward the middle.  During bidding the container remains calm
   and compact because this only turns on once a directional card is present. */
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) {
  position:relative;display:block;flex:0 0 clamp(132px,34vh,230px);
  width:min(60vw,600px);margin:0 auto;min-height:clamp(132px,34vh,230px)!important;
}
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-0,
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-1,
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-2,
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-3 {
  position:absolute;z-index:4;margin:0;gap:2px;pointer-events:none;
}
/* Overlapping center cluster: every played card sits near the MIDDLE, nudged
   toward the seat that played it, so position alone tells you who played what
   ("further left = the player on my left"). Cards LAYER by play order — the first
   card played sits on the bottom, each later play stacks on top (DOM order = play
   order gives that for free, since all four share the same z-index). A small tilt
   makes it read like a real tossed pile rather than a neat grid. */
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-0 {left:50%;top:50%;bottom:auto;transform:translate(-50%,calc(-50% + 28px)) rotate(var(--dn-tilt,0deg))}
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-1 {left:50%;right:auto;top:50%;transform:translate(calc(-50% - 26px),-50%) rotate(var(--dn-tilt,0deg))}
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-2 {left:50%;top:50%;transform:translate(-50%,calc(-50% - 28px)) rotate(var(--dn-tilt,0deg))}
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-3 {left:50%;right:auto;top:50%;transform:translate(calc(-50% + 26px),-50%) rotate(var(--dn-tilt,0deg))}
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-0 .card,
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-1 .card,
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-2 .card,
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .dn-trick-seat-3 .card {width:clamp(56px,9vw,112px)!important}
/* drop the per-card name labels in the overlapping pile — the seat-offset is the label */
html.dn-land .felt.dn-table .dn-center:has(.dn-trick-seat-0,.dn-trick-seat-1,.dn-trick-seat-2,.dn-trick-seat-3) .pile>div:last-child{display:none}
/* never let the message or the picker be shrunk to a sliver by flex — keep full text. */
html.dn-land .felt.dn-table .msg { font-size: 12.5px; min-height: 0; z-index: 5; flex: 0 0 auto; }
html.dn-land .felt.dn-table .pile,
html.dn-land .felt.dn-table .pile .lbl,
html.dn-land .felt.dn-table .bagcount { font-size: 12.5px; }
/* Center status text sits CLEAN on the felt (Jesse 2026-07-16: no black bubbles).
   Legibility comes from a text shadow that works on every felt color. */
html.dn-land .felt.dn-table .msg:not(:empty),
html.dn-land .felt.dn-table #score:not(:empty),
html.dn-land .felt.dn-table .scorebar:not(:empty) {
  background: none; border-radius: 0; padding: 2px 8px; box-shadow: none;
  text-shadow: 0 1px 2px rgba(0,0,0,.9), 0 0 14px rgba(0,0,0,.5);
}
html.dn-land .felt.dn-table #score:not(:empty),
html.dn-land .felt.dn-table .scorebar:not(:empty) { font-size: 12.5px; z-index: 7; }
/* pin the team score as a compact strip at the very top of the felt (like the
   reference's top score chip), OUT of the flow — so the vertically-centred play area
   (trick / message / picker) sits in the middle of the oval without ever colliding
   with it or the bottom action buttons. */
html.dn-land .felt.dn-table #score,
html.dn-land .felt.dn-table .scorebar {
  position: absolute; top: 3px;
  left: var(--dn-score-rail-center,50%); transform: translateX(-50%);
  width: max-content;
  margin: 0; max-width: min(76%,var(--dn-score-rail-width,76%)); z-index: 9;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  /* keep it a single row — the base .scorebar is flex-wrap:wrap, so as scores/bags/
     the won-bid tracker grow it would wrap to 2-3 lines and grow DOWN onto the North
     partner chip. It fits on one line within the 76% cap. */
  flex-wrap: nowrap;
}
/* pitch's persistent bid/trump line (#status) is reference info, not a centred status
   message — pin it as a compact chip in the top-LEFT corner (like the reference app's
   points chip) so it never sits behind the partner or in the play area. */
html.dn-land .felt.dn-table #status:not(:empty) {
  position: absolute; top: 42px; left: max(8px,env(safe-area-inset-left)); transform: none;
  margin: 0; max-width: 42%; z-index: 8; text-align: left;
  font-size: 12.5px; line-height: 1.25; white-space: normal;
}
/* The PRIMARY bid / choice / action bar (.bid-pick in spades; #bidbar in
   bid-whist/euchre/pitch) FLOWS with the centred message — it is NEVER absolutely
   positioned over the text. One row across the wide-but-short landscape, in a solid
   rounded panel so nothing bleeds through it. Empty → no panel. */
html.dn-land .felt.dn-table .bid-pick:not(.hidden),
html.dn-land .felt.dn-table #bidbar:not(:empty):not(.hidden),
html.dn-land .felt.dn-table #targets:not(:empty):not(.hidden) {
  position: static; z-index: 9; margin: 6px auto 0; flex: 0 0 auto;
  display: flex; flex-wrap: nowrap; justify-content: center; align-items: center; gap: 4px;
  max-width: 94%;
  background: rgba(6,17,13,.9); border: 1px solid rgba(255,255,255,.06);
  border-radius: 16px; padding: 8px 10px; box-shadow: 0 6px 22px rgba(0,0,0,.55);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
html.dn-land .felt.dn-table .bid-pick button,
html.dn-land .felt.dn-table #bidbar .btn,
html.dn-land .felt.dn-table #targets .btn { padding: 6px 8px; font-size: 12.5px; min-width: 0; }
/* go-fish's ask-history log would push its ask bar down into the hand/controls — hide
   it in landscape (the running log isn't essential; the seat books show the state). */
html.dn-land .felt.dn-table #askLogWrap { display: none; }
/* keep the end-of-hand breakdown compact + scrollable in landscape so it stays in the
   centre band and never collides with the bottom Hint/Next controls on short screens. */
html.dn-land .felt.dn-table .breakdown:not(:empty) {
  font-size: 12.5px; line-height: 1.3; max-height: 38vh; overflow-y: auto; z-index: 6;
}
/* a per-bid advice line (pitch #hint) flows too — BELOW the buttons, never behind them. */
html.dn-land .felt.dn-table #hint:not(:empty) {
  order: 0; margin: 5px auto 0; max-width: 78%; text-align: center;
  font-size: 12.5px; line-height: 1.3; color: var(--muted, #a9d8c2);
}
/* The ONLY absolute control row is the SECONDARY Hint / Undo (#controls): pin it just
   above the hand so it never lands over the message or the flowing bid bar. */
html.dn-land .felt.dn-table #controls:not(:empty) {
  position: absolute; bottom: 84px; left: 50%; right: auto; transform:translateX(-50%); margin: 0; z-index: 5;
  display: flex; justify-content: center; gap: 8px;
}
/* A classless PRIMARY .controls row (crazy-eights: Sort/Hint/Draw) gets the same
   flowing panel treatment as the primary bid bar — never absolute (an absolute pin
   collided with the message/stock lines), never under the hand. */
html.dn-land .felt.dn-table .controls:not(:empty):not(.hidden) {
  position: static; z-index: 9; flex: 0 0 auto; margin: 6px auto 0;
  display: flex; flex-wrap: nowrap; justify-content: center; align-items: center; gap: 6px;
}
/* the dark panel ONLY when something is actually visible inside — a row of
   hidden buttons still fails :empty and was rendering as a bare black band
   (hearts' pass phase). :has() is supported by our WebView floor; without it
   the row simply has no backdrop, which is the safe fallback. */
/* :not(.hidden) on the CONTAINER too — go-fish's #pondWrap is a .controls row
   whose static children are never .hidden, so the child check alone painted a
   phantom black panel (and its height shoved the status line up into North's
   label) while the row itself was hidden-by-class. */
html.dn-land .felt.dn-table .controls:not(:empty):not(.hidden):has(> :not(.hidden)) {
  background: rgba(6,17,13,.9); border: 1px solid rgba(255,255,255,.06);
  border-radius: 16px; padding: 6px 8px;
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
/* #controls is the compact Hint/Undo row.  It deliberately floats on the
   felt; giving its full-width absolute wrapper a dark panel creates the black
   tray/bar users read as a border around the cards. */
html.dn-land .felt.dn-table #controls.controls:not(:empty):not(.hidden):has(> :not(.hidden)) {
  background:transparent; border:0; border-radius:0; padding:0;
  -webkit-backdrop-filter:none; backdrop-filter:none;
}
html.dn-land .felt.dn-table .controls .btn { padding: 7px 10px; font-size: 12.5px; }
/* inside the pinned bottom strip: the action panel is pinned CLEAR of the hand
   (the hand is independently bottom-pinned, so flex order can't separate them),
   and the redundant "You" avatar/name hide — it's your hand, identity is obvious */
html.dn-land .felt.dn-table .dn-seat-you .controls:not(:empty) {
  position: absolute; left: 50%; bottom: 104px; transform: translateX(-50%); margin: 0;
}
html.dn-land .felt.dn-table .dn-seat-you .controls .name { display: none; }
html.dn-land .felt.dn-table .dn-seat-you > .seat-av,
html.dn-land .felt.dn-table .dn-seat-you > .name { display: none; }
/* the center stock/discard row must never flex-stretch the column apart; the
   bottom margin lifts it clear of the pinned action panel below */
html.dn-land .felt.dn-table .center-row { flex: 0 0 auto; min-height: 0; margin-bottom: 34px; }
/* YOU pinned across the bottom. Do NOT override display: a seat-wrapper keeps its
   column layout (hand over hidden name); a bare hand keeps its centered flex ROW
   (the fan) — forcing a direction here would stack the fan vertically. */
/* YOUR hand: pinned across the very bottom of the felt, whatever seat markup a
   game uses (some wrap it in a .seat, some leave it bare; some tag it .hand.mine,
   some just .hand). tagTable() adds .dn-hand to the real hand and .mine for shared
   styling, so this one rule places every game's hand identically. Absolute vs the
   felt so a nested seat can't shove it up into the middle of the table. */
/* .hand.mine included as a belt-and-braces twin: one sweep run caught a hand
   without its dn-hand tag (unpinned, giant cards mid-felt) — the pin must never
   depend on a single tagging pass. */
html.dn-land .felt.dn-table .dn-hand,
html.dn-land .felt.dn-table .hand.mine {
  position: absolute; left: env(safe-area-inset-left); right: env(safe-area-inset-right);
  bottom: calc(9px + env(safe-area-inset-bottom)); width: auto; margin: 0;
  min-height: 0; z-index: 8;
  display: flex; flex-wrap: nowrap; justify-content: center; align-items: flex-end;
}
/* Cards grow into the height reclaimed by the floating (no-bar) top overlay —
   "longer, bigger, easier to see" (fanHand's overlap keeps 13 on one row). */
/* Bigger, clearer hand cards — but the width is the SMALLER of a vw-based size and a
   height-derived cap (card height = width*7/5, so capping width at 32vh*5/7 pins the
   card to ~32vh tall). That lets the card grow into a roomy 16:9 table yet never clip
   the bottom of a tall-narrow (~2.16) phone in landscape. */
html.dn-land .felt.dn-table .dn-hand .card,
html.dn-land .felt.dn-table .hand.mine .card { width: min(clamp(66px, 12.5vw, 124px), calc(32vh * 5 / 7)); }
/* A mouse/trackpad web table has room for a larger, clearer hand.  Phones retain
   their compact cap so a full 13-card fan never becomes a clipped row. */
@media (min-width:800px) and (pointer:fine) {
  html.dn-land .felt.dn-table .dn-hand .card,
  html.dn-land .felt.dn-table .hand.mine .card { width: min(clamp(66px, 11vw, 124px), calc(32vh * 5 / 7)); }
}
/* Landscape is where full trick-taking hands hit their largest card count. Cap
   the centre suit below the width of its card so every top-left rank stays clear. */
html.dn-land .felt.dn-table .dn-hand .card .pip:not(.face),
html.dn-land .felt.dn-table .hand.mine .card .pip:not(.face) { font-size:clamp(28px,3.8vw,46px); }
html.dn-land .felt.dn-table .dn-hand .card .pip .ace,
html.dn-land .felt.dn-table .hand.mine .card .pip .ace { font-size:clamp(33px,4.5vw,52px); }
/* the human seat's own label/meta are redundant next to the pinned hand */
html.dn-land .felt.dn-table .dn-seat-you {
  background: transparent; border: 0; padding: 0;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
html.dn-land .felt.dn-table .dn-seat-you > .name,
html.dn-land .felt.dn-table .dn-seat-you > .meta { display: none; }
/* Some games (bid-whist, euchre, pitch) leave the human's own status line as a bare
   .meta directly on the felt — it's redundant next to your visible hand and it lands
   behind the bid buttons. Opponent metas live inside .dn-seat chips, so hiding only
   the felt's DIRECT-child meta clears the clutter without touching them. */
html.dn-land .felt.dn-table > .meta { display: none; }

/* ---------- storefront (coinShop) ---------- */
.dn-shop{max-width:440px}
.dn-shop-bal{color:var(--muted);margin:0 0 10px;font-weight:700}
.dn-shop-tabs{display:flex;gap:6px;margin-bottom:10px}
.dn-shop-tab{flex:1;padding:9px 4px;border-radius:10px;border:1px solid var(--line);
  background:var(--bg2);color:var(--muted);font-weight:800;font-size:13px;cursor:pointer}
.dn-shop-tab.on{background:var(--gold);color:#241c07;border-color:var(--gold)}
.dn-shop-body{max-height:min(52vh,420px);overflow-y:auto;padding-right:2px}
.dn-shop-row{display:flex;align-items:center;gap:10px;padding:8px 2px;border-bottom:1px solid rgba(255,255,255,.05)}
.dn-shop-icon{font-size:22px;flex:0 0 52px;text-align:center;font-style:normal;display:grid;place-items:center}
.dn-shop-main{flex:1;min-width:0;display:flex;flex-direction:column;line-height:1.25}
.dn-shop-main small{color:var(--muted);font-size:11.5px;text-transform:capitalize}
.dn-shop-mini{padding:7px 10px;font-size:12px;white-space:nowrap}
.dn-shop-note{color:var(--muted);font-size:12px;margin:10px 0 6px;line-height:1.45}
.dn-shop-place{width:100%;margin:6px 0 2px;font-size:12.5px}
/* felt swatches — explicit colors so previews don't depend on what's equipped */
.dn-swatch{width:34px;height:22px;border-radius:50%/46%;border:2px solid #17181d;box-shadow:0 1px 4px rgba(0,0,0,.5)}
.dn-swatch-red{background:radial-gradient(circle at 50% 35%,#b3222d,#5f1017)}
.dn-swatch-green{background:radial-gradient(circle at 50% 35%,#227a55,#0d3123)}
.dn-swatch-blue{background:radial-gradient(circle at 50% 35%,#1f4f8a,#0d2342)}
.dn-swatch-royal{background:radial-gradient(circle at 50% 35%,#6a3fa0,#37205a)}
.dn-swatch-noir{background:radial-gradient(circle at 50% 35%,#3a3d46,#131418)}
.dn-swatch-emerald{background:radial-gradient(circle at 50% 35%,#1d8f5f,#0a3d28)}
.dn-swatch-crimson{background:radial-gradient(circle at 50% 35%,#a51d2d,#520d16);box-shadow:0 1px 4px rgba(0,0,0,.5),inset 0 0 0 1px rgba(216,180,90,.5)}
/* deck swatches (Decks tab) — a LIVE mini card back carrying its .dn-bk-<id>
   theme class, so the preview is the real rendering, not an approximation */
.dn-shop-icon .card.mini.back{width:46px;border-radius:7px;pointer-events:none}
.dn-room-swatch-classic{background:linear-gradient(160deg,#231533,#130d1f)}
.dn-room-swatch-midnight{background:linear-gradient(160deg,#0e1220,#070910)}
.dn-room-swatch-ocean{background:linear-gradient(160deg,#0c2e38,#071820)}
.dn-room-swatch-velvet{background:linear-gradient(160deg,#2c1119,#170810)}

/* ---------- profile flair + table items ---------- */
.dn-flair{margin-left:5px;font-style:normal;font-weight:400}
.dn-tray{padding:6px 9px}
.dn-tray-pop{position:fixed;z-index:140;display:flex;gap:6px;padding:8px;border-radius:14px;
  background:var(--panel);border:1px solid var(--line);box-shadow:0 12px 30px rgba(0,0,0,.5)}
.dn-tray-pop button{border:1px solid var(--line);background:var(--bg2);border-radius:10px;
  padding:8px 10px;font-size:20px;cursor:pointer;color:var(--text)}
.dn-tray-pop button small{font-size:10px;color:var(--muted)}
.dn-table-item{position:absolute;z-index:45;font-size:clamp(22px,4.5vw,34px);font-style:normal;pointer-events:none;
  filter:drop-shadow(0 4px 6px rgba(0,0,0,.45));
  animation:dnItemPop .55s cubic-bezier(.34,1.56,.64,1);transition:opacity 2s ease}
.dn-table-item.dn-item-fade{opacity:0}
.dn-rarity-rare{filter:drop-shadow(0 4px 6px rgba(0,0,0,.45)) drop-shadow(0 0 8px rgba(216,180,90,.55))}
.dn-rarity-epic{filter:drop-shadow(0 4px 6px rgba(0,0,0,.45)) drop-shadow(0 0 14px rgba(216,180,90,.85))}
@keyframes dnItemPop{0%{transform:translateY(-42px) scale(.3) rotate(-14deg);opacity:0}
  62%{transform:translateY(4px) scale(1.14) rotate(3deg)}100%{transform:none;opacity:1}}

/* ---------- focus mode: clean table once play is underway ---------- */
/* Labels retire; avatars, badges, and the turn glow stay. Tap an avatar to
   peek that player's name/meta as a floating chip for a moment. */
html.dn-focus .seat .name,html.dn-focus .seat .meta{display:none}
/* While a trick is on the table, leave the names visible.  The player needs to
   connect the directional card and its source seat at a glance; quiet focus can
   resume as soon as the cards sweep away. */
html.dn-focus .felt:has(#trick .dn-trick-seat-0) .seat .name,
html.dn-focus .felt:has(#trick .dn-trick-seat-0) .seat .meta{display:block}
/* During the AUCTION, each seat's live bid must be readable next to that seat —
   focus mode otherwise hides seat labels until cards hit the table. The game sets
   html.dn-bidding while any bid/name-trump phase is live. */
html.dn-focus.dn-bidding .seat .name,
html.dn-focus.dn-bidding .seat .meta{display:block}
/* Go Fish / Old Maid: the opponent card/book counts (and the player's pool count)
   are live info the game is built around — keep the .meta counts on the table in
   focus mode. Names still retire to the avatars for a clean look. */
html.dn-focus.dn-seatmeta .seat .meta{display:block}
/* !important on purpose: the landscape .dn-seat chip rule is more specific and
   used to keep its dark box behind the avatar — Jesse's bar is a CLEAN felt,
   so in focus the avatar floats directly on the table in every orientation. */
html.dn-focus .seat,html.dn-focus .dn-seat{min-width:0;padding:6px;
  background:transparent !important;border-color:transparent !important;box-shadow:none !important}
/* peek chips anchor to their seat. Positioning discipline learned the hard way:
   (a) the YOU-seat must NEVER become positioned — it re-anchors the absolutely-
   pinned hand off the felt and floats it mid-table; (b) in LANDSCAPE the seats
   are already position:absolute — a later `relative` OVERRIDES it (same
   specificity, later in file) and collapses them into the flow as clipped
   slivers. So: relative only in PORTRAIT, only for opponent seats. */
html.dn-focus:not(.dn-land) .seat:not(.seat-you):not(.dn-seat-you){position:relative}
html.dn-focus .seat .seat-av{cursor:pointer}
html.dn-focus .seat.dn-peek .name,html.dn-focus .seat.dn-peek .meta{
  display:block;position:absolute;left:50%;transform:translateX(-50%);z-index:82;white-space:nowrap;
  background:rgba(10,11,14,.94);border:1px solid var(--line);border-radius:10px;padding:5px 10px;
  box-shadow:0 8px 20px rgba(0,0,0,.45);animation:dnPeek .16s ease}
html.dn-focus .seat.dn-peek .name{top:calc(100% + 4px)}
html.dn-focus .seat.dn-peek .meta{top:calc(100% + 38px)}
/* bottom seat (you): peek upward so it never collides with your hand */
html.dn-focus .seat.seat-you.dn-peek .name,html.dn-focus .dn-seat-you.dn-peek .name{top:auto;bottom:calc(100% + 4px)}
html.dn-focus .seat.seat-you.dn-peek .meta,html.dn-focus .dn-seat-you.dn-peek .meta{top:auto;bottom:calc(100% + 38px)}
@keyframes dnPeek{from{opacity:0;transform:translateX(-50%) translateY(-3px)}to{opacity:1;transform:translateX(-50%)}}
.shell-session{color:var(--muted);font-size:12.5px;margin:2px 0 0}

/* ---- live-table turn clock (play.html) — appended, scoped to .mp-timer ---- */
.mp-timer{align-self:center;height:28px;display:inline-flex;align-items:center;gap:6px;padding:0 14px;
  border-radius:999px;background:rgba(0,0,0,.35);border:1px solid var(--line);color:var(--text);
  font-weight:800;font-size:13px;letter-spacing:.04em;box-shadow:0 2px 8px rgba(0,0,0,.25)}
.mp-timer.amber{color:#ffb347;border-color:rgba(255,179,71,.45)}
.mp-timer.crit{color:#ff5d5d;border-color:rgba(255,93,93,.55);animation:mp-timer-pulse .8s ease-in-out infinite}
.mp-timer.hidden{display:none}
@keyframes mp-timer-pulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.08);opacity:.7}}

/* ============================================================
   D23 polish niceties (appended 2026-07-16) — additive, dn- scoped
   ============================================================ */
/* Seat speech bubble (Shell.say): brief table talk by the acting seat for
   moments the games already announce (bids, "Pass", "Go fish!"). CLEAN-LABEL
   standard — NO box; legibility comes from the standard text shadow. Fixed
   position (appended to <body>) so it never re-anchors a pinned hand and
   never participates in table layout. */
.dn-bubble{position:fixed;z-index:96;pointer-events:none;transform:translateX(-50%);
  font-weight:800;font-size:13.5px;letter-spacing:.02em;color:#f0d48a;white-space:nowrap;
  text-shadow:0 1px 2px rgba(0,0,0,.9),0 0 12px rgba(0,0,0,.5);
  animation:dnBubbleIn 1.6s ease both}
@keyframes dnBubbleIn{0%{opacity:0;transform:translateX(-50%) translateY(5px) scale(.9)}
  9%,74%{opacity:1;transform:translateX(-50%) translateY(0) scale(1)}
  100%{opacity:0;transform:translateX(-50%) translateY(-7px) scale(1)}}
@media (prefers-reduced-motion:reduce){.dn-bubble{animation:none}}

/* Go Fish draw pond: appears only when YOU must "Go fish" — the pile pulses
   gold until tapped (static glow under reduced motion so the affordance stays). */
.dn-pond{display:flex;align-items:center;gap:10px;background:none;border:0;cursor:pointer;
  padding:4px 10px;-webkit-tap-highlight-color:transparent}
.dn-pond .card{display:block;pointer-events:none}
.dn-pond-lbl{font-size:13px;font-weight:800;color:#f0d48a;
  text-shadow:0 1px 2px rgba(0,0,0,.9),0 0 10px rgba(0,0,0,.45)}
.dn-pond .card.back{outline:2px solid rgba(216,180,90,.7);outline-offset:2px;
  animation:dnPondPulse 1.1s ease-in-out infinite}
@keyframes dnPondPulse{0%,100%{outline-color:rgba(216,180,90,.35);filter:drop-shadow(0 0 4px rgba(216,180,90,.2))}
  50%{outline-color:rgba(216,180,90,1);filter:drop-shadow(0 0 13px rgba(216,180,90,.8))}}
@media (prefers-reduced-motion:reduce){.dn-pond .card.back{animation:none;
  outline-color:var(--gold);filter:drop-shadow(0 0 10px rgba(216,180,90,.6))}}

/* ============================================================
   Cosmetics + retention batch (appended 2026-07-17) — dn- scoped
   ============================================================ */

/* ---- per-player card backs (Jesse ruling 2026-07-17) ----
   The equipped deck theme (html.dn-back-<id>) skins only the backs the BUYER
   owns visually: the deck/stock piles they draw from and their own face-down
   cards. game-shell.js tags every back that belongs to an OPPONENT (their hand
   minis, war's opponent pile, old-maid's neighbor hand) with .dn-back-theirs,
   and this block pins those to the free Classic Gold back — the :root prefix
   out-specifies every html.dn-back-<id> theme rule above. Cards carrying an
   explicit .dn-bk-<id> (shop swatches, the MP play-reveal) are never tagged. */
:root .card.back.dn-back-theirs,:root .card.back.dn-bk-classic{
  background:
    repeating-linear-gradient(45deg, transparent 0 8px, rgba(216,180,90,.09) 8px 9px),
    repeating-linear-gradient(-45deg, transparent 0 8px, rgba(216,180,90,.09) 8px 9px),
    radial-gradient(125% 95% at 50% 0%, #2b3668, #1a2247 68%, #131a37);
  border-color:#0e1330;
  box-shadow:0 2px 6px rgba(0,0,0,.4),
    inset 0 0 0 2px rgba(216,180,90,.34),
    inset 0 0 0 3px rgba(0,0,0,.4),
    inset 0 0 0 4px rgba(216,180,90,.13)}
:root .card.back.dn-back-theirs::after,:root .card.back.dn-bk-classic::after{inset:15% 21%;border-radius:6px;
  border:1px solid rgba(216,180,90,.42);
  background:radial-gradient(circle at 50% 50%, rgba(216,180,90,.16), transparent 62%)}
:root .card.back.dn-back-theirs::before,:root .card.back.dn-bk-classic::before{width:24%;height:24%;
  transform:translate(-50%,-50%) rotate(45deg);border-radius:3px;
  background:linear-gradient(135deg, rgba(216,180,90,.6), rgba(216,180,90,.22));
  border:1px solid rgba(216,180,90,.55);box-shadow:0 0 8px rgba(216,180,90,.3)}

/* ---- five NEW purchasable card-back themes (same twin-selector pattern:
   html.dn-back-<id> equips it, .dn-bk-<id> is the card-level shop swatch).
   Each is deliberately tell-apart at 16-24px minis: navy-with-pinstripes,
   the only LIGHT back, saturated blue, graphite weave, purple damask. */
/* Midnight Pinstripe — deep navy banker's cloth, fine silver pinstripes */
html.dn-back-midnight-pinstripe .card.back,:root .card.back.dn-bk-midnight-pinstripe{
  background:
    repeating-linear-gradient(90deg,transparent 0 6px,rgba(190,205,235,.22) 6px 7px),
    radial-gradient(125% 95% at 50% 0%,#1d2a4e,#131c38 62%,#0a0f22);
  border-color:#060a18;
  box-shadow:0 2px 6px rgba(0,0,0,.5),
    inset 0 0 0 2px rgba(190,205,235,.4),
    inset 0 0 0 3px rgba(0,0,0,.5),
    inset 0 0 0 4px rgba(190,205,235,.14)}
html.dn-back-midnight-pinstripe .card.back::after,:root .card.back.dn-bk-midnight-pinstripe::after{
  inset:12% 16%;border-color:rgba(190,205,235,.45);background:none;border-radius:7px}
html.dn-back-midnight-pinstripe .card.back::before,:root .card.back.dn-bk-midnight-pinstripe::before{
  width:26%;height:26%;transform:translate(-50%,-50%) rotate(45deg);border-radius:3px;
  background:linear-gradient(135deg,rgba(190,205,235,.55),rgba(190,205,235,.16));
  border:1px solid rgba(190,205,235,.6);box-shadow:0 0 8px rgba(190,205,235,.3)}
/* Ivory Deco — the one LIGHT back: warm ivory field, gold art-deco sunburst */
html.dn-back-ivory-deco .card.back,:root .card.back.dn-bk-ivory-deco{
  background:
    repeating-conic-gradient(from 240deg at 50% 108%,rgba(201,155,46,.16) 0 5deg,transparent 5deg 14deg),
    radial-gradient(125% 95% at 50% 0%,#f7efdc,#efe2c2 62%,#ddc794);
  border-color:#8a6d2e;
  box-shadow:0 2px 6px rgba(0,0,0,.35),
    inset 0 0 0 2px rgba(138,109,46,.6),
    inset 0 0 0 3px rgba(247,239,220,.8),
    inset 0 0 0 4px rgba(138,109,46,.25)}
html.dn-back-ivory-deco .card.back::after,:root .card.back.dn-bk-ivory-deco::after{
  inset:12% 16%;border-color:rgba(138,109,46,.55);
  background:radial-gradient(circle at 50% 42%,rgba(255,255,255,.5),transparent 60%);border-radius:7px}
html.dn-back-ivory-deco .card.back::before,:root .card.back.dn-bk-ivory-deco::before{
  width:30%;height:22%;transform:translate(-50%,-50%);border:0;border-radius:0;box-shadow:none;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 44'%3E%3Cg fill='none' stroke='%238a6d2e' stroke-width='3'%3E%3Cpath d='M30 42V20'/%3E%3Cpath d='M30 42L12 8'/%3E%3Cpath d='M30 42L48 8'/%3E%3Cpath d='M30 42L2 26'/%3E%3Cpath d='M30 42L58 26'/%3E%3C/g%3E%3Ccircle cx='30' cy='42' r='6' fill='%23c99b2e'/%3E%3C/svg%3E") center/contain no-repeat}
/* Sapphire Filigree — saturated royal blue, pale-gold filigree scrollwork */
html.dn-back-sapphire-filigree .card.back,:root .card.back.dn-bk-sapphire-filigree{
  background:
    repeating-radial-gradient(circle at 50% 50%,rgba(240,212,138,.06) 0 1px,transparent 1px 8px),
    radial-gradient(125% 95% at 50% 0%,#1e56b4,#153f8a 62%,#0a2050);
  border-color:#071636;
  box-shadow:0 2px 6px rgba(0,0,0,.45),
    inset 0 0 0 2px rgba(240,212,138,.5),
    inset 0 0 0 3px rgba(0,0,0,.45),
    inset 0 0 0 4px rgba(240,212,138,.15)}
html.dn-back-sapphire-filigree .card.back::after,:root .card.back.dn-bk-sapphire-filigree::after{
  inset:11% 15%;border-color:rgba(240,212,138,.5);background:none;border-radius:8px}
html.dn-back-sapphire-filigree .card.back::before,:root .card.back.dn-bk-sapphire-filigree::before{
  width:58%;height:54%;border:0;border-radius:0;box-shadow:none;transform:translate(-50%,-50%);
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80'%3E%3Cg fill='none' stroke='%23f0d48a' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M40 12c10 0 16 7 16 14s-6 12-16 12-16-5-16-12 6-14 16-14z'/%3E%3Cpath d='M40 38v20'/%3E%3Cpath d='M40 48c-8 0-14 4-16 10 8 0 13-3 16-7'/%3E%3Cpath d='M40 48c8 0 14 4 16 10-8 0-13-3-16-7'/%3E%3Cpath d='M28 20c-6-2-10-1-13 2 4 3 8 3 13 1'/%3E%3Cpath d='M52 20c6-2 10-1 13 2-4 3-8 3-13 1'/%3E%3C/g%3E%3Ccircle cx='40' cy='25' r='4' fill='%23f0d48a'/%3E%3C/svg%3E") center/contain no-repeat}
/* Onyx Carbon — graphite carbon weave, gunmetal frame, steel stud center */
html.dn-back-onyx-carbon .card.back,:root .card.back.dn-bk-onyx-carbon{
  background:
    repeating-linear-gradient(45deg,rgba(255,255,255,.05) 0 3px,transparent 3px 6px),
    repeating-linear-gradient(-45deg,rgba(0,0,0,.4) 0 3px,transparent 3px 6px),
    radial-gradient(125% 95% at 50% 0%,#2e3138,#1b1d22 62%,#0b0c0f);
  border-color:#000;
  box-shadow:0 2px 6px rgba(0,0,0,.55),
    inset 0 0 0 2px rgba(154,161,173,.45),
    inset 0 0 0 3px rgba(0,0,0,.6),
    inset 0 0 0 4px rgba(154,161,173,.14)}
html.dn-back-onyx-carbon .card.back::after,:root .card.back.dn-bk-onyx-carbon::after{
  inset:13% 17%;border-color:rgba(154,161,173,.45);
  background:radial-gradient(circle at 50% 50%,rgba(154,161,173,.1),transparent 62%);border-radius:6px}
html.dn-back-onyx-carbon .card.back::before,:root .card.back.dn-bk-onyx-carbon::before{
  width:24%;height:24%;transform:translate(-50%,-50%) rotate(45deg);border-radius:2px;
  background:linear-gradient(135deg,#c6ccd6,#5c616d);
  border:1px solid rgba(220,226,236,.7);box-shadow:0 0 9px rgba(154,161,173,.5)}
/* Royal Damask — regal purple damask, gold fleur ornament */
html.dn-back-royal-damask .card.back,:root .card.back.dn-bk-royal-damask{
  background:
    repeating-radial-gradient(circle at 25% 20%,rgba(240,212,138,.07) 0 2px,transparent 2px 11px),
    repeating-radial-gradient(circle at 75% 80%,rgba(240,212,138,.07) 0 2px,transparent 2px 11px),
    radial-gradient(125% 95% at 50% 0%,#6b3fa8,#4c2b7c 62%,#271343);
  border-color:#1a0c30;
  box-shadow:0 2px 6px rgba(0,0,0,.5),
    inset 0 0 0 2px rgba(240,212,138,.5),
    inset 0 0 0 3px rgba(0,0,0,.45),
    inset 0 0 0 4px rgba(240,212,138,.15)}
html.dn-back-royal-damask .card.back::after,:root .card.back.dn-bk-royal-damask::after{
  inset:12% 16%;border-color:rgba(240,212,138,.5);
  background:radial-gradient(circle at 50% 45%,rgba(240,212,138,.12),transparent 60%);border-radius:8px}
html.dn-back-royal-damask .card.back::before,:root .card.back.dn-bk-royal-damask::before{
  width:52%;height:52%;border:0;border-radius:0;box-shadow:none;transform:translate(-50%,-50%);
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cg fill='%23f0d48a'%3E%3Cpath d='M32 6c4 8 4 14 0 20-4-6-4-12 0-20z'/%3E%3Cpath d='M18 18c8 1 12 5 14 11-7-1-12-5-14-11z'/%3E%3Cpath d='M46 18c-8 1-12 5-14 11 7-1 12-5 14-11z'/%3E%3Cpath d='M29 30h6l-1 14h-4z'/%3E%3Cpath d='M24 46h16l-2 6H26z'/%3E%3C/g%3E%3C/svg%3E") center/contain no-repeat}

/* ---- play-reveal (.dn-play-reveal, class contract shared with the MP client) ----
   Apply ONCE to a card landing in the trick/center together with its .dn-bk-<id>;
   remove both classes after ≤750ms (game-shell.js does this for solo; mp-room
   does it for live tables). A short edge-flash in the back's signature color —
   never persistent, runs a single iteration. Reduced motion / Animations off
   get a static tinted edge for the same window instead. */
.card.dn-bk-rose-noir{--dn-bk-glow:rgba(216,180,90,.95)}
.card.dn-bk-midnight-pinstripe{--dn-bk-glow:rgba(154,180,235,.95)}
.card.dn-bk-ivory-deco{--dn-bk-glow:rgba(240,226,190,.98)}
.card.dn-bk-gold-royale{--dn-bk-glow:rgba(240,212,138,.98)}
.card.dn-bk-sapphire-filigree{--dn-bk-glow:rgba(80,140,235,.95)}
.card.dn-bk-crimson-velvet{--dn-bk-glow:rgba(224,86,99,.95)}
.card.dn-bk-emerald-lux{--dn-bk-glow:rgba(47,191,135,.95)}
.card.dn-bk-onyx-carbon{--dn-bk-glow:rgba(198,204,214,.9)}
.card.dn-bk-royal-damask{--dn-bk-glow:rgba(169,112,255,.95)}
.card.dn-play-reveal{animation:dnPlayReveal .62s ease-out 1}
@keyframes dnPlayReveal{
  0%{box-shadow:0 0 0 0 var(--dn-bk-glow,rgba(216,180,90,.9))}
  24%{box-shadow:0 0 0 3px var(--dn-bk-glow,rgba(216,180,90,.9)),0 0 20px 4px var(--dn-bk-glow,rgba(216,180,90,.55))}
  100%{box-shadow:0 0 0 0 rgba(0,0,0,0)}}
@media (prefers-reduced-motion:reduce){
  .card.dn-play-reveal{animation:none;box-shadow:0 0 0 2px var(--dn-bk-glow,rgba(216,180,90,.75))}}
html.dn-anim-off .card.dn-play-reveal{animation:none;box-shadow:0 0 0 2px var(--dn-bk-glow,rgba(216,180,90,.75))}

/* ---- flagship prestige table (large-coin-cost goal per Jesse) ---- */
/* Sovereign Gold felt — black-gold baize with a bright gold etch ring */
html.dn-felt-sovereign{--felt-hi:#7a6420;--felt-mid:#554414;--felt-lo:#2b2208;--felt-line:#1a1404;
  --felt-etch:rgba(240,212,138,.42)}
.dn-swatch-sovereign{background:radial-gradient(circle at 50% 35%,#7a6420,#2b2208);
  box-shadow:0 1px 4px rgba(0,0,0,.5),inset 0 0 0 1px rgba(240,212,138,.75)}
/* Penthouse Skyline room — night glass, warm city glow low on the horizon */
html.dn-room-penthouse body{background:
  radial-gradient(1100px 620px at 50% -12%,rgba(120,150,255,.14),transparent 60%),
  radial-gradient(900px 320px at 50% 106%,rgba(240,180,80,.22),transparent 68%),
  repeating-linear-gradient(90deg,transparent 0 46px,rgba(255,214,140,.05) 46px 49px),
  repeating-radial-gradient(circle at 78% 18%,rgba(255,255,255,.035) 0 1px,transparent 1px 46px),
  linear-gradient(180deg,#0b0e1c,#050308)}
.dn-room-swatch-penthouse{background:linear-gradient(180deg,#0b0e1c 40%,#241503)}

/* ---- free-coin timer: quiet HUD nudge when a claim is ready ---- */
.dn-coin.dn-free-ready .dn-coin-plus{color:#4bd0a0;text-shadow:0 0 8px rgba(75,208,160,.8)}

/* The rules "?" is THE way to re-read rules (coaches auto-show only on a
   game's first-ever open) — make it read as a real control, not a ghost:
   gold-ringed circle, always visible in every game topbar. */
.shell-help{width:34px;height:34px;padding:0 !important;border-radius:50% !important;
  flex:0 0 auto;font-size:16px;font-weight:800;line-height:1;
  color:var(--gold);border:1.5px solid rgba(216,180,90,.65);background:rgba(216,180,90,.08)}
.shell-help:active{transform:translateY(1px)}

/* ---- shared DeNiro Card deck mark -------------------------------------------------
   The free Classic back is the actual live DeNiro card-back artwork. Every paid
   material keeps its own surface, but carries the same authentic gold DC-with-rose
   logo from the live brand rather than an abstract placeholder glyph. */
.card.back{--dn-back-frame:rgba(240,212,138,.66);--dn-back-glow:rgba(240,212,138,.14)}
html.dn-back-rose-noir,:root .card.back.dn-bk-rose-noir{--dn-back-frame:rgba(241,204,115,.72);--dn-back-glow:rgba(241,204,115,.16)}
html.dn-back-gold-royale,:root .card.back.dn-bk-gold-royale{--dn-back-frame:rgba(74,54,9,.7);--dn-back-glow:rgba(74,54,9,.13)}
html.dn-back-crimson-velvet,:root .card.back.dn-bk-crimson-velvet{--dn-back-frame:rgba(244,212,127,.72);--dn-back-glow:rgba(244,212,127,.16)}
html.dn-back-emerald-lux,:root .card.back.dn-bk-emerald-lux{--dn-back-frame:rgba(243,212,110,.72);--dn-back-glow:rgba(243,212,110,.15)}
html.dn-back-midnight-pinstripe,:root .card.back.dn-bk-midnight-pinstripe{--dn-back-frame:rgba(184,210,255,.7);--dn-back-glow:rgba(184,210,255,.15)}
html.dn-back-ivory-deco,:root .card.back.dn-bk-ivory-deco{--dn-back-frame:rgba(111,73,20,.62);--dn-back-glow:rgba(111,73,20,.13)}
html.dn-back-sapphire-filigree,:root .card.back.dn-bk-sapphire-filigree{--dn-back-frame:rgba(232,241,255,.72);--dn-back-glow:rgba(232,241,255,.15)}
html.dn-back-onyx-carbon,:root .card.back.dn-bk-onyx-carbon{--dn-back-frame:rgba(228,233,242,.66);--dn-back-glow:rgba(228,233,242,.14)}
html.dn-back-royal-damask,:root .card.back.dn-bk-royal-damask{--dn-back-frame:rgba(255,224,138,.72);--dn-back-glow:rgba(255,224,138,.16)}

/* Match the specificity of every existing pack override without changing a pack ID,
   its owned state, pricing, or the buyer-only / opponent-classic policy. */
:root .card.back::after,
:root .card.back.dn-back-theirs::after,
:root .card.back[class*="dn-bk-"]::after,
html[class*="dn-back-"] .card.back::after{
  content:"";inset:9% 13%;border-radius:8px;border:1px solid var(--dn-back-frame);
  background:radial-gradient(circle at 50% 44%,var(--dn-back-glow),transparent 62%);
  box-shadow:inset 0 0 0 2px rgba(0,0,0,.16),0 0 0 1px rgba(255,255,255,.05)
}
:root .card.back::before,
:root .card.back.dn-back-theirs::before,
:root .card.back[class*="dn-bk-"]::before,
html[class*="dn-back-"] .card.back::before{
  content:"";top:50%;left:50%;width:63%;height:58%;border:0;border-radius:0;
  transform:translate(-50%,-50%);background:url("icons/dc-rose-gold.png") center/contain no-repeat;
  box-shadow:none;filter:drop-shadow(0 1px 4px var(--dn-back-glow));
  -webkit-mask:none;mask:none
}
/* On tiny opponent cards and shop swatches the real mark stays readable. */
.card.mini.back::before{width:66%;height:60%}

/* Classic is a faithful local copy of the live back — no external dependency.
   Opponents always use it, and the free shop swatch stays truthful to the deck. */
html:not([class*="dn-back-"]) .card.back:not([class*="dn-bk-"]),
:root .card.back.dn-back-theirs,
:root .card.back.dn-bk-classic{
  background:#090d1f url("icons/dc-rose-card-back.png") center/100% 100% no-repeat;
  border-color:#e7c52b;
  box-shadow:0 2px 6px rgba(0,0,0,.42),inset 0 0 0 1px rgba(255,225,110,.25)
}
html:not([class*="dn-back-"]) .card.back:not([class*="dn-bk-"])::after,
html:not([class*="dn-back-"]) .card.back:not([class*="dn-bk-"])::before,
:root .card.back.dn-back-theirs::after,
:root .card.back.dn-back-theirs::before,
:root .card.back.dn-bk-classic::after,
:root .card.back.dn-bk-classic::before{display:none}

/* ---- Comfortable text & controls -------------------------------------------------
   Browser zoom stays off so the native WebViews never jump or accidentally zoom;
   this optional in-app preference gives a younger player or an older player a
   reliable, saved way to enlarge the things that matter.  Portrait gets the full
   boost.  Landscape keeps its dense table geometry, but still increases labels
   and targets enough to be noticeably easier to read without covering the hand. */
html.dn-large-ui .shell-modal .shell-row{min-height:48px;font-size:16px}
html.dn-large-ui .shell-modal .shell-select{min-height:42px;font-size:16px}
html.dn-large-ui:not(.dn-land) .felt .seat .name{font-size:16px}
html.dn-large-ui:not(.dn-land) .felt .seat .meta{font-size:13px;line-height:1.35}
html.dn-large-ui:not(.dn-land) .felt .msg{font-size:17px;line-height:1.34}
html.dn-large-ui:not(.dn-land) .felt .pile{font-size:14px}
html.dn-large-ui:not(.dn-land) .felt .scorebar{font-size:15px}
html.dn-large-ui:not(.dn-land) .felt .controls .btn{min-height:46px;font-size:16px}
html.dn-large-ui:not(.dn-land) .felt .hand.mine .card{width:clamp(58px,16vw,86px)}
html.dn-large-ui.dn-land .felt.dn-table .dn-seat .name{font-size:13px}
html.dn-large-ui.dn-land .felt.dn-table .dn-seat .meta{font-size:13px}
html.dn-large-ui.dn-land .felt.dn-table .msg{font-size:13px}
html.dn-large-ui.dn-land .felt.dn-table #score:not(:empty),
html.dn-large-ui.dn-land .felt.dn-table .scorebar:not(:empty){font-size:12.5px}
html.dn-large-ui.dn-land .felt.dn-table .controls .btn{min-height:38px;font-size:13px}

/* Phone release contract: controls may look compact on a short table, but their
   ACTUAL, non-overlapping boxes stay at the 44px mobile target floor. Do not
   reintroduce transparent pseudo hit-slop: adjacent Help/Settings controls can
   otherwise activate each other. Board cells and playing cards are spatial game
   pieces, so they keep their board/fan geometry. */
@media (pointer:coarse),(max-width:900px){
  header .header-cta .btn{
    min-height:44px;display:inline-flex;align-items:center;justify-content:center}
  html .topbar .brand{min-width:44px;min-height:44px;justify-content:center}
  html .topbar .shell-actions .btn,html .topbar > .shell-help{
    min-width:44px;min-height:44px;height:44px;padding:0;
    display:grid;place-items:center}
  html .topbar .shell-actions .shell-new-game{width:auto;min-width:44px;padding:0 8px}
  html .topbar .dn-coin{min-height:44px}
  .controls .btn,.ctrlrow .btn,.shell-modal .btn,.bid-pick button{
    min-height:44px}
  .shell-modal .btn{min-width:44px}
  .shell-select{min-height:44px}
  .shell-switch{height:44px;min-height:44px}
  .shell-switch::after{top:10px}
  html.dn-land .felt.dn-table .dn-bidpanel .dn-bp-opt,
  html.dn-land .felt.dn-table .dn-bidpanel .dn-bp-bid,
  html.dn-land .felt.dn-table .dn-bidpanel .dn-bp-pass{
    min-height:44px;height:44px}
}

/* The shared bid panel has three decision rows in Bid Whist. On a short
   landscape phone the separate #msg repeated the panel prompt and pushed the
   44px BID/PASS targets below the viewport. Keep every target full-size: remove
   only that duplicate line from layout, then anchor the transparent host inside
   all four safe areas. The panel remains centered in the usable width, while
   pointer-events pass through the host everywhere except the actual panel. */
@media (orientation:landscape) and (max-height:430px){
  html.dn-land .felt.dn-table:has(.dn-bidpanel) > #msg{display:none!important}
  html.dn-land .felt.dn-table #bidbar:has(.dn-bidpanel),
  html.dn-land .felt.dn-table .bid-pick:has(.dn-bidpanel){
    position:absolute!important;
    left:max(10px,env(safe-area-inset-left))!important;
    right:max(10px,env(safe-area-inset-right))!important;
    bottom:max(4px,env(safe-area-inset-bottom))!important;
    width:auto!important;max-width:none!important;margin:0!important;
    transform:none!important;z-index:12!important;pointer-events:none}
  html.dn-land .felt.dn-table .dn-bidpanel{pointer-events:auto}
}
