/* ─────────────────────────────────────────────────────────────────────
   Disable iOS double-tap-to-zoom on interactive content.
   `touch-action: manipulation` preserves single tap, pan (scroll), and
   pinch-to-zoom (accessibility), but turns off the legacy double-tap
   zoom gesture. As a side benefit, this also removes the 300 ms tap
   latency that older iOS versions added to detect double-taps.
   Applies globally because every clickable surface in the app should
   feel snappy and stable (avatar picker, reaction buttons, action
   buttons during a hand, etc.).
   ───────────────────────────────────────────────────────────────────── */
/* ── Police officielle Inter (parité QML, point 7 du chantier clone) ──
   Fichier EXACT du client officiel (Inter-VariableFont de l'APK, axes
   opsz 14-32 + wght 100-900), converti en woff2 (877 Ko → 350 Ko),
   self-hosted (public/fonts/, licence OFL jointe). Les tokens --ff-* des
   thèmes PokerTH référencent déjà 'Inter' : ce @font-face les résout enfin
   (ils retombaient silencieusement sur system-ui). */
@font-face {
  font-family: 'Inter';
  src: url('fonts/InterVariable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
/* Texte courant en Inter dans les thèmes PokerTH uniquement (le QML utilise
   Inter partout) ; les autres thèmes gardent leur pile système. Les contrôles
   de formulaire n'héritent pas par défaut -> inherit explicite. */
:root[data-theme^="pokerth"] body { font-family: 'Inter', system-ui, sans-serif; }
:root[data-theme^="pokerth"] :is(button, input, select, textarea) { font-family: inherit; }

html { touch-action: manipulation; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

:root {
  /* ═══════════════════════════════════════════════════════════════════════
     COUCHE DE TOKENS GÉNÉRIQUE — échelles unifiées (typo · espacement · rayon ·
     hauteurs de contrôle). Barème DISCRET, surchargeable par breakpoint en un
     seul endroit (voir docs/DESIGN_TOKENS.md → §Responsive). ADDITIF : tant
     qu'un élément ne référence pas ces variables, le rendu est INCHANGÉ.
     Les valeurs QML (barre d'action 54/40 px, All-In 52 px, radius 9/10) sont
     encodées ici → la fidélité est préservée par construction.
     Réf. complète : docs/DESIGN_TOKENS.md */
  /* Typographie (rem) — dérivée des 77 valeurs existantes ramenées à 10 pas */
  --fs-3xs: .55rem;  --fs-2xs: .62rem;  --fs-xs: .70rem;   --fs-sm: .78rem;
  --fs-base: .85rem; --fs-md: .95rem;   --fs-lg: 1.05rem;  --fs-xl: 1.35rem;
  --fs-2xl: 1.6rem;  --fs-hero: 2.6rem;
  --lh-tight: 1.1;   --lh-base: 1.35;
  /* Espacement (px, base 2 — 6/8 = chevaux de trait) */
  --sp-0: 2px;  --sp-1: 4px;  --sp-2: 6px;  --sp-3: 8px;
  --sp-4: 10px; --sp-5: 12px; --sp-6: 16px; --sp-7: 20px;
  /* Rayons (QML : bouton 9, barre 10) */
  --r-xs: 3px; --r-sm: 6px; --r-md: 9px; --r-lg: 12px; --r-bar: 10px; --r-pill: 999px;
  /* Accent des pastilles du bandeau de jeu. #pot-strip est un bandeau NOIR
     rgba(0,0,0,0.78) dans TOUS les modes (parite QML §7), donc son accent doit
     rester lisible sur du sombre : il suit le theme par defaut, mais la palette
     claire le ramene a l'or QML (Theme.colorAccent #E3C800 n'a qu'UNE valeur
     dans la bible §13.2, pas de variante claire). */
  --sel-bar: var(--sel, #E3C800);
  --sel-bar-rgb: var(--sel-rgb, 227,200,0);
  /* Hauteurs de contrôle (QML : barre 54, landscapeCompact 40, All-In 52) */
  --ctrl-sm: 26px; --ctrl-md: 34px; --ctrl-lg: 40px; --ctrl-xl: 54px; --ctrl-allin: 52px;

  --ff-display: 'Cinzel', serif;
  --ff-mono:    'Inconsolata', monospace;
  --timer-glow: 0 0 3px rgba(var(--gold-rgb), 0.5), 0 1px 2px rgba(0,0,0,0.9);
  --felt:       #182e1a;
  --felt-mid:   #1e3820;
  --felt-hi:    #254526;
  /* Surface de la table (axe "Tapis") — feutre de l'oval + lisere. Voir theme.mjs. */
  --felt-base-hi:  #1e6b1e;
  --felt-base-mid: #0f3d0f;
  --felt-base-lo:  #091f09;
  --modal-bg:   linear-gradient(160deg, #0a1f0a 0%, #142a18 50%, #08160a 100%);
  --body-glow:  rgba(50,100,50,0.18);
  --rail:          #6b3e18;
  --rail-dark:     #3a2208;
  --rail-glow:     rgba(107,62,24,0.3);
  --panel:      rgba(8,18,10,0.88);
  --panel-hi:   rgba(14,28,16,0.9);
  --gold:       #E3C800;
  --gold-rgb:   227,200,0;
  /* Couleur des ÉTOILES (étoile « moi » de la liste, badge ★N, barre de la
     carte joueur). Séparée de --gold parce que tous les thèmes ne font pas de
     --gold un or : le thème « pokerth » le met à #eff1f5 (blanc, l'accent QML)
     et garde le vrai or sous --sel. Une étoile blanche ne se lit plus comme
     une étoile, d'où cette variable — chaque thème y met SON or, avec son
     propre contraste, plutôt qu'un or figé qui jurerait sur le feutre rouge
     et deviendrait illisible en clair. */
  --star:       var(--gold);
  --chrome-tint: rgba(0,0,0,0.35);
  --pos:        #4cff88;
  --neg:        #ff6b6b;
  --info:       #7ec8e3;
  --green-rgb:  39,174,96;
  --red-rgb:    192,57,43;
  --blue-rgb:   52,152,219;
  --green2-rgb: 88,201,122;
  --orange-rgb: 230,126,34;
  --on-green:   #d7ffe0;
  --ld-quit-fg: #fad6d0;
  --ld-create-fg: #d6f5dc;
  /* Confirm-dialog buttons — couleurs QML constantes sur tous les themes.
     Dialogues quit/disconnect : les deux boutons sont neutres (contour),
     fideles au dialogue officiel. Seul Create (quick-create) = Raise vert. */
  --qml-pos-grad:       linear-gradient(180deg, #50b840, #1e6614);
  --qml-pos-grad-hi:    linear-gradient(180deg, #5ec84c, #248019);
  --qml-pos-edge:       #7ad06a;
  --qml-pos-fg:         #eafbe6;
  --wp-need-fg: #e0a040;
  --gold-hi:    #f2dc4d;
  --gold-dim:   #b09a00;
  --cream:      #ede8dc;
  --field-bg:   #0c1e0e;
  /* Texte du bouton All-In des packs a image CREUSE (Green Casino : un simple
     cadre rouge, fill="none") : c'est le fond de la barre qu'on voit dedans,
     donc la couleur doit suivre la palette et non le pack. */
  --allin-hollow-fg:   #e88a8a;
  --allin-hollow-fg-b: #f4b0b0;
  --inset: rgba(255,255,255,0.05);
  --inset-hi: rgba(255,255,255,0.08);
  --on-gold:    #110900;
  --text:       #9aaa92;
  --text-hi:    #c8d8c0;
  --red:        #c0392b;
  --green:      #27ae60;
  --orange:     #e67e22;
  --border:     rgba(var(--gold-rgb), 0.25);
  --border-hi:  rgba(var(--gold-rgb), 0.65);
  --card-back:  url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%20100%20140%27%20preserveAspectRatio%3D%27none%27%3E%3Cdefs%3E%3CradialGradient%20id%3D%27h%27%20cx%3D%2750%25%27%20cy%3D%2744%25%27%20r%3D%2780%25%27%3E%3Cstop%20offset%3D%270%27%20stop-color%3D%27%23234023%27%2F%3E%3Cstop%20offset%3D%27100%25%27%20stop-color%3D%27%230c1c0e%27%2F%3E%3C%2FradialGradient%3E%3Cpattern%20id%3D%27l%27%20width%3D%2714%27%20height%3D%2714%27%20patternUnits%3D%27userSpaceOnUse%27%3E%3Cpath%20d%3D%27M7%200L14%207L7%2014L0%207Z%27%20fill%3D%27none%27%20stroke%3D%27%23c8a84a%27%20stroke-width%3D%270.6%27%20opacity%3D%270.4%27%2F%3E%3Ccircle%20cx%3D%277%27%20cy%3D%277%27%20r%3D%270.8%27%20fill%3D%27%23c8a84a%27%20opacity%3D%270.45%27%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%27100%27%20height%3D%27140%27%20fill%3D%27url%28%23h%29%27%2F%3E%3Crect%20x%3D%276%27%20y%3D%276%27%20width%3D%2788%27%20height%3D%27128%27%20rx%3D%275%27%20fill%3D%27url%28%23l%29%27%2F%3E%3Crect%20x%3D%275%27%20y%3D%275%27%20width%3D%2790%27%20height%3D%27130%27%20rx%3D%277%27%20fill%3D%27none%27%20stroke%3D%27%23c8a84a%27%20stroke-width%3D%271.4%27%2F%3E%3Crect%20x%3D%278.5%27%20y%3D%278.5%27%20width%3D%2783%27%20height%3D%27123%27%20rx%3D%275%27%20fill%3D%27none%27%20stroke%3D%27%237a6428%27%20stroke-width%3D%270.6%27%2F%3E%3Cellipse%20cx%3D%2750%27%20cy%3D%2770%27%20rx%3D%2725%27%20ry%3D%2737%27%20fill%3D%27%2313280f%27%20stroke%3D%27%23c8a84a%27%20stroke-width%3D%270.9%27%2F%3E%3Cellipse%20cx%3D%2750%27%20cy%3D%2770%27%20rx%3D%2721%27%20ry%3D%2732.5%27%20fill%3D%27none%27%20stroke%3D%27%237a6428%27%20stroke-width%3D%270.5%27%2F%3E%3Ctext%20x%3D%2750%27%20y%3D%2784%27%20font-size%3D%2738%27%20text-anchor%3D%27middle%27%20fill%3D%27%23e0c070%27%20font-family%3D%27Georgia%2Cserif%27%3E%E2%99%A0%3C%2Ftext%3E%3C%2Fsvg%3E");
}

/* ── Palette : Sombre neutre (axe "Palette", choisie via le menu 🎨) ──
   "Casino vert" reste le défaut dans :root ci-dessus ; ce bloc ne fait
   qu'écraser les variables de couleur quand <html data-theme="dark">.
   Voir public/modules/theme.mjs. */
:root[data-theme="dark"] {
  --felt:       #1b1e24;
  --felt-mid:   #232730;
  --felt-hi:    #2c313c;
  --modal-bg:   linear-gradient(160deg, #0e1014 0%, #161a20 50%, #0c0e12 100%);
  --panel:      rgba(12,14,18,0.90);
  --panel-hi:   rgba(20,23,29,0.92);
  --gold:       #E3C800;
  --gold-hi:    #f2dc4d;
  --gold-dim:   #b09a00;
  --cream:      #ece9e2;
  --field-bg:   #12151b;
  --inset: rgba(255,255,255,0.05);
  --inset-hi: rgba(255,255,255,0.08);
  --on-gold:    #110900;
  --text:       #97a0ad;
  --text-hi:    #d4dae3;
  --red:        #c0392b;
  --green:      #27ae60;
  --orange:     #e67e22;
  --border:     rgba(var(--gold-rgb), 0.22);
  --border-hi:  rgba(var(--gold-rgb), 0.60);
  --card-back:  url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%20100%20140%27%20preserveAspectRatio%3D%27none%27%3E%3Cdefs%3E%3CradialGradient%20id%3D%27h%27%20cx%3D%2750%25%27%20cy%3D%2744%25%27%20r%3D%2780%25%27%3E%3Cstop%20offset%3D%270%27%20stop-color%3D%27%232c313c%27%2F%3E%3Cstop%20offset%3D%27100%25%27%20stop-color%3D%27%230c0e12%27%2F%3E%3C%2FradialGradient%3E%3Cpattern%20id%3D%27l%27%20width%3D%2714%27%20height%3D%2714%27%20patternUnits%3D%27userSpaceOnUse%27%3E%3Cpath%20d%3D%27M7%200L14%207L7%2014L0%207Z%27%20fill%3D%27none%27%20stroke%3D%27%23c8a84a%27%20stroke-width%3D%270.6%27%20opacity%3D%270.4%27%2F%3E%3Ccircle%20cx%3D%277%27%20cy%3D%277%27%20r%3D%270.8%27%20fill%3D%27%23c8a84a%27%20opacity%3D%270.45%27%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%27100%27%20height%3D%27140%27%20fill%3D%27url%28%23h%29%27%2F%3E%3Crect%20x%3D%276%27%20y%3D%276%27%20width%3D%2788%27%20height%3D%27128%27%20rx%3D%275%27%20fill%3D%27url%28%23l%29%27%2F%3E%3Crect%20x%3D%275%27%20y%3D%275%27%20width%3D%2790%27%20height%3D%27130%27%20rx%3D%277%27%20fill%3D%27none%27%20stroke%3D%27%23c8a84a%27%20stroke-width%3D%271.4%27%2F%3E%3Crect%20x%3D%278.5%27%20y%3D%278.5%27%20width%3D%2783%27%20height%3D%27123%27%20rx%3D%275%27%20fill%3D%27none%27%20stroke%3D%27%237a6428%27%20stroke-width%3D%270.6%27%2F%3E%3Cellipse%20cx%3D%2750%27%20cy%3D%2770%27%20rx%3D%2725%27%20ry%3D%2737%27%20fill%3D%27%231b1e24%27%20stroke%3D%27%23c8a84a%27%20stroke-width%3D%270.9%27%2F%3E%3Cellipse%20cx%3D%2750%27%20cy%3D%2770%27%20rx%3D%2721%27%20ry%3D%2732.5%27%20fill%3D%27none%27%20stroke%3D%27%237a6428%27%20stroke-width%3D%270.5%27%2F%3E%3Ctext%20x%3D%2750%27%20y%3D%2784%27%20font-size%3D%2738%27%20text-anchor%3D%27middle%27%20fill%3D%27%23e0c070%27%20font-family%3D%27Georgia%2Cserif%27%3E%E2%99%A0%3C%2Ftext%3E%3C%2Fsvg%3E");
}

/* ── Palette : PokerTH officiel (axe "Palette") ── slate neutre + accent ambré,
   couleurs reprises du client QML 2.x. Comme :root/dark, ne fait qu'ecraser les
   variables. Voir theme.mjs. */
:root[data-theme="pokerth"] {
  --ff-display: 'Inter', system-ui, sans-serif;
  --ff-mono:    'Inter', system-ui, sans-serif;
  --felt:       #161b23;
  --felt-mid:   #1d222b;
  --felt-hi:    #252c38;
  --modal-bg:   linear-gradient(160deg, #12161d 0%, #1d222b 50%, #0e1218 100%);
  --panel:      rgba(20,25,33,0.92);
  --panel-hi:   rgba(29,34,43,0.94);
  --gold:       #eff1f5;
  --gold-rgb:   239,241,245;
  --gold-hi:    #ffffff;
  --gold-dim:   #a0acc4;
  --sel:        #E3C800;
  --sel-rgb:    227,200,0;
  --sel-hi:     #ffd700;
  --star:       var(--sel);   /* --gold vaut ici le blanc de l'accent QML */
  --timer-normal: #cdd3e0;
  --cream:      #eff1f5;
  --field-bg:   #161c25;
  --inset: rgba(255,255,255,0.05);
  --inset-hi: rgba(255,255,255,0.08);
  --on-gold:    #110900;
  --text:       #a0acc4;
  --text-hi:    #eff1f5;
  --red:        #c0392b;
  --green:      #27ae60;
  --orange:     #e67e22;
  --border:     rgba(160,172,196,0.16);
  --border-hi:  rgba(var(--gold-rgb), 0.55);
  --card-back:  url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%20100%20140%27%20preserveAspectRatio%3D%27none%27%3E%3Cdefs%3E%3CradialGradient%20id%3D%27h%27%20cx%3D%2750%25%27%20cy%3D%2744%25%27%20r%3D%2780%25%27%3E%3Cstop%20offset%3D%270%27%20stop-color%3D%27%232c313c%27%2F%3E%3Cstop%20offset%3D%27100%25%27%20stop-color%3D%27%230c0e12%27%2F%3E%3C%2FradialGradient%3E%3Cpattern%20id%3D%27l%27%20width%3D%2714%27%20height%3D%2714%27%20patternUnits%3D%27userSpaceOnUse%27%3E%3Cpath%20d%3D%27M7%200L14%207L7%2014L0%207Z%27%20fill%3D%27none%27%20stroke%3D%27%236b7689%27%20stroke-width%3D%270.6%27%20opacity%3D%270.4%27%2F%3E%3Ccircle%20cx%3D%277%27%20cy%3D%277%27%20r%3D%270.8%27%20fill%3D%27%236b7689%27%20opacity%3D%270.45%27%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%27100%27%20height%3D%27140%27%20fill%3D%27url%28%23h%29%27%2F%3E%3Crect%20x%3D%276%27%20y%3D%276%27%20width%3D%2788%27%20height%3D%27128%27%20rx%3D%275%27%20fill%3D%27url%28%23l%29%27%2F%3E%3Crect%20x%3D%275%27%20y%3D%275%27%20width%3D%2790%27%20height%3D%27130%27%20rx%3D%277%27%20fill%3D%27none%27%20stroke%3D%27%236b7689%27%20stroke-width%3D%271.4%27%2F%3E%3Crect%20x%3D%278.5%27%20y%3D%278.5%27%20width%3D%2783%27%20height%3D%27123%27%20rx%3D%275%27%20fill%3D%27none%27%20stroke%3D%27%233a4254%27%20stroke-width%3D%270.6%27%2F%3E%3Cellipse%20cx%3D%2750%27%20cy%3D%2770%27%20rx%3D%2725%27%20ry%3D%2737%27%20fill%3D%27%231b1e24%27%20stroke%3D%27%236b7689%27%20stroke-width%3D%270.9%27%2F%3E%3Cellipse%20cx%3D%2750%27%20cy%3D%2770%27%20rx%3D%2721%27%20ry%3D%2732.5%27%20fill%3D%27none%27%20stroke%3D%27%233a4254%27%20stroke-width%3D%270.5%27%2F%3E%3Ctext%20x%3D%2750%27%20y%3D%2784%27%20font-size%3D%2738%27%20text-anchor%3D%27middle%27%20fill%3D%27%23a0acc4%27%20font-family%3D%27Georgia%2Cserif%27%3E%E2%99%A0%3C%2Ftext%3E%3C%2Fsvg%3E");
}

/* ── Palette : PokerTH officiel (clair) (axe "Palette") ── variante claire de
   la palette PokerTH officielle (valeurs _light du client QML). Ne fait qu'ecraser
   les variables. Voir theme.mjs. */
:root[data-theme="pokerth-light"] {
  --ff-display: 'Inter', system-ui, sans-serif;
  --ff-mono:    'Inter', system-ui, sans-serif;
  /* Rôles de surfaces QML 2.1.6 (Theme.qml) : la page est GRISE (col700
     passé de #f0f3f8 à #e3e8f0), les boxes de contenu / popups / dialogues
     sont BLANCS et se détachent de la page (avant : page blanche, boxes
     grises — rendu « sale » au lieu de hiérarchisé). Les lignes internes
     (colorPanelRow #f5f7fb) et les champs (colorField #eaeef6) restent
     légèrement enfoncés sur fond blanc, direction inversée vs le dark. */
  --felt:       #e3e8f0;
  --felt-mid:   #dce2ec;
  --felt-hi:    #d3dae6;
  --modal-bg:   #ffffff;
  --panel:      #ffffff;
  --panel-hi:   #f5f7fb;
  --gold:       #1d222b;
  --gold-rgb:   29,34,43;
  --gold-hi:    #000000;
  --gold-dim:   #576378;
  --sel:        #9a7d0a;
  --sel-rgb:    154,125,10;
  --star:       var(--sel);   /* or sombre : lisible sur fond clair */
  /* Bandeau noir meme en clair -> on garde l'or QML (sinon 3,7:1 sur le texte,
     sous le seuil AA, et bordure a 2,1:1 quasi invisible). */
  --sel-bar:     #E3C800;
  --sel-bar-rgb: 227,200,0;
  --sel-hi:     #c2a02e;
  --timer-normal: #394150;
  --timer-glow: 0 1px 1px rgba(0,0,0,0.22);
  --timer-bg:   rgba(255,255,255,0.92);
  --chrome-tint: rgba(0,0,0,0.06);
  --pos:        #1e7e43;
  --neg:        #c0392b;
  --info:       #2a6fb0;
  --green-rgb:  30,126,67;
  --red-rgb:    166,47,34;
  --blue-rgb:   42,111,176;
  --green2-rgb: 30,126,67;
  --orange-rgb: 179,94,16;
  --on-green:   #11532b;
  --ld-quit-fg: #a62f22;
  --ld-create-fg: #1e7e43;
  --wp-need-fg: #b35e10;
  --cream:      #1d222b;
  --field-bg:   #eaeef6;  /* colorField QML 2.1.6 : enfoncé même sur fond blanc */
  /* Sur --panel blanc, le rose du pack casino tombe a ~2,4:1. On reprend le
     rouge du cadre du SVG lui-meme (~5,1:1), cadre et texte s'accordent. */
  --allin-hollow-fg:   #c0392b;
  --allin-hollow-fg-b: #a4302a;
  --inset: rgba(0,0,0,0.06);
  --inset-hi: rgba(0,0,0,0.08);
  --on-gold:    #fdf6e6;
  --text:       #576378;
  --text-hi:    #1d222b;
  --red:        #a62f22;
  --green:      #1e7e43;
  --orange:     #b35e10;
  --border:     rgba(60,72,90,0.16);
  --border-hi:  rgba(var(--gold-rgb), 0.45);
  --card-back:  url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%20100%20140%27%20preserveAspectRatio%3D%27none%27%3E%3Cdefs%3E%3CradialGradient%20id%3D%27h%27%20cx%3D%2750%25%27%20cy%3D%2744%25%27%20r%3D%2780%25%27%3E%3Cstop%20offset%3D%270%27%20stop-color%3D%27%232c313c%27%2F%3E%3Cstop%20offset%3D%27100%25%27%20stop-color%3D%27%230c0e12%27%2F%3E%3C%2FradialGradient%3E%3Cpattern%20id%3D%27l%27%20width%3D%2714%27%20height%3D%2714%27%20patternUnits%3D%27userSpaceOnUse%27%3E%3Cpath%20d%3D%27M7%200L14%207L7%2014L0%207Z%27%20fill%3D%27none%27%20stroke%3D%27%236b7689%27%20stroke-width%3D%270.6%27%20opacity%3D%270.4%27%2F%3E%3Ccircle%20cx%3D%277%27%20cy%3D%277%27%20r%3D%270.8%27%20fill%3D%27%236b7689%27%20opacity%3D%270.45%27%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%27100%27%20height%3D%27140%27%20fill%3D%27url%28%23h%29%27%2F%3E%3Crect%20x%3D%276%27%20y%3D%276%27%20width%3D%2788%27%20height%3D%27128%27%20rx%3D%275%27%20fill%3D%27url%28%23l%29%27%2F%3E%3Crect%20x%3D%275%27%20y%3D%275%27%20width%3D%2790%27%20height%3D%27130%27%20rx%3D%277%27%20fill%3D%27none%27%20stroke%3D%27%236b7689%27%20stroke-width%3D%271.4%27%2F%3E%3Crect%20x%3D%278.5%27%20y%3D%278.5%27%20width%3D%2783%27%20height%3D%27123%27%20rx%3D%275%27%20fill%3D%27none%27%20stroke%3D%27%233a4254%27%20stroke-width%3D%270.6%27%2F%3E%3Cellipse%20cx%3D%2750%27%20cy%3D%2770%27%20rx%3D%2725%27%20ry%3D%2737%27%20fill%3D%27%231b1e24%27%20stroke%3D%27%236b7689%27%20stroke-width%3D%270.9%27%2F%3E%3Cellipse%20cx%3D%2750%27%20cy%3D%2770%27%20rx%3D%2721%27%20ry%3D%2732.5%27%20fill%3D%27none%27%20stroke%3D%27%233a4254%27%20stroke-width%3D%270.5%27%2F%3E%3Ctext%20x%3D%2750%27%20y%3D%2784%27%20font-size%3D%2738%27%20text-anchor%3D%27middle%27%20fill%3D%27%23a0acc4%27%20font-family%3D%27Georgia%2Cserif%27%3E%E2%99%A0%3C%2Ftext%3E%3C%2Fsvg%3E");
}

/* Inter : chiffres tabulaires pour aligner stacks/mises sur les thèmes officiels. */
:root[data-theme="pokerth"], :root[data-theme="pokerth-light"] { font-variant-numeric: tabular-nums; }

/* ── Palette : Feutre rouge (axe "Palette") ── feutre + UI + popups en rouge.
   Comme :root/dark, ne fait qu'ecraser les variables. Voir theme.mjs. */
:root[data-theme="redfelt"] {
  --felt:       #2c1012;
  --felt-mid:   #3a1418;
  --felt-hi:    #4c1c20;
  --felt-base-hi:  #8e1e1e;
  --felt-base-mid: #4a0e0e;
  --felt-base-lo:  #280606;
  --rail:          #5a2410;
  --rail-dark:     #2e1206;
  --rail-glow:     rgba(150,40,40,0.30);
  --panel:      rgba(26,8,10,0.93);
  --panel-hi:   rgba(38,12,14,0.94);
  --gold:       #c8a84a;
  --gold-hi:    #e0c070;
  --gold-dim:   #7a6428;
  --cream:      #f2e6e2;
  --field-bg:   #1e0a0c;
  --inset: rgba(255,255,255,0.05);
  --inset-hi: rgba(255,255,255,0.08);
  --on-gold:    #110900;
  --text:       #bd9d9a;
  --text-hi:    #ecd6d2;
  --red:        #c0392b;
  --green:      #27ae60;
  --orange:     #e67e22;
  --border:     rgba(var(--gold-rgb), 0.25);
  --border-hi:  rgba(var(--gold-rgb), 0.65);
  --modal-bg:   linear-gradient(160deg, #2c0e10 0%, #3c1519 50%, #1f0709 100%);
  --body-glow:  rgba(140,40,40,0.16);
}

/* ── Tapis (axe "Tapis", choisi via le menu dedie) ─ surcharge la SURFACE
   de la table (feutre + lisere) independamment de la Palette.
   Defaut (vert) = :root ci-dessus. Voir public/modules/theme.mjs. */
:root[data-table="pokerth"] {
  --felt-base-hi:  #008c58;
  --felt-base-mid: #006a44;
  --felt-base-lo:  #073120;
  --rail:          #1d222b;
  --rail-dark:     #12161d;
  --rail-glow:     rgba(0,140,88,0.20);
}
:root[data-table="blue"] {
  --felt-base-hi:  #1f6aa8;
  --felt-base-mid: #124b78;
  --felt-base-lo:  #08233a;
}
:root[data-table="bordeaux"] {
  --felt-base-hi:  #8a1e2e;
  --felt-base-mid: #5e0f1d;
  --felt-base-lo:  #330810;
}
:root[data-table="slate"] {
  --felt-base-hi:  #3a4150;
  --felt-base-mid: #232730;
  --felt-base-lo:  #14171d;
  --rail:          #2b2f38;
  --rail-dark:     #15171c;
  --rail-glow:     rgba(120,130,150,0.18);
}
/* Feutre texture (photo) — garde l'oval + le lisere CSS, remplace seulement
   le remplissage par un vrai feutre extrait de l'image PokerTH (sans lisere,
   donc pas de double lisere ni de distorsion). Asset : /table/felt-green.jpg
   (PokerTH, AGPL-3.0). */
:root[data-table="photo"] .felt-oval {
  background:
    radial-gradient(ellipse 90% 80% at 50% 50%, transparent 55%, rgba(0,0,0,0.40) 100%),
    url("/table/felt-green.jpg") center / cover no-repeat;
}

/* Table « image complète » — une table importée marquée full:true (tables.json /
   themes.json) remplace TOUT le rendu CSS du tapis (bordure, liserés box-shadow,
   liseré ::before, dégradés texture/halo, forme ovale) pour n'afficher QUE l'image,
   centrée et entière (contain). Les feutres-texture (photo, gallery sans full) ne
   sont pas affectés : eux gardent l'ovale + le liseré. L'image vit dans --table-img
   (URL nue, sans cover) ; data-table-full="1" est posé par theme.mjs et l'applier <head>. */
:root[data-table-full="1"] .felt-oval {
  background: var(--table-img, none) center / contain no-repeat;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  filter: drop-shadow(0 12px 18px rgba(0,0,0,0.40));
}
:root[data-table-full="1"] .felt-oval::before { display: none; }
/* Full image (data-table-full): the table PNG carries its own felt + rail with
   transparent corners, so the frame/zone behind it must stay transparent —
   otherwise a light panel shows through the corners. Scoped to full mode only. */
:root[data-table-full="1"] .table-scaler,
:root[data-table-full="1"] .table-zone { background: transparent !important; }

/* Table « Fullscreen » : l'image du pack part en fond plein ecran (--wallpaper,
   active par data-bg-img -> regle body.in-game ci-dessous) et l'ovale devient
   invisible. data-table-fs est pose par theme.mjs (pack table en mode fullscreen). */
:root[data-table-fs="1"] .felt-oval {
  background: transparent;
  border: 0;
  box-shadow: none;
}
:root[data-table-fs="1"] .felt-oval::before { display: none; }

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Taille minimale supportée (déclarative, pas de plancher CSS — html/body sont en
   overflow:hidden, un min-width clipperait) : 390×600 sur desktop (parité fenêtre
   min du client Qt/QML) et largeur ≥ 360 sur mobile. En dessous, rendu dégradé. */
html, body {
  height: 100%;
  height: 100dvh;
  overflow: hidden;
  overscroll-behavior: none;
  font-family: var(--ff-mono);
  background: var(--felt);
  color: var(--text);
}

body {
  background-image:
    radial-gradient(ellipse 140% 70% at 50% 0%, var(--body-glow) 0%, transparent 65%),
    repeating-linear-gradient(45deg, transparent, transparent 3px,
      rgba(0,0,0,0.04) 3px, rgba(0,0,0,0.04) 6px);
}
/* Palette PokerTH claire : fond plat #e3e8f0 (QML col700 light 2.1.6), sans rayures. */
:root[data-theme="pokerth-light"] body { background-image: none; }

/* Fond « Fullscreen » : l'image (--wallpaper) est posee sur la ZONE DE TABLE
   (au-dessus de la barre d'action) et non sur tout le body -> le bas de l'image
   (lisere) reste visible juste au-dessus de la barre. Ancrage bas + cover.
   .table-zone (flex:1) occupe la zone de jeu au-dessus de la barre d'action ;
   l'ovale (.felt-oval) est transparent et laisse voir ce fond. data-table-fs est
   pose par theme.mjs. S'applique a TOUS les tapis en mode fullscreen. */
/* Fullscreen : le feutre couvre TOUT l'ecran de jeu (#s-game, 100dvh) -> il
   descend jusqu'en bas, derriere la barre d'action, et l'entoure. La zone de
   table devient transparente (le fond vient de #s-game), et la barre d'action
   + player-bar sont transparentes ; seul le panneau des boutons (.action-grid)
   flotte sur le feutre. Scoping data-table-fs -> n'affecte que le mode fullscreen. */
:root[data-table-fs="1"] #s-game {
  background-image:
    linear-gradient(rgba(0,0,0,0.14), rgba(0,0,0,0.14)),
    var(--wallpaper, none);
  /* --wallpaper-dyn-* : cadrage QML « center » calculé par renderSeats
     (parité tableBackgroundImage : couvre la bande zone+action bar, centré
     sur communityCenterY, × TableBackgroundZoom). Prioritaire ; repli sur
     le comportement historique (--wallpaper-size / cover, pos / bas). */
  background-size: var(--wallpaper-dyn-size, var(--wallpaper-size, cover));   /* 2.1.3: TableBackgroundZoom (import) surcharge cover */
  background-position: var(--wallpaper-dyn-pos, var(--wallpaper-pos, center bottom));
  background-repeat: no-repeat;
}
/* Portrait (iPhone) : garantir un fond PLEIN ÉCRAN même quand le cadrage
   dynamique (--wallpaper-dyn-*) n'est pas encore posé (image en cours de
   chargement) ou indisponible. Sinon le repli --wallpaper-size (ex. 130%,
   TableBackgroundZoom) = 130% de la LARGEUR, hauteur proportionnelle → une
   bande courte sur écran étroit. Ici le repli devient 'cover' = plein écran.
   Le dynamique, quand présent, reste prioritaire (crop centré × zoom). */
@media (orientation: portrait) {
  :root[data-table-fs="1"] #s-game {
    background-size: var(--wallpaper-dyn-size, cover);
    background-position: var(--wallpaper-dyn-pos, center);
  }
}
:root[data-table-fs="1"] .table-zone { background-image: none; }
:root[data-table-fs="1"] .my-zone,
:root[data-table-fs="1"] .player-bar { background: transparent !important; border-top: 0 !important; }
:root[data-table-fs="1"] .my-zone .action-grid {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 8px 10px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.38);
}
/* Panneau d'attente (« EN ATTENTE / + BOTS ») : hors partie il occupe la barre
   d'action -> lui aussi doit flotter sur le feutre avec le fond de palette,
   sinon il devient transparent en fullscreen. */
:root[data-table-fs="1"] .my-zone .waiting-panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: 0 6px 20px rgba(0,0,0,0.38);
}

/* ─── SCREENS ─── */
.screen { display: none; height: 100vh; height: 100dvh; }

/* ── Installed PWA: make the layout box the WHOLE screen ──
   On iOS, an installed web app resolves 100dvh (and 100vh) from a viewport
   that can stay one relayout behind: when the screen content fits without
   scrolling, the value keeps excluding the home-indicator inset. The .screen
   then stops short of the physical bottom and the html/body background shows
   through as a stray band under the connect / lobby / game screens (reported
   narmod 2026-08-01, iPhone + iPad; the band vanished as soon as the music
   player made the page overflow and forced a relayout).

   position:fixed + inset:0 is resolved against the *visual* viewport, which
   with viewport-fit=cover really is the full screen — this is why the two
   overlays already built that way (#about-page, #privacy-page) never showed
   the band. Anchoring body the same way and sizing .screen off it removes the
   dependency on dvh entirely.

   Scoped to the installed app on purpose: in a normal browser tab dvh is the
   correct unit (it tracks the collapsing toolbars) and body must stay in flow. */
@media (display-mode: fullscreen), (display-mode: standalone) {
  body { position: fixed; inset: 0; height: auto; }
  .screen { height: 100%; }

  /* The screens now reach the physical bottom edge, so whatever sits at the
     bottom of one must clear the home indicator itself. Fixed-position bars
     (.player-bar, .my-zone) already do; these are in normal / absolute flow. */
  .connect-footer { bottom: max(10px, env(safe-area-inset-bottom)); }
  .lobby-footbar  { padding-bottom: max(8px, env(safe-area-inset-bottom)); }
  .cp-scroll      { padding-bottom: calc(40px + env(safe-area-inset-bottom)); }
  .pv-scroll      { padding-bottom: env(safe-area-inset-bottom); }
  .ab-body        { padding-bottom: calc(28px + env(safe-area-inset-bottom)); }
}
/* Installed app, measured-viewport variant (--app-h set by an inline script
   in pokerth-client.html; full rationale there). Explicit height everywhere
   an element was sized with dvh — html, body and the screens — and the same
   safe-area paddings as the media-query block above. No position:fixed and
   no dvh in this path (both proven broken on iOS/iPadOS 26, 2026-08-08).
   Falls back to 100dvh until the first measurement lands. */
:root[data-pwa="1"], :root[data-pwa="1"] body { height: var(--app-h, 100dvh); }
:root[data-pwa="1"] .screen { height: var(--app-h, 100dvh); }
:root[data-pwa="1"] .connect-footer { bottom: max(10px, env(safe-area-inset-bottom)); }
:root[data-pwa="1"] .lobby-footbar  { padding-bottom: max(8px, env(safe-area-inset-bottom)); }
:root[data-pwa="1"] .cp-scroll      { padding-bottom: calc(40px + env(safe-area-inset-bottom)); }
:root[data-pwa="1"] .pv-scroll      { padding-bottom: env(safe-area-inset-bottom); }
:root[data-pwa="1"] .ab-body        { padding-bottom: calc(28px + env(safe-area-inset-bottom)); }
:fullscreen            { background: var(--felt); }
:-webkit-full-screen   { background: var(--felt); }
:-moz-full-screen      { background: var(--felt); }
.screen.active { display: flex; }

/* ── Écran dédié « Créer une partie » (page pleine, garde le formulaire existant) ── */
#s-create { flex-direction: column; background: var(--field-bg); }
.cp-header {
  display: flex;
  align-items: center;
  gap: 14px;
  /* Hauteur UNIFORME avec tous les autres headers (--header-h, base QML
     2.1.3 : 38 px / 30 en landscapeCompact) : padding vertical 4 px et
     bouton Retour a 30 px comme les boutons des autres headers. */
  padding: 4px clamp(8px,2vw,20px);
  min-height: var(--header-h, 38px);
  box-sizing: border-box;
  border-bottom: 1px solid var(--border);
  /* Meme fond que les .header globaux (etait --chrome-tint, trop discret). */
  background: var(--panel);
  flex-shrink: 0;
}
/* Pousse le globe + roue crantée à droite (parité header lobby). */
.cp-head-spacer { flex: 1; }
.cp-title {
  font-family: var(--ff-display);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--text-hi);
  letter-spacing: 0.02em;
}
/* Titre centré dans le header (entre la porte à gauche et globe/roue à
   droite) : positionnement absolu au centre du .cp-header — insensible aux
   largeurs inégales des deux groupes d'icônes. max-width borné pour ne
   jamais chevaucher les icônes ; pointer-events off (label non cliquable). */
#s-create .cp-title,
#privacy-page .cp-title {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  max-width: 60%;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}
.cp-scroll { flex: 1; overflow-y: auto; padding: 20px 18px 40px; }
.cp-body { max-width: 720px; margin: 0 auto; }
.cp-body #create-form { display: block; }

/* ── Champs « Créer une partie » façon officiel (steppers / toggles / sections) ── */
.cf-block { margin-bottom: 14px; }
.cf-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 9px 0; }
.cf-row-col { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
.cf-lbl { font-size: var(--cf-fs-body); color: var(--text); font-weight: 500; }
.cf-input, .cf-select {
  width: 100%; box-sizing: border-box;
  background: var(--field-bg); border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); padding: 9px 12px; font-size: var(--cf-fs-body); outline: none;
}
.cf-input:focus, .cf-select:focus { border-color: var(--gold-dim); }
.cf-sec {
  margin: 16px 0 2px; padding-top: 14px;
  font-size: var(--cf-fs-body); font-weight: 700; color: var(--text);
  letter-spacing: 0.02em; text-transform: none;
  border-top: 1px solid var(--border);
}
/* Stepper − valeur + */
.cf-step { display: inline-flex; align-items: center; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: var(--field-bg); }
.cf-step-btn {
  width: 34px; height: 34px; background: transparent; border: none;
  color: var(--text); font-size: 1.15rem; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.cf-step-btn:hover { background: rgba(127,127,127,0.14); }
.cf-step-num {
  width: 72px; border: none; background: transparent; color: var(--text);
  text-align: center; font-size: var(--cf-fs-value); font-family: var(--ff-display);
  outline: none; -moz-appearance: textfield;
}
.cf-step-num::-webkit-inner-spin-button, .cf-step-num::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.cf-step-sm .cf-step-num { width: 48px; }
/* Toggle switch */
.cf-switch { position: relative; display: inline-block; width: 44px; height: 24px; cursor: pointer; flex: none; }
.cf-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.cf-switch-tr { position: absolute; inset: 0; background: var(--border); border-radius: var(--r-pill); transition: background 0.16s; }
.cf-switch-tr::before {
  content: ''; position: absolute; width: 18px; height: 18px; left: 3px; top: 3px;
  background: #fff; border-radius: 50%; transition: transform 0.16s; box-shadow: 0 1px 3px rgba(0,0,0,0.35);
}
.cf-switch input:checked + .cf-switch-tr { background: #2f81f7; }
.cf-switch input:checked + .cf-switch-tr::before { transform: translateX(20px); }
/* Intervalle blindes : radios + stepper */
.cf-interval { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-6); }
.cf-radio { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--cf-fs-body); color: var(--text); cursor: pointer; }
.cf-radio input { accent-color: #2f81f7; }
.cf-create-btn { flex: 1; padding: var(--sp-5); }

/* ══════════════════════════════════════════
   CONNECT SCREEN
══════════════════════════════════════════ */
#s-connect {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow-y: auto;
  padding: 20px 16px;
  /* PokerTH official login backdrop (flames) — login screen only, theme-agnostic.
     Dark veil keeps the centered .card readable over the bright artwork. */
  background:
    linear-gradient(rgba(8,10,14,0.10), rgba(8,10,14,0.15)),
    url("/img/pokerth-login-bg.webp") center / cover no-repeat;
}

/* Chrome Android : les items flex de la carte login (select de mode, input
   pseudo/host) ont min-width:auto = leur largeur intrinsèque et ne peuvent
   pas rétrécir → la rangée déborde de la carte et le bouton avatar sort de
   l'écran. min-width:0 rend les rangées compressibles ; les boutons 🐼/⚙
   gardent leur taille (flex-shrink:0). Même leçon que la barre d'action. */
.card .sel-wrap { min-width: 0; }
.card .f input { min-width: 0; }

.form-card, .card {
  background: var(--modal-bg);
  border: 1px solid var(--border-hi);
  border-radius: 4px;
  padding: 20px 22px;
  width: min(380px, calc(100vw - 32px));
  display: flex;
  flex-direction: column;
  gap: 11px;
  box-shadow: 0 12px 40px rgba(0,0,0,0.8);
}

.card-title {
  font-family: var(--ff-display);
  font-size: var(--fs-xs);
  letter-spacing: 0.3em;
  color: var(--gold-dim);
  text-transform: uppercase;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
}

.card-logo {
  text-align: center;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
}
.card-suits-row { font-size: var(--fs-lg); letter-spacing: 0.2em; opacity: 0.7; margin-bottom: 4px; }
.card-suits-row .r { color: var(--red); }
.card-title-big {
  font-family: var(--ff-display);
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--text-hi);
  letter-spacing: 0.12em;
  line-height: 1;
}
.card-subtitle { font-size: var(--fs-2xs); letter-spacing: 0.4em; text-transform: uppercase; color: var(--text); }
.card-chip {
  display: block;
  margin: 0 auto;
  /* Parité client QML (Theme.brandLogoSize) : plafond 126 px desktop /
     100 px phone, plancher 56 px, mise à l'échelle fluide (narmod 19/07).
     Les media-queries max-height ci-dessous prennent le relais sur écrans
     très courts, comme le compact QML. */
  width: clamp(56px, 13vw, 126px);
  height: clamp(56px, 13vw, 126px);
  filter: drop-shadow(0 4px 12px rgba(0,0,0,0.55));
}
@media (max-width: 600px) {
  .card-chip { width: clamp(56px, 22vw, 100px); height: clamp(56px, 22vw, 100px); }  /* compact QML : plafond 100 */
}

/* Small / short screens (e.g. iPhone SE, landscape): compact the login
   card so it fits without scrolling. The header block (chip, title, suits,
   subtitle) is the biggest space user, so it shrinks the most. !important
   overrides the inline sizing set in the markup. */
/* Paliers progressifs de réduction de la carte de connexion (760/640) :
   raffinements fluides pour écrans peu hauts, pas des frontières de classe. */
@media (max-height: 760px) {
  #s-connect { padding: 8px 14px; }
  .card { gap: var(--sp-3); padding: 14px 18px; }
  .card-logo { padding: 8px 0 10px !important; }
  .card-chip { width: 54px; height: 54px; }
  .card-title-big { font-size: 1.5rem !important; margin-top: 6px !important; }
  .card-suits-row { font-size: var(--fs-md) !important; margin-top: 5px !important; }
  .card-subtitle { font-size: var(--fs-3xs) !important; margin-top: 4px !important; }
  .f { gap: var(--sp-1); }
  .f input { padding: 7px 10px; font-size: var(--fs-base); }
  .sel-wrap select { padding: 7px 30px 7px 10px; }
  .login-avatar-top #av-trigger { width: 72px; height: 72px; font-size: 2.1rem; }
  #login-step2 { gap: var(--sp-3); }
  .btn-primary { padding: var(--sp-4); }
  .tls-row { font-size: var(--fs-xs); }
}
@media (max-height: 640px) {
  .card-chip { width: 44px; height: 44px; }
  .card-title-big { font-size: var(--fs-xl) !important; }
  .card-suits-row { display: none !important; }
  .card { gap: var(--sp-2); }
  #login-step2 { gap: var(--sp-2); }
}

.sel-wrap { position: relative; }
.sel-wrap select {
  -webkit-appearance: none; appearance: none;
  width: 100%;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: 2px;
  color: var(--cream);
  padding: 9px 32px 9px 11px;
  font-family: var(--ff-mono);
  font-size: var(--fs-base);
  outline: none; cursor: pointer;
}
.sel-wrap select:focus { border-color: var(--gold); }
.sel-wrap select option { background: var(--field-bg); color: var(--cream); }
.sel-arr { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); color: var(--gold); pointer-events: none; font-size: var(--fs-sm); }

/* ── Dropdown « Type de partie » (parité QML StyledCombo : icônes champ + liste) ── */
.gt-dd { position: relative; }
.gt-dd-native { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.gt-dd-btn {
  width: 100%; box-sizing: border-box; display: flex; align-items: center; gap: 9px;
  background: var(--field-bg); border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); padding: 9px 32px 9px 12px; font-size: var(--fs-md); font-family: inherit;
  cursor: pointer; text-align: left; position: relative;
}
.gt-dd-btn:focus { outline: none; border-color: var(--gold-dim); }
.gt-dd-lbl { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gt-dd-ico {
  width: 16px; height: 16px; flex: 0 0 16px; background-color: currentColor;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-size: contain; mask-size: contain;
}
.gt-ico-1 { -webkit-mask-image: url('img/gametype/user.svg');       mask-image: url('img/gametype/user.svg'); }
.gt-ico-2 { -webkit-mask-image: url('img/gametype/userSquare.svg'); mask-image: url('img/gametype/userSquare.svg'); }
.gt-ico-3 { -webkit-mask-image: url('img/gametype/users.svg');      mask-image: url('img/gametype/users.svg'); }
.gt-ico-4 { -webkit-mask-image: url('img/gametype/chipStack.svg');  mask-image: url('img/gametype/chipStack.svg'); }
.gt-dd-list {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 40;
  background: var(--field-bg); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.35); overflow: hidden;
}
.gt-dd-item {
  display: flex; align-items: center; gap: 9px; padding: 9px 12px;
  color: var(--text); font-size: var(--fs-md); cursor: pointer;
}
.gt-dd-item:hover, .gt-dd-item.sel { background: rgba(255,255,255,0.07); }
:root[data-theme="pokerth-light"] .gt-dd-item:hover,
:root[data-theme="pokerth-light"] .gt-dd-item.sel { background: rgba(0,0,0,0.07); }

/* Quick-bets 1/3·1/2·Pot masquables (parité QML ShowPotPercentButtons) */
body.adv-no-potbtns .pct-row { display: none !important; }
/* Contenus communauté masquables (parité QML showCommunityContent) */
body.adv-no-communitycontent #ranking-modal .rk-tab[data-src="bbc"],
body.adv-no-communitycontent #ranking-modal .rk-tab[data-src="wec"] { display: none !important; }
body.adv-no-communitycontent .adv-row:has(#adv-defcommunity) { display: none !important; }

/* Verrous du formulaire de création (parité QML : classé / invité) */
.cf-locked { opacity: 0.55; pointer-events: none; }
.gt-dd-btn:disabled { opacity: 0.55; cursor: default; pointer-events: none; }
#cf-name:disabled { opacity: 0.7; }
.cf-name-err { color: #ef4444; font-size: var(--cf-fs-xs); margin-top: 4px; }

.avatar-picker {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 5px;
}
.av-btn {
  background: var(--inset);
  border: 2px solid var(--border);
  border-radius: var(--r-sm);
  font-size: 1.25rem;
  cursor: pointer;
  padding: 4px 2px;
  transition: border-color 0.15s, transform 0.1s, background 0.15s;
  line-height: 1.2;
  text-align: center;
  color: var(--text-hi);
}
.av-btn:hover { border-color: var(--gold-dim); transform: scale(1.1); background: rgba(var(--gold-rgb), 0.08); }
.av-btn.selected { border-color: var(--sel,var(--gold)); background: rgba(var(--sel-rgb,var(--gold-rgb)), 0.18); box-shadow: 0 0 6px rgba(var(--sel-rgb,var(--gold-rgb)), 0.3); }
.av-btn.av-none { font-size: var(--fs-xs); font-family: var(--ff-display); color: var(--text); letter-spacing: 0.05em; }

/* Emoji avatar dans le siège */
.seat-avatar.emoji-av .seat-initial { font-size: 1.5em; line-height: 1; }


/* ── Clickable pseudo pill in the lobby header ──
   The #h-nick pill in the lobby now contains a tiny avatar (emoji or
   PokerTH image) + the name. .pill-clickable adds the cursor + hover
   feedback without disturbing the existing .pill positioning. */
.pill.pill-clickable {
  cursor: pointer;
  user-select: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  transition: background 0.15s, border-color 0.15s;
}
.pill.pill-clickable:hover {
  background: rgba(var(--gold-rgb), 0.22);
  border-color: var(--gold-dim);
}
.pill .h-nick-av {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  font-size: 0.9em;
  line-height: 1;
}
.pill .h-nick-av img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  pointer-events: none;
  display: block;
}

/* ── Clickable "TABLE" label in the game header ──
   Subtle underline on hover so the user notices it's interactive,
   without changing the visual weight of the header. */
#g-name.g-name-clickable {
  cursor: pointer;
  border-bottom: 1px dashed transparent;
  transition: border-color 0.15s, color 0.15s;
}
#g-name.g-name-clickable:hover {
  border-bottom-color: var(--gold);
  color: var(--gold);
}

/* ── En-tête de jeu : nom de table centré + badges de statut (palette QML) ──
   Le libellé statique « TABLE » devient le nom réel de la partie, centré sur
   la barre (position absolue → indépendant de la largeur des boutons), suivi
   de badges Admin (or #E3C800) et Public/Privé (vert #27ae60). Reste cliquable
   → openGameInfoPopup(). Le nom tronque en « … » ; les badges ne rétrécissent
   pas (le nom cède en premier). */
.header-brand--game {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  max-width: min(72vw, 560px);
  justify-content: center;
  pointer-events: none;   /* les zones vides laissent passer les clics aux boutons */
  z-index: 1;
}
.header-brand--game .g-brand-click {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
  max-width: 100%;
  cursor: pointer;
}
/* Annule l'atténuation globale `.header-brand span { opacity:.6 }` (definie
   plus bas → on double la classe pour gagner en specificite). */
.header-brand.header-brand--game span { opacity: 1; font-size: inherit; }
.header-brand--game #g-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-hi);
  transition: color 0.15s;
}
.header-brand--game .g-brand-click:hover #g-name { color: var(--gold); }
.g-brand-sep { color: var(--text); opacity: 0.45; flex-shrink: 0; }
.g-status-badge {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  font-family: var(--ff-mono);
  font-size: 0.42em;
  letter-spacing: 0.04em;
  padding: 1px 5px;
  border-radius: 4px;
  gap: 3px;
  white-space: nowrap;
  line-height: 1;
}
.gsb-label { margin-left: 0; }
.g-status-badge svg { flex: none; width: 1.4em; height: 1.4em; }
/* Spécificité (0,3,0) pour battre `.header-brand.header-brand--game span
   { font-size: inherit }` (0,2,1) qui, sinon, force le texte du badge à la
   taille pleine du nom de table. Ici : texte = 0.45em du nom de table. */
.header-brand--game .g-brand-click .g-status-badge { font-size: 0.45em; }
.g-status-admin {
  color: var(--sel, var(--gold));
  background: rgba(var(--sel-rgb, var(--gold-rgb)), 0.12);
  border: 1px solid rgba(var(--sel-rgb, var(--gold-rgb)), 0.35);
}
.g-status-public {
  color: rgb(var(--blue-rgb));
  background: rgba(var(--blue-rgb), 0.12);
  border: 1px solid rgba(var(--blue-rgb), 0.35);
}
.g-status-private {
  color: var(--text);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border);
}
/* Petits écrans : on resserre pour ne pas mordre sur les boutons du header. */
@media (max-width: 599.98px) {
  .header-brand--game { max-width: 82vw; }
  /* Emoji seul (le libelle deborde sur petit ecran). */
  .header-brand--game .gsb-label { display: none; }
  .g-status-badge { padding: 1px 5px; }
  .header-brand--game .g-brand-click { gap: var(--sp-1); }
}

/* ── Statut d'attente centre dans le header du lobby ──
   Affiche « En attente de joueurs » + points animes tant que la partie
   creee/rejointe n'a pas demarre. Centre en absolu (independant des
   boutons), non cliquable (laisse passer les clics). Palette QML (or). */
.lobby-wait-status {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  gap: 7px;
  max-width: min(70vw, 460px);
  font-family: var(--ff-display);
  font-size: clamp(0.72rem, 1.8vw, 0.95rem);
  letter-spacing: 0.05em;
  color: var(--gold);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  z-index: 1;
}
.lws-hourglass {
  font-size: 0.95em;
  display: inline-block;
  animation: lwsHourglass 2.6s ease-in-out infinite;
}
@keyframes lwsHourglass {
  0%, 78%, 100% { transform: rotate(0deg); }
  90%          { transform: rotate(180deg); }
}
.wait-dots { display: inline-flex; }
.wait-dots span {
  opacity: 0.25;
  animation: waitDot 1.4s ease-in-out infinite;
}
.wait-dots span:nth-child(2) { animation-delay: 0.2s; }
.wait-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes waitDot {
  0%, 100% { opacity: 0.25; }
  30%      { opacity: 1; }
}
@media (max-width: 599.98px) {
  .lobby-wait-status { max-width: 62vw; font-size: var(--fs-xs); gap: 5px; }
}
@media (prefers-reduced-motion: reduce) {
  .lws-hourglass { animation: none; }
  .wait-dots span { animation: none; opacity: 0.85; }
}

/* ── Game-info modal (opened by clicking the "TABLE" label) ──
   Centered card with backdrop. The card holds a title (table name)
   + a subtitle (admin/private badges), and a body filled by JS with
   a 2-column grid of label/value rows grouped by section. */
/* Meme bascule que #player-info-modal ci-dessus : fenetre generique, pas de
   voile noir, z-index dans la bande geree. */
#game-info-modal {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Idem : marge d'au moins var(--sp-6), davantage si l'appareil declare des
     zones sures. */
  padding: max(var(--sp-6), env(safe-area-inset-top)) max(var(--sp-6), env(safe-area-inset-right))
           max(var(--sp-6), env(safe-area-inset-bottom)) max(var(--sp-6), env(safe-area-inset-left));
  pointer-events: none;
}
#game-info-modal .gim-card { pointer-events: auto; }
.gim-backdrop { display: none; }
.gim-card {
  position: relative;
  background: var(--modal-bg);
  border: 1px solid var(--gold-dim);
  border-radius: 8px;
  padding: 18px 18px 16px;
  width: min(360px, 100%);
  max-height: min(80vh, 600px);
  overflow-y: auto;
  box-shadow: 0 8px 32px rgba(0,0,0,0.6);
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  animation: slideDown 0.18s ease;
  -webkit-overflow-scrolling: touch;
}
.gim-close {
  position: absolute;
  top: 8px;
  right: 10px;
  background: none;
  border: none;
  color: var(--text);
  cursor: pointer;
  font-size: var(--fs-md);
  padding: 2px 6px;
  line-height: 1;
  z-index: 2;
}
.gim-close:hover { color: var(--gold); }
.gim-header {
  text-align: center;
  border-bottom: 1px solid var(--border);
  padding-bottom: 10px;
  padding-right: 24px; /* keep title from going under the ✕ */
}
.gim-title {
  font-size: 1.15rem;
  color: var(--cream);
  font-family: var(--ff-display);
  font-weight: 600;
  letter-spacing: 0.04em;
  word-break: break-word;
}
.gim-subtitle {
  font-size: var(--fs-xs);
  color: var(--gold-dim);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-top: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
}
.gim-badge {
  background: rgba(var(--gold-rgb), 0.12);
  border: 1px solid var(--gold-dim);
  border-radius: var(--r-xs);
  padding: 1px 6px;
  color: var(--gold);
  letter-spacing: 0.1em;
  font-size: var(--fs-2xs);
}
.gim-section {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}
/* Breathing room above each section heading except the first, so
   'CONFIGURATION' / 'GAME STATE' don't sit tight against the row above. */
.gim-section + .gim-section { margin-top: 12px; }
.gim-section-title {
  font-size: var(--fs-2xs);
  color: var(--gold-dim);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-bottom: 1px solid rgba(var(--gold-rgb), 0.15);
  padding-bottom: 4px;
  margin-bottom: 2px;
}
.gim-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-5);
  padding: 3px 0;
  font-size: var(--fs-sm);
}
.gim-row-label {
  color: var(--text);
  font-family: var(--ff-mono);
}
.gim-row-value {
  color: var(--cream);
  font-family: var(--ff-mono);
  font-weight: 500;
  text-align: right;
}

/* ── Player-info modal (opened by clicking #h-nick) ──
   Centered card with backdrop. The pim-card is sized so it fits
   comfortably on phones; the avatar is big (~96px) so the user
   can clearly see what they have. */
/* Modele de fenetre generique (comme le classement, le forum, les messages
   prives) : plus de voile noir plein ecran, le lobby reste lisible et
   utilisable derriere ; z-index dans la bande geree par z-order.mjs (1200) au
   lieu de 9999, sinon la carte passerait devant toutes les autres fenetres
   quoi qu'il arrive. pointer-events: none sur le conteneur pour que seul le
   panneau intercepte les clics. */
#player-info-modal {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  /* Zones sures : plus haute que l'ecran, la carte se collait au bord et
     passait sous la barre d'etat / l'encoche sur iPhone. */
  padding: max(8px, env(safe-area-inset-top)) max(8px, env(safe-area-inset-right))
           max(8px, env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-left));
}
#player-info-modal .pim-card { pointer-events: auto; }
.pim-backdrop { display: none; }
.pim-card {
  position: relative;
  background: var(--modal-bg);
  border: 1px solid var(--gold-dim);
  border-radius: 8px;
  padding: 24px 20px 18px;
  min-width: 260px;
  max-width: 320px;
  /* 100% du conteneur, qui est deja retreci par les zones sures ci-dessus :
     un calc() sur 100dvh les ignorerait et rendrait le padding inoperant. */
  max-height: 100%;
  overflow-y: auto;
  box-shadow: 0 8px 32px rgba(0,0,0,0.6);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-5);
  animation: slideDown 0.18s ease;
}
/* Children keep their intrinsic height; the card scrolls instead of squashing
   them (notably the avatar) when the cups make it taller than the viewport. */
.pim-card > * { flex-shrink: 0; }
.pim-close {
  position: absolute;
  top: 8px;
  right: 10px;
  background: none;
  border: none;
  color: var(--text);
  cursor: pointer;
  font-size: var(--fs-md);
  padding: 2px 6px;
  line-height: 1;
}
.pim-close:hover { color: var(--gold); }
.pim-avatar {
  width: 96px;
  height: 96px;
  border-radius: var(--r-lg);
  background: rgba(0,0,0,0.5);
  border: 2px solid var(--gold);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3rem;
  line-height: 1;
  overflow: hidden;
  box-shadow: 0 0 14px rgba(var(--gold-rgb), 0.35);
}
.pim-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--r-bar);
  pointer-events: none;
  display: block;
}
.pim-avatar.is-letter {
  font-size: 2.4rem;
  font-family: var(--ff-display);
  font-weight: 700;
  color: var(--gold);
}
/* Avatar cliquable : ouvre le sélecteur d'avatar, comme le bouton. */
.pim-avatar-clickable { cursor: pointer; transition: box-shadow 0.2s, transform 0.2s; }
.pim-avatar-clickable:hover,
.pim-avatar-clickable:focus-visible {
  box-shadow: 0 0 20px rgba(var(--gold-rgb), 0.75);
  transform: scale(1.03);
  outline: none;
}
.pim-name {
  font-size: var(--fs-lg);
  color: var(--cream);
  font-weight: 600;
  text-align: center;
  word-break: break-word;
}
/* Drapeau pays sous l'avatar (popup de profil) */
.pim-flag {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  letter-spacing: 0.06em;
  color: var(--gold-dim);
  margin-top: -2px;
}
.pim-flag-code { font-family: var(--ff-mono); }
/* Image drapeau auto-hébergée (rendu identique sur tous les OS). */
.cc-flag {
  display: inline-block;
  vertical-align: middle;
  object-fit: cover;
  border-radius: 2px;
  box-shadow: 0 0 0 0.5px rgba(0,0,0,0.35);
}
.cc-flag-lg { width: 26px; height: 19px; border-radius: var(--r-xs); }
/* ── Bloc stats du popup de profil : onglets SESSION/TOTAL/CLASSEMENT,
   identiques au panneau en jeu (réutilise .stats-tabs/.stats-body/.stats-reset). ── */
.pim-stats {
  width: 100%;
  margin: 4px 0 2px;
}
.pim-stats .stats-tabs { margin-bottom: 4px; }
.pim-stats .stats-body { padding: 8px 2px 2px; }
/* ── Popup d'info joueur : bloc « adversaire » (rôle, infos en jeu, lien
   profil). Entièrement piloté par les variables de thème → suit la palette
   active. Seules les pastilles SB/BB gardent le bleu/rouge des jetons de
   blinds posés sur le tapis (jetons de jeu, identiques sur tous les thèmes). ── */
.pim-info {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
}
.pim-role {
  font-size: var(--fs-base);
  letter-spacing: 0.04em;
  color: var(--gold-dim);
  text-transform: uppercase;
  font-family: var(--ff-mono);
}
.pim-ig {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: 10px 12px;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.pim-ig-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-5);
  font-size: var(--fs-base);
}
.pim-ig-k { color: var(--gold-dim); }
.pim-ig-v { color: var(--cream); font-family: var(--ff-mono); font-weight: 700; }
.pim-ig-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  margin-top: 2px;
}
.pim-status {
  font-size: var(--fs-xs);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gold);
  border: 1px solid var(--gold-dim);
  border-radius: var(--r-pill);
  padding: 2px 9px;
}
.pim-pos-wrap { display: inline-flex; gap: var(--sp-1); }
.pim-pos {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 18px;
  padding: 0 5px;
  font-size: var(--fs-2xs);
  font-weight: 700;
  font-family: var(--ff-mono);
  border-radius: var(--r-pill);
  color: #fff;
}
.pim-pos-d  { background: var(--gold); color: #1a1a1a; }
.pim-pos-sb { background: #1565c0; }
.pim-pos-bb { background: #b71c1c; }
.pim-profile-link {
  font-size: var(--fs-sm);
  color: var(--gold);
  text-decoration: none;
  border-bottom: 1px dotted var(--gold-dim);
  padding-bottom: 1px;
}
.pim-profile-link:hover { color: var(--cream); border-bottom-color: var(--gold); }

/* ── Avatar picker shown as a floating modal (from the lobby) ──
   The existing #avatar-popup lives statically inside the connect
   form and is positioned via the surrounding flow. When opened from
   the lobby we add .avatar-popup-as-modal to make it float centered.
   The JS detaches and re-attaches it to body so this rule applies
   regardless of where it lives in the DOM. */
#avatar-popup.avatar-popup-as-modal {
  /* Fixed full-viewport "backdrop" surface; the popup's own
     header+grid form the visible card, centered via flex. */
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex !important;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: rgba(0,0,0,0.65);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  padding: var(--sp-6);
  border: none;
  border-radius: 0;
  /* Reset the small overflow:hidden / fixed border of the static
     popup -- we re-apply card styling below on the children. */
  overflow: visible;
}
#avatar-popup.avatar-popup-as-modal .avp-header,
#avatar-popup.avatar-popup-as-modal .avp-cats,
#avatar-popup.avatar-popup-as-modal .avp-grid {
  /* These children become the visible "card". All stretched to the
     same fixed width so they look like one panel (header on top, the
     category tabs in the middle, the emoji grid below). */
  background: var(--modal-bg);
  width: min(360px, 100%);
  max-width: 360px;
  border-left: 1px solid var(--gold-dim);
  border-right: 1px solid var(--gold-dim);
}
#avatar-popup.avatar-popup-as-modal .avp-header {
  border-top: 1px solid var(--gold-dim);
  border-top-left-radius: 8px;
  border-top-right-radius: 8px;
}
#avatar-popup.avatar-popup-as-modal .avp-grid {
  max-height: 60vh;
  overflow-y: auto;
  border-bottom: 1px solid var(--gold-dim);
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
  /* Smooth scroll on iOS */
  -webkit-overflow-scrolling: touch;
}


/* ══ POPUP AVATAR ══ */
#avatar-popup {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 0;
  overflow: hidden;
  animation: slideDown 0.2s ease;
}
.avp-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 12px;
  font-size: var(--fs-2xs); letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--gold-dim);
  border-bottom: 1px solid var(--border);
  background: var(--chrome-tint);
}
.avp-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: var(--sp-1);
  padding: var(--sp-4);
  /* Cap the height + scroll so a big filtered category can't push the
     grid past the SE CONNECTER button / footer on the connect screen.
     The lobby modal overrides this with its own 60vh cap below. */
  max-height: 42vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
/* ── Category tab row above the emoji grid ── */
.avp-cats {
  display: flex;
  gap: 3px;
  padding: 7px 8px;
  border-bottom: 1px solid var(--border);
  background: rgba(0,0,0,0.25);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none; /* Firefox: hide the scrollbar, swipe instead */
}
.avp-cats::-webkit-scrollbar { display: none; } /* WebKit: hide too */
.avp-cat {
  flex: 0 0 auto;
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  background: var(--inset);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font-size: 1.15rem;
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s, transform 0.1s;
  padding: 0;
}
.avp-cat:hover { background: rgba(var(--gold-rgb), 0.1); transform: scale(1.1); }
.avp-cat.selected {
  border-color: var(--gold);
  background: rgba(var(--gold-rgb), 0.2);
}
.avp-btn {
  aspect-ratio: 1;
  background: var(--inset);
  border: 2px solid transparent;
  border-radius: var(--r-sm);
  font-size: var(--fs-xl);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: border-color 0.12s, transform 0.1s, background 0.12s;
  padding: 0;
}
.avp-btn:hover { border-color: var(--gold-dim); transform: scale(1.15); background: rgba(var(--gold-rgb), 0.1); }
.avp-btn.selected { border-color: var(--sel,var(--gold)); background: rgba(var(--sel-rgb,var(--gold-rgb)), 0.2); }
/* Example-avatar gallery thumbnails: photo fills the grid cell instead
   of an emoji glyph (the gallery is now a category tab, not a modal). */
.avp-gal-item { padding: 0; overflow: hidden; }
.avp-gal-item img { width: 100%; height: 100%; object-fit: cover; border-radius: 4px; display: block; pointer-events: none; }
.avp-none { font-size: var(--fs-xs) !important; font-family: var(--ff-display); color: var(--text); }
/* PokerTH official avatar entry -- accent color to distinguish it
   from the emoji costumes (this is a real identity, not a mask).
   Hosts an <img> of the PokerTH chip logo; tighten padding so the
   image fills the button nicely and centers properly. */
.avp-pth {
  background: rgba(var(--green-rgb), 0.12) !important;
  border-color: rgba(var(--green-rgb), 0.4) !important;
  padding: 0 !important;
  display: flex !important;
  align-items: center;
  justify-content: center;
}
.avp-pth:hover { background: rgba(var(--green-rgb), 0.22) !important; border-color: var(--green) !important; }
.avp-pth.selected { background: rgba(var(--green-rgb), 0.3) !important; border-color: var(--green) !important; }
#av-trigger.has-avatar { border-color: var(--gold-dim); background: rgba(var(--gold-rgb), 0.1); }

/* Emoji avatar dans le siège */
.seat-avatar.emoji-av .seat-initial { font-size: 1.5em; line-height: 1; }

.tls-row { display: flex; align-items: flex-start; gap: var(--sp-3); cursor: pointer; font-size: var(--fs-sm); color: var(--text); line-height: 1.4; }

/* Ligne « Mode invité » : la case à gauche, le lien « mot de passe oublié ? »
   à droite, nettement détaché (gap) et jamais collé au libellé. Le lien passe
   à la ligne sur les cartes étroites plutôt que de comprimer la case. */
.guest-line { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-3) var(--sp-6); }
.guest-line #guest-mode-row { flex: 0 1 auto; }
#forgot-pw-link {
  color: var(--gold); font-size: var(--fs-xs); font-weight: 600;
  text-decoration: none; opacity: 0.95; white-space: nowrap;
  /* Poussé contre le bord droit de la ligne, donc aligné sur la fin des
     champs de saisie (même conteneur, mêmes marges). Le gap de .guest-line
     reste l'écart MINIMUM avec la case quand la carte est étroite. */
  margin-left: auto;
}
#forgot-pw-link:hover { opacity: 1; text-decoration: none; }

.row2 { display: flex; gap: var(--sp-5); }
.row2 .f:first-child { flex: 1; }
.row2 .f-sm { width: 110px; }
.row2 .f { min-width: 0; }        /* items flex compressibles (fix debordement Port en portrait iOS) */
.row2 .f input { width: 100%; }   /* borne la largeur de l'input a son conteneur */

/* Roue crantée « Plus d'options » à droite du sélecteur de mode : même langage
   visuel que le bouton avatar et la roue du lobby. Passe en or et l'icône pivote
   quand les options sont ouvertes. Le bloc avancé reprend le rythme vertical
   (gap 11px) de la carte et s'ouvre avec un léger fondu. */
/* Contrôle segmenté du mode de connexion (remplace le <select> visuel) */
.mode-seg {
  display: flex;
  gap: 5px;
  min-width: 0;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 5px;
}
.mode-seg-btn {
  flex: 1 1 0;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  border-radius: var(--r-sm);
  padding: 8px 3px;
  cursor: pointer;
  font-family: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-1);
  transition: background 0.16s, color 0.16s, box-shadow 0.16s;
}
.mode-seg-ic { font-size: var(--fs-xl); line-height: 1; }
.mode-seg-lb {
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
.mode-seg-btn:hover { color: var(--text-hi); }
.mode-seg-btn[aria-pressed="true"] {
  background: linear-gradient(180deg, rgba(var(--gold-rgb), 0.22), rgba(var(--gold-rgb), 0.10));
  color: var(--gold-hi);
  box-shadow: inset 0 0 0 1px rgba(var(--gold-rgb), 0.55), 0 2px 10px rgba(var(--gold-rgb), 0.18);
}
.mode-seg-btn:focus-visible { outline: 2px solid var(--gold-hi); outline-offset: 2px; }

/* ── Wizard login en 2 étapes (palette = tokens du thème) ── */
#login-step1 .login-lead { text-align: center; font-size: var(--fs-lg); font-weight: 800; color: var(--cream); margin: 0 0 3px; }
#login-step1 .login-lead-sub { text-align: center; font-size: var(--fs-base); color: var(--text); margin: 0 0 16px; }
.login-cards { display: flex; flex-direction: column; gap: var(--sp-4); }
.login-card {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left; cursor: pointer;
  font-family: inherit; color: var(--cream); background: var(--inset);
  border: 1px solid var(--border); border-radius: 13px; padding: 14px;
  transition: border-color 0.16s, box-shadow 0.16s, transform 0.12s, background 0.16s;
}
.login-card:hover { border-color: var(--border-hi); background: rgba(var(--gold-rgb), 0.08);
  box-shadow: 0 6px 18px rgba(var(--gold-rgb), 0.15); transform: translateY(-1px); }
.login-card:active { transform: translateY(0); }
.login-card:focus-visible { outline: 2px solid var(--gold-hi); outline-offset: 2px; }
/* Focus posé au démarrage d'une page (keynav focusStart) : pas de cadre avant
   la première touche, comme le QML sans raison de focus (parité b170786). */
[data-kn-quiet]:focus-visible { outline: none !important; }
/* Zones de lecture focalisées pour le défilement clavier (keynav) : pas de
   cadre, comme un ScrollView QML. */
[data-kn-read]:focus { outline: none; }
.lc-ic { font-size: 1.8rem; width: 40px; text-align: center; flex: 0 0 auto; }
.lc-tx { flex: 1; min-width: 0; }
.lc-t { display: block; font-size: var(--fs-md); font-weight: 800; color: var(--text-hi); }
.lc-d { display: block; font-size: var(--fs-xs); color: var(--text); margin-top: 3px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lc-go { font-size: 1.5rem; color: var(--gold-dim); flex: 0 0 auto; line-height: 1; }
/* Live server figures under the Internet card (modules/ui/live-stats.mjs).
   Hidden by default: it only appears once the relay answers with figures.
   Icon + number per counter, no words — the card is narrow on a phone and a
   worded line was being cut off mid-sentence. The full wording lives in each
   item's title/aria-label, so it is still read out and still translated. */
.lc-live { display: flex; align-items: center; flex-wrap: wrap; row-gap: 3px;
  font-size: var(--fs-xs); color: var(--green); margin-top: 4px; }
.lc-live .lcl { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap;
  margin-right: 12px; }
.lc-live .lcl:last-child { margin-right: 0; }
.lc-live .lcl svg { width: 13px; height: 13px; flex: 0 0 auto; }
.lc-live .lcl-g { font-size: 13px; line-height: 1; flex: 0 0 auto; }
.lc-live .lcl b { font-weight: 700; }


.login-back { display: flex; align-items: center; gap: 9px; width: 100%; margin: 0; padding: var(--sp-0);
  background: none; border: 0; font-family: inherit; cursor: pointer; }
.login-back:focus-visible { outline: 2px solid var(--gold-hi); outline-offset: 2px; border-radius: var(--r-sm); }
.lb-chev { font-size: var(--fs-xl); color: var(--gold); line-height: 1; }
.lb-name { display: flex; align-items: center; gap: 7px; font-weight: 800; font-size: var(--fs-md); color: var(--text-hi); }
.lb-ico { font-size: var(--fs-lg); }
.lb-edit { margin-left: auto; font-size: var(--fs-xs); font-weight: 700; color: var(--gold); }

.login-avatar-top { display: flex; flex-direction: column; align-items: center; gap: 7px; margin: 2px 0 6px; }
.login-avatar-top #av-trigger {
  width: 88px; height: 88px; border-radius: 16px; font-size: var(--fs-hero);
  background: var(--inset); border: 1px solid var(--border); cursor: pointer; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  transition: border-color 0.16s, box-shadow 0.16s, transform 0.12s;
}
.login-avatar-top #av-trigger:hover { border-color: var(--border-hi);
  box-shadow: 0 6px 18px rgba(var(--gold-rgb), 0.2); transform: translateY(-1px); }
.login-avatar-top #av-trigger img { width: 100% !important; height: 100% !important; }
.login-avatar-cap { font-size: var(--fs-xs); font-weight: 700; color: var(--gold); }
/* Le wrapping en étapes retirait aux champs le gap du .card parent : on
   redonne à l'étape 2 le même rythme vertical (aéré comme avant). Le <form>
   passe en display:contents pour que ses champs (pseudo, mots de passe,
   options avancées) participent aussi à ce rythme. */
#login-step2 { display: flex; flex-direction: column; gap: 11px; }
#login-form { display: contents; }

.conn-adv-gear {
  flex-shrink: 0;
  width: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--inset);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--gold-dim);
  cursor: pointer;
  font-size: 1.15rem;
  line-height: 1;
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}
.conn-adv-gear:hover {
  color: var(--gold);
  border-color: var(--gold-dim);
  background: rgba(255,255,255,0.1);
}
.conn-adv-gear.open {
  color: var(--gold);
  border-color: var(--gold);
  background: rgba(var(--gold-rgb), 0.12);
}
.conn-adv-gear-ico {
  display: inline-block;
  transition: transform 0.3s ease;
}
.conn-adv-gear.open .conn-adv-gear-ico { transform: rotate(90deg); }

#conn-advanced { display: flex; flex-direction: column; gap: 11px; }
#conn-advanced.anim { animation: connAdvIn 0.28s ease; }
@keyframes connAdvIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.f {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.f label {
  font-size: var(--fs-xs);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-dim);
}

.f input {
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: 9px 11px;
  color: var(--cream);
  font-family: var(--ff-mono);
  font-size: var(--fs-md);
  outline: none;
  transition: border-color 0.2s, background 0.2s;
}

.f input:focus {
  border-color: var(--gold);
  background: rgba(var(--gold-rgb), 0.05);
}

/* Champ pseudo VERROUILLÉ (mode invité pokerth.net) : fond sombre façon
   client QML (col700 #1d222b, texte atténué col300 #a0acc4) pour signaler
   que le GuestXXXXX n'est pas modifiable. Prime sur :focus. */
.f input[readonly],
.f input[readonly]:focus {
  background: #1d222b;
  color: #a0acc4;
  border-color: rgba(255,255,255,0.10);
  cursor: default;
}

.btn-primary {
  background: #2b3444;
  color: #ffffff;
  border: none;
  padding: var(--sp-5);
  font-family: var(--ff-display);
  font-size: var(--fs-base);
  font-weight: 700;
  letter-spacing: 0.15em;
  cursor: pointer;
  border-radius: 2px;
  transition: background 0.15s, transform 0.1s, box-shadow 0.15s;
  box-shadow: 0 4px 16px rgba(0,0,0,0.28);
}
.btn-primary:hover { background: #384252; box-shadow: 0 6px 24px rgba(0,0,0,0.4); }
.btn-primary:active { transform: scale(0.98); }

.status {
  min-height: 18px;
  font-size: var(--fs-sm);
  text-align: center;
  color: var(--text);
  transition: color 0.2s;
}
.status.ok  { color: var(--green); }
.status.err { color: var(--red); }

/* ══════════════════════════════════════════
   LOBBY SCREEN
══════════════════════════════════════════ */
#s-lobby { flex-direction: column; overflow-y: auto; }
#create-form.open { display: block !important; }

/* Header */
/* ── Hauteur de header UNIFORME (tous ecrans, tous appareils, tous OS) ──
   Base QML officiel 2.1.3 : topBar 38 px, 30 px en landscapeCompact
   (paysage mobile h<600, ou desktop ratio>2.1 && h<1300 — delta §2).
   Une seule variable consommee par TOUTES les regles de header (base,
   desktop, mobile portrait, paysage, connexion) -> plus aucune divergence. */
:root { --header-h: 38px; }
@media (orientation: landscape) and (max-height: 600px),
       (min-aspect-ratio: 210/100) and (max-height: 799px) {
  :root { --header-h: 30px; }
}
.header {
  display: flex;
  align-items: center;
  padding: 4px clamp(8px,2vw,20px);
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  /* Wider gap so the action buttons are more spaced out horizontally
     (requested by narmod). Was clamp(4px,1.2vw,14px). */
  gap: clamp(6px,2.2vw,18px);
  flex-shrink: 0;
  box-sizing: border-box;
  /* Aligne sur le topBar QML officiel : 38 px (30 en landscapeCompact). */
  min-height: var(--header-h, 38px);
  overflow: hidden;
}

.header-brand {
  font-family: var(--ff-display);
  font-size: clamp(0.7rem, 2vw, 1.05rem);
  color: var(--text-hi);
  letter-spacing: 0.08em;
  display: flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  flex-shrink: 0;
}
.header-brand span { opacity: 0.6; font-size: 1.1em; }
.header-chip {
  height: 1.6em;
  width: auto;
  vertical-align: middle;
  display: inline-block;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,0.6));
}
/* The lobby ♠ is a tap target to reveal/hide the POKERTH word on phones. */
#lobby-brand .brand-suit {
  cursor: pointer;
  opacity: 0.9;
  transition: opacity 0.15s, transform 0.15s;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
#lobby-brand .brand-suit:hover { opacity: 0.95; }
#lobby-brand .brand-suit:active { transform: scale(0.85); }
#lobby-brand.brand-open .brand-suit { opacity: 1; }

.header-pills {
  flex: 1;
  display: flex;
  gap: clamp(3px, 1vw, 10px);
  min-width: 0;
  overflow: hidden;
}

.pill {
  background: rgba(var(--gold-rgb), 0.1);
  border: 1px solid var(--border);
  border-radius: 2px;
  padding: clamp(1px,0.4vh,3px) clamp(4px,1vw,10px);
  font-size: clamp(0.58rem, 1.4vw, 0.75rem);
  color: var(--gold);
  white-space: nowrap;
  flex-shrink: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  display: inline-flex;
  align-items: center;
}

.lang-btn.active { background: rgba(var(--sel-rgb,var(--gold-rgb)), 0.2) !important; border-color: var(--gold-dim) !important; color: var(--sel,var(--gold)) !important; }
.btn-watch {
  background: rgba(var(--blue-rgb), 0.12);
  border: 1px solid rgba(var(--blue-rgb), 0.35);
  color: var(--info);
  padding: 4px 10px;
  font-family: var(--ff-display);
  font-size: var(--fs-xs);
  font-weight: 700;
  border-radius: 2px;
  cursor: pointer;
  letter-spacing: 0.08em;
  transition: background 0.15s;
  flex-shrink: 0;
}
.btn-watch:hover { background: rgba(var(--blue-rgb), 0.25); }
.btn-sm {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
  padding: clamp(3px,0.6vh,5px) clamp(6px,1.2vw,14px);
  font-family: var(--ff-mono);
  font-size: clamp(0.62rem, 1.4vw, 0.78rem);
  cursor: pointer;
  border-radius: 2px;
  transition: border-color 0.2s, color 0.2s;
  white-space: nowrap;
  flex-shrink: 0;
}
.btn-sm:hover { border-color: var(--border-hi); color: var(--text-hi); }

/* Uniformisation hauteur des boutons dans tous les headers */
.header .btn-sm,
.cp-header .btn-sm {
  height: 30px;
  min-width: 30px;
  padding: 0 13px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  font-size: var(--fs-sm);
  line-height: 1;
  color: var(--text-hi);
}
/* Bouton carré (icône seule) — un peu plus large pour respirer */
.header .btn-icon,
.cp-header .btn-icon {
  width: 34px;
  padding: 0;
  background: var(--inset);
}

/* ── Unread-chat badge ── */
/* Red counter pinned to the top-right of a 💬 button (game/lobby header
   and the floating FAB) so a closed chat panel never hides new messages. */
#chat-toggle-btn, #lobby-chat-btn, #gchat-fab { position: relative; }
.chat-badge {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--red);
  color: #fff;
  font-family: var(--ff-mono);
  font-size: var(--fs-2xs);
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  box-shadow: 0 1px 4px rgba(0,0,0,0.5);
  pointer-events: none;
  z-index: 6;
  animation: chatBadgePop 0.25s ease;
}
@keyframes chatBadgePop {
  0%   { transform: scale(0); }
  70%  { transform: scale(1.25); }
  100% { transform: scale(1); }
}
#gchat-fab .chat-badge { top: -3px; right: -3px; }

/* Body */
.lobby-body {
  flex: 1;
  min-height: 0;  /* allow shrinking */
  display: flex;
  overflow: hidden;
}

/* ── Games panel ── */
.pane-games {
  flex: 1;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
  overflow: hidden;
}

.create-bar {
  display: flex; gap: var(--sp-3); padding: 10px 12px;
  background: rgba(var(--green-rgb), 0.06);
  border-bottom: 1px solid var(--border);
}
.btn-create {
  flex: 1; background: rgba(var(--green-rgb), 0.15);
  border: 1px solid var(--green); color: var(--green);
  padding: 9px 14px; font-family: var(--ff-display);
  font-size: var(--fs-sm); font-weight: 700; cursor: pointer; border-radius: 4px;
}
.btn-create:hover { background: rgba(var(--green-rgb), 0.28); }
.btn-create:disabled { opacity: 0.5; cursor: default; }
.btn-create-manual {
  background: var(--inset); border: 1px solid var(--border);
  color: var(--text); padding: 9px 12px; font-size: var(--fs-sm);
  cursor: pointer; border-radius: 4px; white-space: nowrap;
}
.btn-create-manual:hover { background: rgba(255,255,255,0.1); }
.create-form { padding: 12px 14px; background: var(--modal-bg); border-bottom: 1px solid var(--border); }
.create-fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 8px 12px; margin-bottom: 0; }
/* Le nom de la table occupe toute la largeur du cadre : on sort ce champ de la
   grille auto-fill (sinon il reste cantonné à une colonne de ~130px) et on force
   l'input à 100% de la largeur du cadre. */
.cf-name-wrap { display: block; }
.cf-name-wrap .cf { width: 100%; }
.cf-name-wrap .cf input { width: 100%; box-sizing: border-box; }
.cf { display: flex; flex-direction: column; gap: 3px; }
.cf-sm { min-width: 80px; }
/* Libellés du formulaire de création : retour à la ligne au lieu d'une
   troncature « … » quand la traduction dépasse la colonne (parité QML
   Local/NetworkGameSettings, stable 27/08/2026 — wrapMode WordWrap ajouté
   sur chaque Label). Les rangées sont en flex : elles grandissent avec. */
.cf label { font-size: var(--fs-3xs); color: var(--gold-dim); letter-spacing: 0.1em; text-transform: uppercase; white-space: normal; overflow-wrap: break-word; }
.cf input, .cf select { background: var(--field-bg); border: 1px solid var(--border); border-radius: 2px; color: var(--cream); padding: 5px 7px; font-family: var(--ff-mono); font-size: var(--fs-base); outline: none; width: 100%; }
.cf input:focus, .cf select:focus { border-color: var(--gold); }

.pane-header {
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold-dim);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}

.games-scroll {
  flex: 1;
  overflow-y: auto;
}

.game-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 16px;
  border-bottom: 1px solid rgba(var(--gold-rgb), 0.07);
  cursor: pointer;
  transition: background 0.12s;
}
.game-row:hover { background: var(--inset-hi); }

.dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}
.dot-open   { background: var(--green); box-shadow: 0 0 6px rgba(var(--green-rgb), 0.5); }
.dot-run    { background: var(--orange); }
.dot-closed { background: #555; }

.game-info { flex: 1; min-width: 0; }
.game-name {
  color: var(--text-hi);
  font-size: var(--fs-base);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.game-meta {
  font-size: var(--fs-xs);
  color: var(--text);
  margin-top: 2px;
  display: flex;
  gap: var(--sp-4);
}
.game-type  { color: var(--gold-dim); }
.game-count { color: var(--gold); font-weight: 500; }

.empty {
  padding: 48px;
  text-align: center;
  color: rgba(154,170,146,0.35);
  font-size: var(--fs-base);
  line-height: 2;
}

/* ── Chat panel ── */
.chat-msgs {
  flex: 1;
  overflow-y: auto;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.msg {
  font-size: var(--fs-sm);
  line-height: 1.45;
  word-break: break-word;
}
.msg .who   { color: var(--gold); font-weight: 700; }
.msg .txt   { color: var(--text-hi); }
.msg.sys    { color: var(--text); font-size: var(--fs-sm); font-style: italic; }
/* Notes locales multilignes (suggest communautaire, parite amont 4afc377) :
   le \n du texte devient un saut de ligne, sans toucher au markup. */
.msg.sys .txt { white-space: pre-line; }
.msg.reaction { font-size: var(--fs-lg); opacity: 0.85; font-style: italic; }
.msg.bc     { color: var(--gold-dim); border-left: 2px solid var(--gold-dim); padding-left: 6px; }
.msg.mine .who { color: var(--green); }
/* Mention : mon pseudo cité dans un message d'autrui → corps en or gras
   (parité QML LobbyHandler, colorAccent = --gold). */
.msg.mention .txt { color: var(--gold); font-weight: 700; }
/* Action « /me … » : corps en italique, format « *Nom fait qqch* » (parité QML isAction). */
.msg.action .txt { font-style: italic; }
/* Le (chat bot) m'adresse directement → corps en rouge gras (parité QML colorDanger). */
.msg.botwarn .txt { color: var(--red); font-weight: 700; }
#g-chat-msgs .msg.botwarn .txt { color: var(--red); font-weight: 700; }
/* Emojis unicode du chat agrandis ~22px (parité QML enlargeEmojis). */
.chat-emoji-lg { font-size: 22px; line-height: 1; vertical-align: -0.15em; }

.chat-input {
  padding: 8px 10px;
  border-top: 1px solid var(--border);
  display: flex;
  gap: var(--sp-3);
  flex-shrink: 0;
}
.chat-input input {
  flex: 1;
  background: var(--field-bg);
  border: 1px solid var(--border);
  color: var(--cream);
  padding: 7px 10px;
  font-family: var(--ff-mono);
  font-size: var(--fs-base);
  outline: none;
  border-radius: 2px;
}
.chat-input input:focus { border-color: var(--gold); }
/* ── Popup de suggestion d'emotes (":smi…" → 😄) — parité ChatBox QML :
   radius 8, lignes 26 px, max 6 visibles + scroll, emoji 15 px + :code:
   secondaire, survol/sélection surlignés. Ancré au-dessus de la saisie. ── */
.chat-input { position: relative; }
.emote-suggest {
  position: absolute;
  left: 10px; right: 10px;
  bottom: calc(100% + 8px);
  z-index: 60;
  max-height: 164px;               /* 6 × 26 px + 2 × 4 px */
  overflow-y: auto;
  padding: var(--sp-1);
  border-radius: 8px;
  background: var(--panel-hi);
  border: 1px solid var(--border);
  box-shadow: 0 6px 18px rgba(0,0,0,.45);
  display: none;
}
.emote-suggest.open { display: block; }
.emote-suggest .es-row {
  display: flex; align-items: center; gap: var(--sp-3);
  height: 26px; padding: 0 6px;
  border-radius: 5px; cursor: pointer;
}
.emote-suggest .es-row.sel { background: rgba(255,255,255,.09); }
.emote-suggest .es-emoji { font-size: 15px; flex: none; line-height: 1; }
.emote-suggest .es-code {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--ff-mono); font-size: 12px; color: var(--text);
}
.emote-suggest .es-row.sel .es-code { color: var(--text-hi); }
.chat-send {
  background: var(--gold);
  color: var(--on-gold);
  border: none;
  padding: 0 14px;
  font-family: var(--ff-display);
  font-size: var(--fs-sm);
  cursor: pointer;
  border-radius: 2px;
  transition: background 0.15s;
}
.chat-send:hover { background: var(--gold-hi); }
/* Palette claire : corps des listes du lobby (joueurs, chat, tri) posés sur le
   gris du panneau comme le QML — leurs styles inline mettent --field-bg blanc. */
:root[data-theme="pokerth-light"] #players-list-body,
:root[data-theme="pokerth-light"] #lobby-chat-panel .chat-msgs { background:transparent !important; }
:root[data-theme="pokerth-light"] .pl-sortbar .sel-wrap select { background:#eaeef6; border-color:transparent; border-radius:var(--r-sm); color:#1d222b; }
/* Champ Message du chat lobby : colorField QML 2.1.6 (#eaeef6), champ
   « enfoncé » sur la box blanche (avant : pixel 2.1.4 #d4d9e1). */
:root[data-theme="pokerth-light"] #lobby-chat-panel .chat-input input { background:var(--inset); border-color:transparent; border-radius:var(--r-sm); color:var(--text); }
:root[data-theme="pokerth-light"] #lobby-chat-panel .chat-input input::placeholder { color:#687285; }
/* Chat du LOBBY : icônes façon QML — envoi = avion en papier vert (chat send
   #4ade80 sombre / #16a34a clair), emoji = smiley nu ; aucun chrome de bouton. */
#lobby-chat-panel .chat-send {
  background:none; border:none; padding:0 8px; color:var(--green);
  display:flex; align-items:center; border-radius:var(--r-sm); flex:0 0 auto;
}
/* L'input du chat doit pouvoir rétrécir, sinon il pousse le bouton d'envoi
   hors du cadre quand la colonne est étroite. */
.chat-input { min-width:0; }
.chat-input input { min-width:0; }
.chat-input .chat-emoji-toggle, .chat-input .chat-send { flex:0 0 auto; }
#lobby-chat-panel .chat-send:hover { background:var(--inset-hi); }
:root[data-theme="pokerth"] #lobby-chat-panel .chat-send { color:#4ade80; }
:root[data-theme="pokerth-light"] #lobby-chat-panel .chat-send { color:#16a34a; }
#lobby-chat-panel .chat-emoji-toggle {
  background:none; border:none; padding:0 6px; font-size:1.15rem; border-radius:var(--r-sm);
}
#lobby-chat-panel .chat-emoji-toggle:hover,
#lobby-chat-panel .chat-emoji-toggle.active { background:var(--inset-hi); border:none; }

/* Lobby chat panel — fond unifié + bordure basse nette + ombre, pour que le
   bas de la zone message soit clairement délimité du reste du lobby. */
#lobby-chat-panel { background: var(--modal-bg); border: 1px solid var(--gold-dim); border-top: none; border-bottom: 2px solid var(--border-hi);
  box-shadow: 0 8px 16px rgba(0,0,0,0.5);
  position: fixed;       /* overlay : ne pousse plus les tables, les recouvre */
  top: var(--ovl-top, 48px);             /* juste sous le header (comme #players-panel) */
  left: 0; right: 0;
  z-index: 100;
  display: flex;
  flex-direction: column;
}

/* Scrollbars */
::-webkit-scrollbar { width: 3px; height: 3px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }

/* Mobile responsive */
/* ══════════════════════════════════
/* ══ GAME STATUS BAR (parité GameStatusBar QML §7 + delta 2.1.3) ══
   Bandeau noir rgba(0,0,0,0.78), hauteur 40 px (28 en landscapeCompact),
   marges 16 px. Gauche : Total $X (vert #99D500, gras) / Mises $Y (#7aa800),
   libellés gris #9e9e9e. Droite : phase (blanc DemiBold, letterSpacing 0.5)
   puis Partie/Main (gris). Tailles 13/11 px, compact 11/10/9 px.
   Centre (spécifique web, conservé) : badge blinds + statut spectateur. */
#pot-strip {
  position: relative;             /* ancre du badge blinds (.gsb-mid absolu) */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: 0 16px;                /* marges QML 16 px */
  background: rgba(0,0,0,0.78);
  border-bottom: 1px solid rgba(255,255,255,0.08);
  flex-shrink: 0;
  height: 40px;                   /* QML 2.1.3 : landscapeCompact ? 28 : 40 */
  box-sizing: border-box;
  font-family: var(--ff-display);
  overflow: hidden;
}
.gsb-col {
  display: flex;
  flex-direction: column;
  justify-content: center;
  line-height: 1.3;
  min-width: 0;
  white-space: nowrap;
}
.gsb-right { align-items: flex-end; text-align: right; }
/* Centre (badge blinds + spectateur) : position ABSOLUE à 33 % — décalé à
   gauche du centre et surtout FIXE, indépendant de la largeur des montants
   Total/Mises (demande narmod). Hors flux → gauche/droite restent 1:1 QML. */
/* Pastille blinds CENTRÉE au vrai milieu de la barre (demande narmod
   2026-07-15) : même règle mobile ET desktop → affichage identique partout.
   Remplace l'ancien décalage 33% (mobile) / 230px (desktop). */
.gsb-mid {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.gsb-row { display: inline-flex; align-items: baseline; gap: 5px; }
.gsb-lbl { color: #9e9e9e; font-size: 13px; }
.gsb-row .gsb-lbl + .gsb-lbl { margin-left: 8px; }   /* espace Partie · Main */
.gsb-total { color: #99D500; font-weight: 700; font-size: 13px; }
.gsb-left .gsb-row:last-child .gsb-lbl,
.gsb-gh .gsb-lbl, .gsb-val { font-size: 11px; }
.gsb-bets { color: #7aa800; font-weight: 600; font-size: 11px; }
.gsb-val  { color: #9e9e9e; }
.gsb-phase {
  color: #ffffff;
  font-weight: 600;               /* DemiBold */
  font-size: 13px;
  letter-spacing: 0.5px;
}
/* landscapeCompact (parité Responsive QML 2.1.3) : barre 28 px, tailles 11/10/9 */
@media (orientation: landscape) and (max-height: 600px),
       (min-aspect-ratio: 210/100) and (max-height: 799px) {
  #pot-strip { height: 28px; padding: 0 16px; }
  .gsb-col { line-height: 1.15; }
  .gsb-lbl { font-size: 10px; }
  .gsb-total, .gsb-phase { font-size: 11px; }
  .gsb-left .gsb-row:last-child .gsb-lbl,
  .gsb-gh .gsb-lbl, .gsb-val, .gsb-bets { font-size: 9px; }
  /* Barre 28 px : pas de marge pour grossir — badge blinds figé compact. */
  #pot-strip .blinds-next { height: 18px; padding: 0 9px; font-size: 10px; }
}
/* Compteur "blinds" cliquable à côté du n° de main dans le bandeau du pot.
   Taille asservie au modèle générique --comm-scale (0.55–1.8, posé par
   renderSeats) comme la pastille de connexion / win-hand-badge : le badge
   grossit avec la résolution, borné par clamp() pour rester dans la barre
   de 40 px (base ≈ tailles historiques à échelle 1 ; repli 1 hors table). */
.blinds-next {
  display: inline-flex;
  align-items: center;
  height: clamp(18px, calc(19px * var(--comm-scale, 1)), 28px);
  box-sizing: border-box;
  padding: 0 clamp(8px, calc(9px * var(--comm-scale, 1)), 14px);
  border-radius: var(--r-pill);
  /* Accent du THEME (equivalent web de Theme.colorAccent cote QML), pas un or
     fige : --sel vaut #E3C800 en sombre et #9a7d0a en clair, et suit le theme
     de table choisi. Meme source que #g-spec-badge, les deux pastilles du
     bandeau restent donc assorties. */
  background: rgba(var(--sel-bar-rgb), 0.22);
  border: 1px solid var(--sel-bar);
  color: var(--sel-bar);
  font-family: var(--ff-mono);
  font-weight: 700;
  font-size: clamp(10px, calc(11px * var(--comm-scale, 1)), 16px);
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
/* Le slot n'est qu'un point d'insertion JS : display:contents → le badge
   devient enfant flex DIRECT de .gsb-mid et se centre verticalement
   exactement comme #g-spec-badge (même offset depuis le haut de la barre). */
#g-blinds-slot { display: contents; }
.blinds-next:hover, .blinds-next:focus { background: rgba(var(--sel-bar-rgb), 0.32); border-color: var(--sel-bar); outline: none; }
/* Bandeau d'info/alerte blinds. Centré en haut, masqué par défaut, révélé
   via .show (transition). Affiche : ↑ Blinds  cur → next  · quand. */
.blinds-up-toast {
  position: fixed;
  top: 64px;
  left: 50%;
  transform: translate(-50%, -14px) scale(0.9);
  z-index: 900;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 8px;
  max-width: min(90vw, 360px);
  padding: 9px 18px;
  border-radius: 22px;
  background: linear-gradient(135deg, rgba(var(--gold-rgb), 0.96), rgba(var(--gold-rgb), 0.72));
  color: var(--on-gold);
  font-family: var(--ff-display);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-align: center;
  box-shadow: 0 8px 28px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.25) inset;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.28s ease, transform 0.28s cubic-bezier(0.2,0.9,0.3,1.3);
}
.blinds-up-toast.show { opacity: 1; transform: translate(-50%, 0) scale(1); }
.blinds-up-toast .bu-icon { font-size: var(--fs-lg); line-height: 1; }
.blinds-up-toast .bu-text { font-size: var(--fs-sm); text-transform: uppercase; }
.blinds-up-toast .bu-val {
  font-family: var(--ff-mono);
  font-size: var(--fs-md);
  font-weight: 700;
  background: rgba(0,0,0,0.16);
  padding: 1px 8px;
  border-radius: var(--r-sm);
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.blinds-up-toast .bu-arrow { opacity: 0.7; }
.blinds-up-toast .bu-when {
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  font-weight: 500;
  opacity: 0.85;
}

/* ══ RESPONSIVE OVAL SIZE via CSS custom props ══
   Échelle continue propre à l'écran de jeu (1100/900/700/740-portrait/500-paysage).
   Exception au canon 600 (décision B) : l'écran de jeu garde 740/768 en portrait —
   le layout téléphone s'étend volontairement aux petites tablettes portrait, comme
   le QML qui choisit ses slots au RATIO de la tableZone et non à la largeur.
   ══ */
:root { --oval-w: 440px; --oval-h: 180px; }
@media (min-width: 1100px)  { :root { --oval-w: 520px; --oval-h: 210px; } }
@media (max-width: 900px)   { :root { --oval-w: 320px; --oval-h: 132px; } }
@media (max-width: 700px)   { :root { --oval-w: 240px; --oval-h: 100px; } }
@media (max-width: 740px) and (orientation: portrait)  { :root { --oval-w: min(58vw,240px); --oval-h: min(24vw,100px); } }
/* Palier « extra-compact » (h<500 = téléphones en paysage), raffinement INTERNE au
   landscapeCompact canonique h<600 — intentionnel, ne pas fusionner avec 600. */
@media (max-height: 500px) and (orientation: landscape){ :root { --oval-w: min(40vw,240px); --oval-h: min(18vh,88px); } }

   RESPONSIVE — orientation-aware
══════════════════════════════════ */



/* ── Confetti rain ── */
@keyframes confettiFall {
  0%   { transform: translateY(-20px) rotate(0deg);   opacity: 1; }
  85%  { opacity: 1; }
  100% { transform: translateY(105vh) rotate(720deg); opacity: 0; }
}
.confetti-piece {
  position: fixed;
  width: 10px; height: 10px;
  top: -20px;
  pointer-events: none;
  z-index: 700;
  border-radius: 2px;
  animation: confettiFall linear forwards;
}

/* ── Urgence (≤5s) — pulsation rouge ── */
/* Halo d'urgence (ton tour, <=8s) : ombre FIXE portee par un pseudo-element
   promu en couche GPU ; seule l'opacity est animee (composite-only) -> fin du
   repaint plein ecran a chaque frame (batterie/chaleur). Rendu visuel identique.
   position:fixed -> se cale sur le viewport (= l'ecran de jeu plein ecran),
   sans toucher au positionnement de #s-game ni de ses enfants. */
@keyframes urgencyPulse {
  0%,100% { opacity: 0; }
  50%     { opacity: 1; }
}
#s-game.urgent::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 90;
  box-shadow: inset 0 0 60px 10px rgba(var(--red-rgb), 0.22);
  will-change: opacity;
  animation: urgencyPulse 0.6s ease-in-out infinite;
}

/* ── Transition lobby → table ── */
@keyframes tableEnter {
  0%   { opacity: 0; transform: scale(0.94); }
  100% { opacity: 1; transform: scale(1); }
}
#s-game.entering { animation: tableEnter 0.45s cubic-bezier(.2,.8,.3,1) both; }

/* ── Hover fan mes cartes ── */
.pb-cards:hover .pk:first-child {
  transform: rotate(-8deg) translateY(-6px) translateX(-4px);
  transition: transform 0.2s ease;
  z-index: 2;
}
.pb-cards:hover .pk:last-child {
  transform: rotate(8deg) translateY(-6px) translateX(4px);
  transition: transform 0.2s ease;
  z-index: 2;
}
.pb-cards .pk { transition: transform 0.2s ease; z-index: 1; }

/* ── Élimination joueur ── */
@keyframes eliminatePlayer {
  0%   { opacity: 1;  transform: translate(-50%,-50%) scale(1)  rotate(0deg); }
  40%  { opacity: 0.7; transform: translate(-50%,-50%) scale(1.1) rotate(-5deg); }
  100% { opacity: 0;  transform: translate(-50%,-50%) scale(0.3) rotate(15deg); }
}
.seat.eliminated { animation: eliminatePlayer 0.8s ease-in forwards; pointer-events: none; }

/* ── Pot énorme — glow rouge/orange ── */
@keyframes bigPotGlow {
  0%,100% { color: var(--gold); text-shadow: 0 0 8px rgba(var(--gold-rgb), 0.4); }
  50%     { color: #ff8c42;     text-shadow: 0 0 20px rgba(255,100,30,0.7); }
}
.pot-huge { animation: bigPotGlow 1.2s ease-in-out infinite !important; }

/* ── River — révélation dramatique ── */
.pk-river {
  /* Parité QML : river immédiate, même fondu 260 ms que le turn. */
  animation: cardFlip 0.26s cubic-bezier(.3,0,.2,1) both !important;
  animation-delay: 0s !important;
}

/* ── Showdown — flip cartes adversaires ── */
.pk-showdown {
  animation: cardFlip 0.32s cubic-bezier(.3,0,.2,1) both;
}

/* ── Reset action fade entre mains ── */
@keyframes actionFadeOut {
  0%   { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(4px); }
}
.seat-action-label.fading {
  animation: actionFadeOut 0.4s ease-out forwards;
}

/* ── Dealer badge volant ── */
.dealer-badge-fly {
  position: fixed;
  background: var(--gold); color: var(--on-gold);
  border-radius: 50%; width: 18px; height: 18px;
  font-size: var(--fs-3xs); font-weight: 900;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none; z-index: 600;
  transition: left 0.6s cubic-bezier(.4,0,.2,1),
              top  0.6s cubic-bezier(.4,0,.2,1),
              opacity 0.2s ease 0.55s;
}

/* ── All-in chips multi ── */
@keyframes allInChip {
  0%   { transform: translate(0,0) scale(1) rotate(0deg);       opacity: 1; }
  60%  { transform: translate(var(--ax),var(--ay)) scale(1.3) rotate(var(--ar)); opacity: 1; }
  100% { transform: translate(var(--bx),var(--by)) scale(0.6) rotate(var(--ar)); opacity: 0; }
}
.allin-chip {
  position: fixed;
  width: 14px; height: 14px;
  border-radius: 50%;
  pointer-events: none; z-index: 550;
  animation: allInChip 0.7s cubic-bezier(.3,0,.7,1) forwards;
}

/* ═══════════════════════════════════════════════════════════
   DESKTOP LARGE (≥ 900px) — Agrandissement table + cartes
   ═══════════════════════════════════════════════════════════ */
@media (min-width: 900px) {

  /* ── Ovale de la table ── */
  .felt-oval {
    width:  min(580px, 64vw) !important;
    height: min(256px, min(32vw, calc(100dvh - 300px))) !important;
  }

  /* ── Cartes communes : tailles gerees par --comm-scale (voir .comm-row,
     slot QML 46x64 x echelle par appareil) — plus d'override fixe ici. ── */

  /* ── Mes cartes dans la player-bar ── */
  .pb-cards .pk {
    width: 62px !important;
    height: 90px !important;
    font-size: var(--fs-xl) !important;
  }
  .pb-cards { gap: 8px !important; }

  /* ── Joueurs (rendu standard : tablette paysage + base desktop) ── */
  .seat-avatar {
    width: 38px !important;
    height: 38px !important;
    font-size: var(--fs-md) !important;
  }
  /* Emoji un peu plus petit sur desktop/tablette (le carré y est plus grand) ;
     mobile garde 1.5em via la règle de base hors media. */
  .seat-avatar.is-bot .seat-initial { font-size: 1.2em !important; }
  .seat-avatar.emoji-av .seat-initial { font-size: 1.2em !important; }
  .seat-name   { font-size: var(--fs-xs) !important; max-width: 84px !important; }
  .seat-money  { font-size: var(--fs-xs) !important; }
  .seat-bet    { font-size: var(--fs-3xs) !important; }
  .seat-action-label { font-size: var(--fs-3xs) !important; }
  .seat { min-width: 54px !important; max-width: 86px !important; }

  .seat.dealer .seat-avatar::after {
    width: 16px !important; height: 16px !important;
    font-size: var(--fs-3xs) !important;
    top: -5px !important; right: -5px !important;
  }
  .blind-badge {
    width: 16px !important; height: 16px !important;
    font-size: var(--fs-3xs) !important;
    top: -5px !important; left: -5px !important;
  }
  .seat .pk.xsm {
    width: 24px !important;
    height: 34px !important;
    font-size: var(--fs-2xs) !important;
  }

  /* ── Scaler : marge pour les sièges qui débordent ── */
  .table-scaler { padding: 80px 110px !important; }

  /* ── Player bar ── */
  .pb-avatar {
    width: 46px !important; height: 46px !important;
    font-size: 1.15rem !important;
  }
  .pb-name  { font-size: var(--fs-md) !important; }
  .pb-money { font-size: var(--fs-sm) !important; }
  .pb-info  { min-width: 110px !important; }

  /* ── Timer SVG autour de l'avatar ── */
  .seat-timer {
    width: 170% !important; height: 170% !important;
    top: -35% !important; left: -35% !important;
  }
  .seat-timer-badge {
    font-size: var(--fs-base) !important;
    margin-top: 16px !important;
    padding: 2px 7px !important;
    min-width: 32px !important;
  }
}

/* ══════════════════════════════════════════════════════════════
   MODE DESKTOP (souris) — agrandissements appliqués UNIQUEMENT aux
   écrans pointés à la souris. Une tablette tactile (même en paysage
   ≥ 900px) garde le rendu standard ci-dessus.
   ══════════════════════════════════════════════════════════════ */
@media (min-width: 900px) and (hover: hover) and (pointer: fine) {

  /* ── Joueurs ── */
  .seat-avatar {
    width: 52px !important;
    height: 52px !important;
    font-size: var(--fs-xl) !important;
  }
  .seat-name   { font-size: var(--fs-md) !important; max-width: 112px !important; }
  .seat-money  { font-size: var(--fs-md) !important; }
  .seat-bet    { font-size: var(--fs-sm) !important; }
  .seat-action-label { font-size: var(--fs-sm) !important; }
  .seat { min-width: 72px !important; max-width: 116px !important; }
  .seat.dealer .seat-avatar::after {
    width: 22px !important; height: 22px !important;
    font-size: var(--fs-sm) !important;
    top: -7px !important; right: -7px !important;
  }
  .blind-badge {
    width: 22px !important; height: 22px !important;
    font-size: var(--fs-xs) !important;
    top: -7px !important; left: -7px !important;
  }
  .seat .pk.xsm {
    width: 32px !important;
    height: 45px !important; /* ratio carte 120:168 (0.714) */
    font-size: var(--fs-sm) !important;
  }

  /* ── Player bar ── */
  .pb-avatar {
    width: 52px !important; height: 52px !important;
    font-size: var(--fs-xl) !important;
  }
  .pb-name  { font-size: var(--fs-lg) !important; }
  .pb-money { font-size: var(--fs-md) !important; }
  .pb-info  { min-width: 120px !important; }
  .pb-action { font-size: var(--fs-md) !important; min-width: 60px !important; }
  #pb-timer  { font-size: var(--fs-md) !important; }
  /* Nos cartes (player-bar du bas) agrandies — desktop souris seulement ;
     la tablette paysage garde 62x90 du bloc min-width:900px ci-dessus. */
  .pb-cards .pk { width: 76px !important; height: 108px !important; font-size: var(--fs-2xl) !important; }
  .pb-cards     { gap: 10px !important; }

  /* ── Contrôles d'action ── */
  /* Boutons d'action, police 15 px. Padding vertical 10 px pour egaler la
     hauteur des themes a boutons-image (skin : border 9 + padding 1 = chrome 20 ;
     ici border 0 + padding 10+10 = chrome 20). Horizontal 18 = meme largeur. */
  .btn-action { font-size: var(--fs-md) !important; padding: 10px 18px !important; }
  .btn-pct { padding: 7px 6px !important; }
  /* Mid-row facon QML : 1/3 · 1/2 · Pot · Tapis compacts groupes a gauche,
     dropdown pousse a droite. (Pas de width fixe sur le Tapis : 52 px cassait
     le border-image 9-slice du skin de boutons.) */
  .mid-row { justify-content: flex-start !important; }
  .mid-row .pct-row { flex: 0 0 auto !important; width: auto !important; }
  .pct-row .btn-pct { flex: 0 0 auto !important; min-width: 52px !important; }
  .mid-row .btn-allin { flex: 0 0 auto !important; min-width: 0 !important; }
  .mid-row .mode-sel-wrap { margin-left: auto !important; flex: 0 0 auto !important; }
  /* Pas d'agrandissement des spans quick-bet : meme taille qu'en base/mobile
     pour garder les boutons 1/3 - 1/2 - Pot a la meme hauteur (compacte). */
  .raise-row .raise-input { font-size: var(--fs-base) !important; }

  /* ── Vignette d'assistance ── */
  .hs-txt { font-size: var(--fs-xs) !important; }

  /* ── Header ── */
  .header { min-height: var(--header-h, 38px) !important; }
  .header-brand { font-size: var(--fs-lg) !important; }
  .header .btn-sm { height: 30px !important; font-size: var(--fs-base) !important; padding: 0 13px !important; }
  .header .btn-icon { width: 34px !important; padding: 0 !important; }

  /* ── Barre d'état de jeu : tailles QML fixes (13/11 px), pas d'override ── */

  /* ── Menu ••• ── */
  #g-overflow-menu, #l-overflow-menu, #cr-overflow-menu, #pv-overflow-menu { width: 264px !important; }
/* « La derniere surface ouverte passe devant » (z-order.mjs, bande 300-390) :
   un menu de header OUVERT est par definition la derniere surface ouverte,
   mais certains headers creent un contexte d'empilement (connect :
   position:absolute + z-index:20) qui plafonne leurs menus SOUS les fenetres
   flottantes. Tant qu'un menu est ouvert, son header monte juste AU-DESSUS de
   la bande (391) puis redescend a la fermeture (:has ne matche plus). Les
   modales/toasts/bannieres (950+) restent prioritaires. */
.header:not(.connect-header):has([id$="-overflow-menu"].open),
.header:not(.connect-header):has(.cl-links[open]) { position: relative; z-index: 391; }
/* Header connect : la regle de base #s-connect .connect-header porte deja
   z-index:20 avec une specificite id+classe — il faut au moins l'egaler,
   sinon la levee a 391 ne s'applique jamais (bug web.52). Le menu hamburger
   est un <details class="cl-links"> : l'etat ouvert est l'attribut [open],
   pas une classe .open. */
#s-connect .connect-header:has([id$="-overflow-menu"].open),
#s-connect .connect-header:has(.cl-links[open]) { z-index: 391; }
  #g-overflow-menu .btn-sm, #l-overflow-menu .btn-sm, #cr-overflow-menu .btn-sm, #pv-overflow-menu .btn-sm { font-size: var(--fs-md) !important; padding: 9px 32px 9px 14px !important; }

  /* ══ Fenetres d'informations ══ */
  .winner-card { max-width: min(640px, 86vw) !important; padding: 24px 28px !important; }
  .wc-trophy { font-size: 2.5rem !important; }
  .wc-label { font-size: var(--fs-xs) !important; }
  .wc-name { font-size: 1.7rem !important; }
  .wc-gain { font-size: var(--fs-xl) !important; }
  .wc-stat-label { font-size: var(--fs-xs) !important; }
  .wc-stat-value { font-size: var(--fs-lg) !important; }
  .wc-section { font-size: var(--fs-xs) !important; }
  .wc-player-row { grid-template-columns: 30px minmax(0,1fr) auto 120px !important; }
  .wc-player-av { width: 30px !important; height: 30px !important; font-size: var(--fs-sm) !important; }
  .wc-player-name { font-size: var(--fs-md) !important; }
  .wc-player-stack { font-size: var(--fs-sm) !important; }
  .wc-player-delta { font-size: var(--fs-lg) !important; }
  .winner-dismiss { font-size: var(--fs-md) !important; padding: 12px 24px !important; }
  /* Resume de manche : cartes agrandies (desktop souris seulement) */
  .wc-cards-row .pk    { width: 48px !important; height: 68px !important; font-size: var(--fs-md) !important; }
  .wc-player-cards .pk { width: 38px !important; height: 54px !important; font-size: var(--fs-base) !important; }

  .endgame-card { max-width: 600px !important; padding: 26px 34px !important; }
  .eg-trophy { font-size: 3.6rem !important; }
  .eg-title { font-size: var(--fs-2xl) !important; }
  .eg-winner-av { width: 46px !important; height: 46px !important; font-size: 1.9rem !important; }
  .eg-winner-av.letter { font-size: var(--fs-xl) !important; }
  .eg-winner-label { font-size: var(--fs-sm) !important; }
  .eg-winner-name { font-size: 1.28rem !important; }
  .eg-stats-title { font-size: var(--fs-xs) !important; }
  .eg-stat-row { font-size: var(--fs-md) !important; }
  .eg-btn { font-size: var(--fs-md) !important; padding: 12px 14px !important; }

  .gim-card { width: min(460px, 100%) !important; max-height: min(82vh, 680px) !important; }
  .gim-title { font-size: var(--fs-xl) !important; }
  .gim-subtitle { font-size: var(--fs-base) !important; }
  .gim-badge { font-size: var(--fs-xs) !important; }
  .gim-section-title { font-size: var(--fs-xs) !important; }
  .gim-row { font-size: var(--fs-md) !important; }

  .pim-card { max-width: 400px !important; }
  .pim-avatar { width: 112px !important; height: 112px !important; }
  .pim-avatar.is-letter { font-size: 2.9rem !important; }
  .pim-name { font-size: 1.28rem !important; }
  .pim-flag { font-size: var(--fs-md) !important; }

  .km-card:not(.adv-card):not(.jr-card) { width: min(460px, 100%) !important; max-height: min(82vh, 680px) !important; }
  .km-title { font-size: var(--fs-xl) !important; }
  .km-subtitle { font-size: var(--fs-base) !important; }
  .km-row .km-av { width: 40px !important; height: 40px !important; font-size: 1.7rem !important; }
  .km-row .km-av.letter { font-size: var(--fs-lg) !important; }
  .km-row .km-name { font-size: var(--fs-md) !important; }
  .km-row .km-meta { font-size: var(--fs-sm) !important; }

  .ld-card { max-width: 500px !important; padding: 26px 28px !important; }
  .ld-title { font-size: 1.22rem !important; }
  .ld-body { font-size: var(--fs-md) !important; }
  .ld-btn { font-size: var(--fs-md) !important; }
}

/* Tablet */
/* Exception canon (décision B) — écran de jeu, voir §RESPONSIVE OVAL SIZE. */
@media (max-width: 768px) {
  .blind-chip  { width: 19px !important; height: 19px !important; top: -11px !important; left: -11px !important; }
  .dealer-chip { width: 19px !important; height: 19px !important; top: -11px !important; right: -11px !important; }
  .seat-flag   { bottom: -6px !important; right: -6px !important; }
  .seat-flag .cc-flag { width: 16px !important; height: 12px !important; }
  /* Drapeaux agrandis UNIQUEMENT en mode pokerth.net (body.pth-net) */
  body.pth-net .seat-flag   { bottom: -3px !important; right: -12px !important; }
  body.pth-net .seat-flag .cc-flag { width: 20px !important; height: 15px !important; }
  .seat-avatar { width: 28px; height: 28px; font-size: var(--fs-xs); }
  .seat-name,.seat-money,.seat-action-label,.seat-bet { font-size: 0.48rem; }
.pill { font-size: var(--fs-2xs); padding: 2px 5px; }
  .pk { width: 36px; height: 52px; font-size: var(--fs-base); }
  .pk.sm { width: 28px; height: 40px; font-size: var(--fs-xs); }
  /* table-zone padding handled by table-scaler */
  /* Oval moins large : libère de l'espace pour les joueurs sur les côtés */
  .felt-oval { width: min(400px, 72vw) !important; }
}
/* Exception canon (décision B) — table de jeu portrait, voir §RESPONSIVE OVAL SIZE. */
@media (max-width: 740px) and (orientation: portrait) {
  /* Oval: compact to fit 10 seats */
  .felt-oval::before {
  content: '';
  position: absolute;
  inset: 10px;
  border-radius: 50% / 42%;
  border: 2px solid rgba(255,255,255,0.07);
  pointer-events: none;
}
.felt-oval { overflow: visible; width: 68vw; height: 42vw; min-height: 130px; }

  /* Cards */
  .pk { width: 28px; height: 40px; font-size: var(--fs-xs); }
  .pk.sm { width: 22px; height: 32px; font-size: var(--fs-3xs); }

  /* Cartes communes : tailles gerees par --comm-scale (slot QML 46x64,
     echelle 1.15 en portrait telephone) — plus d'override fixe ici. */

  /* Chips : très excentrés pour éviter le chevauchement avec le timer */
  .blind-chip  { width: 17px !important; height: 17px !important; top: -13px !important; left: -13px !important; }
  .dealer-chip { width: 17px !important; height: 17px !important; top: -13px !important; right: -13px !important; }
  .seat-flag   { bottom: -5px !important; right: -5px !important; }
  .seat-flag .cc-flag { width: 12px !important; height: 9px !important; }
  /* Drapeaux agrandis UNIQUEMENT en mode pokerth.net (body.pth-net) */
  body.pth-net .seat-flag   { bottom: -2px !important; right: -11px !important; }
  body.pth-net .seat-flag .cc-flag { width: 16px !important; height: 12px !important; }
  /* Seats: tiny for 10-player tables */
  .seat-avatar { width: 18px; height: 18px; font-size: 0.46rem; }
  .seat-name { font-size: 0.36rem; }
  .seat-money,.seat-action-label,.seat-bet { font-size: 0.3rem; }
  .seat { min-width: 26px; max-width: 40px; gap: 0px; }
  .comm-row { gap: var(--sp-0); }

  /* Player bar */
  .player-bar-left { padding: 4px 10px; gap: 7px; }
  .pb-avatar { width: 28px; height: 28px; font-size: var(--fs-xs); }
  .pb-name { font-size: var(--fs-xs); }
  .pb-money { font-size: var(--fs-3xs); }
  .pb-info { min-width: 60px; }

  /* table padding via scaler */
  /* ── Gabarit PORTRAIT téléphone : valeurs QML Theme.compact (GameActionBar
     2.1.3) — rangée d'action 56 px police 15, rangée de relance 26 px :
     montant 78 (police 13), quick-bets 38, Tapis 52, dropdown 132 à droite.
     Préfixe .my-zone : les anciennes règles nues (.btn-action…) étaient
     MORTES, battues par les règles de base plus bas dans le fichier (une
     @media n'augmente pas la spécificité — même leçon que le bloc paysage). */
  .my-zone .act-buttons-row .btn-action { min-height: 45px; padding: 0 8px; font-size: 15px; letter-spacing: 0.5px; border-radius: var(--act-btn-radius, 9px); min-width: 0; }
  .my-zone .bet-row { gap: var(--sp-2); }
  .my-zone .bet-row .raise-amt-field { width: 78px; flex: 0 0 auto; height: 26px; min-height: 26px; padding: 1px 6px; font-size: 13px; }
  .my-zone .bet-row .raise-slider { flex: 1 1 auto; min-width: 60px; }
  .my-zone .mid-row { gap: var(--sp-1); align-items: center; }
  .my-zone .mid-row .pct-row { flex: 0 0 auto; width: auto; gap: var(--sp-1); }
  .my-zone .mid-row .btn-pct { flex: 0 0 38px; width: 38px; min-width: 38px; height: 26px; min-height: 26px; padding: 0 2px; border-radius: 5px; font-size: 11px; font-weight: 700; }
  .my-zone .mid-row .btn-pct .pct-amt { display: none; }
  .my-zone .mid-row .btn-allin { flex: 0 0 52px; width: 52px; min-width: 52px; height: 26px; min-height: 26px; font-size: 12px; padding: 0 2px !important; white-space: nowrap; }
  .my-zone .mid-row .mode-sel-wrap { flex: 0 1 132px; min-width: 0; height: 26px; margin-left: auto; }
  .my-zone .mid-row .mode-sel-wrap select { height: 100%; padding-top: 0; padding-bottom: 0; line-height: 1.1; }
  /* Thème à SVG : Tapis 52×26 en image étirée (le 9-slice se déchire à cette
     taille — même solution que paysage/desktop). !important : bat le border
     9px transparent + padding de :root[data-btn-img] .btn-action (règle de
     base, plus bas dans le fichier). */
  :root[data-btn-img] .my-zone .mid-row .btn-allin {
    border: none !important;
    background: transparent !important;
    background-image: var(--btn-allin-img) !important;
    background-size: 100% 100% !important;
    background-repeat: no-repeat !important;
    border-radius: var(--r-sm);
  }
  .my-zone { padding: 5px 8px; }
  .pot-bar { font-size: var(--fs-xs); }

.header-brand { font-size: var(--fs-base); }
  #g-blind { display: none; }
}

/* Global action-bar layout (applies at all sizes). These flex containers
   are required for the FOLD / CALL / RAISE / ALL-IN buttons to lay out
   correctly. Previously orphaned after a stray closing brace. */
.btn-action { padding: 12px 14px; font-size: var(--fs-base); flex: 1; min-width: 80px; text-align: center; }
.pct-row { display: flex; gap: 5px; width: 100%; }
.pct-row .btn-pct { flex: 1; padding: 7px 4px; font-size: var(--fs-sm); }
.raise-row { display: flex; gap: 5px; flex: 2; min-width: 160px; }
.raise-row .raise-input { flex: 1; padding: 10px 6px; font-size: var(--fs-base); }
.btn-allin { flex: 1.5; }
#g-blind { display: none; }

/* ── Phone LANDSCAPE (wide, short) ── */
/* Palier « extra-compact » du chrome de jeu (h<500 = téléphones en paysage) —
   raffinement interne au landscapeCompact canonique h<600, intentionnel. */
@media (max-height: 500px) and (orientation: landscape) {
  /* Header aplati facon landscapeCompact du QML officiel : 30 px, marges reduites. */
  .header { min-height: var(--header-h, 30px) !important; padding: 2px 6px !important; }
  .header .btn-sm { height: 26px !important; }
  .header .btn-icon { width: 30px !important; }
  .blind-chip  { width: 18px !important; height: 18px !important; top: -12px !important; left: -12px !important; }
  .dealer-chip { width: 18px !important; height: 18px !important; top: -12px !important; right: -12px !important; }
  .seat-flag   { bottom: -5px !important; right: -5px !important; }
  .seat-flag .cc-flag { width: 14px !important; height: 10px !important; }
  /* Drapeaux agrandis UNIQUEMENT en mode pokerth.net (body.pth-net) */
  body.pth-net .seat-flag   { bottom: -2px !important; right: -11px !important; }
  body.pth-net .seat-flag .cc-flag { width: 17px !important; height: 13px !important; }
  /* Oval: small, left side */
  .felt-oval::before {
  content: '';
  position: absolute;
  inset: 10px;
  border-radius: 50% / 42%;
  border: 2px solid rgba(255,255,255,0.07);
  pointer-events: none;
}
.felt-oval { overflow: visible; width: 52vw; height: 22vh; min-height: 100px; }
  .pk { width: 28px; height: 40px; font-size: var(--fs-xs); }
  .pk.sm { width: 22px; height: 32px; font-size: var(--fs-3xs); }
  .comm-row { gap: var(--sp-0); }
  .seat-avatar { width: 24px; height: 24px; font-size: var(--fs-2xs); }
  .seat-name,.seat-money,.seat-action-label { font-size: 0.42rem; }

  /* Fixed bars: very compact */
  .pot-bar { font-size: var(--fs-2xs); padding: 1px 0; }
  /* NB cascade : les regles de base (.btn-action, .action-grid, .pb-cards .pk,
     .pb-avatar, .player-bar-left...) sont definies PLUS BAS dans le fichier.
     Une @media n'augmente PAS la specificite -> a specificite egale, ces
     regles de base l'emportaient sur ce bloc et la compaction paysage etait
     ignoree (boutons + dock affiches en taille bureau). On porte donc ces
     overrides par .my-zone / .player-bar pour gagner en specificite. */

  /* Dock joueur (.player-bar) — compact */
  .my-zone { padding: 2px 6px !important; }
  .player-bar { padding: 2px 6px !important; }
  /* Largeur du dock DISSOCIEE de la barre d'action : au lieu de s'etirer a
     --bottom-w (560px) / pleine largeur, il s'ajuste a son contenu et reste
     centre (justify-content du parent) -> pastille compacte, plus de grand
     vide en paysage. */
  .player-bar .player-bar-left { padding: 2px 8px; gap: 5px; flex: 0 1 auto; width: -webkit-fit-content; width: fit-content; max-width: 100%; }
  .player-bar .pb-avatar { width: 22px; height: 22px; font-size: var(--fs-3xs); }
  .player-bar .pb-name { font-size: var(--fs-3xs); }
  .player-bar .pb-money { font-size: var(--fs-3xs); }
  .player-bar .pb-info { min-width: 45px; }
  .player-bar .pb-cards .pk { width: 50px; height: 70px; font-size: var(--fs-lg); }
  .player-bar .pb-action { font-size: var(--fs-3xs); min-width: 0; }

  /* Barre d'action — 2 rangees compactes et previsibles.
     Rangee 1 (pleine largeur) : Fold | Call | mode.
     Rangee 2 (pleine largeur) : 1/3 1/2 Pot | relance | All-In.
     La hauteur reelle (dock + barre) est mesuree en JS (updateBottomLayout)
     -> .my-zone se place AU-DESSUS du dock (plus de touches cachees) et la
     table reserve juste l'espace necessaire (riviere jamais masquee). */
  /* Barre d'action paysage : 3 zones compactes (montant+slider / pastilles+All-In+mode /
     Fold-Call-Raise), meme structure que l'officiel mais plus dense. Hauteur mesuree en JS. */
  /* ── Profil PAYSAGE mobile de la barre d'action : valeurs QML
     landscapeCompact (bible §5.5) — rangee d'action 40 px, police 12 px,
     radius 9, letterSpacing 0.5. min-height:40px annule EXPLICITEMENT le
     min-height:54px de la regle de base (definie plus bas dans le fichier,
     c'est elle qui rendait la barre geante en paysage). Meme organisation
     que le portrait/QML : quick-bets compacts a gauche, Tapis, mode a
     droite. */
  .my-zone .action-grid { gap: var(--sp-0); width: 100%; max-width: 100%; }
  .my-zone .action-grid > * { min-width: 0; }
  .my-zone .bet-row { gap: var(--sp-2); }
  /* Champ montant : 78 px (spec QML §5.2, conservee en compact). */
  .my-zone .bet-row .raise-amt-field { width: 78px; flex: 0 0 auto; height: 24px; min-height: 24px; padding: 1px 5px; font-size: var(--fs-xs); }
  .my-zone .bet-row .raise-slider { flex: 1 1 auto; min-width: 60px; }
  .my-zone .mid-row { gap: var(--sp-1); align-items: center; }
  .my-zone .mid-row .pct-row { flex: 0 0 auto; gap: 3px; }
  .my-zone .mid-row .mode-sel-wrap { flex: 0 1 110px; min-width: 0; height: 24px; margin-left: auto; }
  /* Le select gardait son padding vertical de base dans un wrap de 24px ->
     libelle « Manuel » rogne. Padding vertical 0 + height 100% : le texte
     (16px force par la regle iOS anti-zoom) tient et se centre. */
  .my-zone .mid-row .mode-sel-wrap select { height: 100%; padding-top: 0; padding-bottom: 0; line-height: 1.1; }
  /* Tapis : l'image du theme (--btn-allin-img) en fond etire au lieu du
     border-image 9-slice, qui se dechirait sur ce bouton etroit. Reste
     l'image DU THEME (pas une couleur en dur) -> chaque theme garde son Tapis. */
  .my-zone .mid-row .btn-allin {
    flex: 0 0 auto; white-space: nowrap; min-width: 48px; height: 24px; min-height: 24px; font-size: var(--fs-2xs);
    border-image: none !important;
    border: 1px solid #ef5350 !important;
    background: linear-gradient(180deg, #9e2a2a, #5c1111) !important;
    color: #fff !important;
    padding: 0 10px !important;
    border-radius: var(--r-md);
  }
  /* Thème à SVG de boutons : le Tapis mobile garde l'image DU THÈME, étirée
     comme le QML (le hardcode ci-dessus n'est plus que le REPLI sans pack —
     il écrasait le thème, bug de la passe 405-416). !important requis pour
     battre le border !important du repli ; spécificité supérieure → gagne. */
  :root[data-btn-img] .my-zone .mid-row .btn-allin {
    border: none !important;
    background: transparent !important;
    background-image: var(--btn-allin-img) !important;
    background-size: 100% 100% !important;
    background-repeat: no-repeat !important;
    border-radius: var(--r-sm);
  }
  .my-zone .act-buttons-row { gap: var(--sp-1); }
  .my-zone .act-buttons-row .btn-action { min-height: 30px; padding: 0 8px; font-size: var(--fs-xs); letter-spacing: 0.5px; border-radius: var(--act-btn-radius, 9px); min-width: 0; }
  .my-zone .mid-row .btn-pct { flex: 0 0 auto; min-width: 40px; height: 24px; min-height: 24px; padding: 0 8px; border-radius: var(--r-md); font-size: var(--fs-3xs); font-weight: 700; border-width: 1px; }
  .my-zone .mid-row .btn-pct .pct-p { font-size: var(--fs-2xs); }
  .my-zone .mid-row .btn-pct .pct-amt { display: none; }
  /* Pas de badges de touches clavier sur mobile (parite QML), comme en portrait. */
  .my-zone .act-key { display: none !important; }

.header { padding: 2px 6px; }
.pill { font-size: var(--fs-3xs); padding: 1px 4px; }
  #g-blind, #g-players { display: none; }
  /* ── Bas d'ecran en FLUX (deterministe) ──────────────────────────────
     Plutot que 2 barres position:fixed empilees via une hauteur mesuree en
     JS (fragile : la barre d'action flottait au milieu de l'ecran), on remet
     .my-zone et .player-bar dans le flux de .game-area (colonne flex) : la
     table (flex:1) occupe le haut, la barre d'action puis le dock s'empilent
     dessous, colles en bas. position:relative (et non static) -> .player-bar
     reste le contexte de #hand-strength (absolu) et .action-wrap celui de
     l'epingle. */
  .game-area { padding-bottom: 0 !important; }
  .my-zone, .player-bar {
    position: relative !important;
    left: auto !important; right: auto !important; bottom: auto !important;
    z-index: auto;
  }
  .my-zone    { order: 2; }
  .player-bar { order: 3; background: transparent !important; border-top: 0 !important; }
}



/* ══ HEADERS WITH OVERFLOW MENU (game + lobby) ══ */
/* Both screens host a ••• dropdown (#g-overflow-menu / #l-overflow-menu)
   that uses position:absolute relative to its containing .header. The
   lobby was missing position:relative here, so its menu was escaping
   to the viewport and rendering off-screen. Same chrome for both now. */
#s-game .header,
#s-lobby .header,
#s-create .cp-header,
#privacy-page .cp-header {
  position: relative;
  flex-wrap: nowrap;
  align-items: center;
  overflow: visible;
}

/* ••• overflow menu is used at EVERY screen size for a consistent header
   across phone / tablet / desktop (requested by narmod). The ••• button is
   always visible, and the secondary actions live exclusively in its menu. */
#g-overflow-btn,
#l-overflow-btn,
#connect-overflow-btn { display: inline-flex; align-items: center; }

/* Direct header twins of the menu actions: hidden at all sizes (they are
   provided by the ••• menu now). Was previously a mobile-only rule. */
#fs-btn-game,
#lang-toggle-game,
#admin-start-btn,
#admin-startnobots-btn,
#admin-close-btn,
#admin-kick-btn,
#haptic-toggle-btn,
#voice-toggle-btn,
#fs-btn-lobby,
#lang-toggle-lobby { display: none !important; }

/* Menu déroulant */
#g-overflow-menu,
#l-overflow-menu,
#connect-overflow-menu,
#cr-overflow-menu,
#pv-overflow-menu {
  display: none;
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 200;
  background: var(--modal-bg);
  border: 1px solid var(--border-hi);
  border-radius: var(--r-bar);
  padding: 6px 5px;
  /* Largeur responsive : suit la fenêtre (min 232, plafond 300) au lieu
     d'une valeur fixe — comme le reste de l'app (narmod 19/07). Reste
     bornée pour ne pas s'élargir selon la longueur du libellé. */
  width: clamp(232px, 46vw, 300px);
  box-sizing: border-box;
  flex-direction: column;
  gap: clamp(4px, 1vw, 7px);
  box-shadow: 0 12px 32px rgba(0,0,0,0.7);
}
/* Menu contextuel d'un siège (clic droit / appui long) — ui/seat-menu.mjs.
   Même vocabulaire visuel que les menus ••• de l'en-tête (fond de modale,
   bordure dorée, boutons .btn-sm pleine largeur) : c'est un menu de plus, pas
   un composant de plus. position: fixed parce qu'il s'ancre au POINTEUR, pas à
   un bouton ; z au-dessus des cartes joueur (1200) puisqu'il peut s'ouvrir
   alors qu'une carte est déjà là. */
#seat-ctx-menu {
  display: none;
  position: fixed;
  z-index: 1250;
  background: var(--modal-bg);
  border: 1px solid var(--border-hi);
  border-radius: var(--r-bar);
  padding: 6px 5px;
  width: clamp(200px, 52vw, 264px);
  box-sizing: border-box;
  flex-direction: column;
  gap: 4px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.7);
}
#seat-ctx-menu.open { display: flex; }
#seat-ctx-menu .btn-sm {
  width: 100%; box-sizing: border-box; text-align: left;
  display: flex; align-items: center;
}
#seat-ctx-menu .ctx-danger { color: var(--danger, #e05050); border-color: var(--danger, #e05050); }
/* En-tête : le pseudo visé. Sans lui, un menu ouvert au pointeur ne dit pas
   sur QUI il porte — la boîte QML, elle, reste visible sous son menu. */
#seat-ctx-menu .ctx-hd {
  padding: 3px 8px 5px;
  font-size: var(--fs-2xs); font-weight: 700;
  color: var(--gold-dim, var(--text));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#seat-ctx-menu .menu-sep { height: 1px; margin: 3px 4px; background: var(--border-hi); }
#g-overflow-menu.open,
#l-overflow-menu.open,
#connect-overflow-menu.open,
#cr-overflow-menu.open,
#pv-overflow-menu.open { display: flex; }
#g-overflow-menu .btn-sm,
#l-overflow-menu .btn-sm,
#connect-overflow-menu .btn-sm,
#cr-overflow-menu .btn-sm,
#pv-overflow-menu .btn-sm {
  width: 100%;
  box-sizing: border-box;
  text-align: left;
  /* Lignes plates comme le menu ☰ : pas de cadre dessiné (le .btn-sm de
     base a un border --border), fond transparent au repos. */
  background: transparent;
  border: 1px solid transparent;
  box-shadow: none;
  /* padding-right généreux : on réserve la place du ✓ (positionné en
     absolu à droite) pour que le texte ne passe jamais dessous. */
  padding: clamp(7px, 1.4vw, 10px) clamp(30px, 5vw, 36px) clamp(7px, 1.4vw, 10px) clamp(12px, 2.2vw, 16px);
  border-radius: 4px;
  font-size: clamp(0.8rem, 1.9vw, 0.95rem);
  position: relative;
  overflow: hidden;
  text-overflow: ellipsis;   /* libellé trop long (autre langue) => "…" au lieu d'élargir */
  white-space: nowrap;
}
/* Coche d'état : ancrée à droite, donc toujours au même endroit quelle
   que soit la longueur du libellé ou la langue. */
#g-overflow-menu .menu-tick,
#l-overflow-menu .menu-tick {
  position: absolute;
  right: 11px;
  top: 50%;
  transform: translateY(-50%);
  font-weight: 700;
  color: var(--green);
  pointer-events: none;
}
#g-overflow-menu .btn-sm:hover,
#l-overflow-menu .btn-sm:hover,
#connect-overflow-menu .btn-sm:hover,
#cr-overflow-menu .btn-sm:hover,
#pv-overflow-menu .btn-sm:hover {
  background: var(--panel-hi);
  border-color: var(--border-hi);
}
/* Group separators inside the ••• menu. Plain hairline between groups,
   and a centred labelled divider for the (admin-only) action block. */
#g-overflow-menu .menu-sep,
#l-overflow-menu .menu-sep {
  height: 1px;
  background: var(--border);
  margin: 5px 8px;
  border: 0;
  flex-shrink: 0;
}
#g-overflow-menu .menu-sep-admin {
  height: auto;
  background: none;
  margin: 8px 4px 3px;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-size: var(--fs-3xs);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-dim);
}
#g-overflow-menu .menu-sep-admin::before,
#g-overflow-menu .menu-sep-admin::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* Mobile-only sizing tweaks (the ••• show / secondary-hide rules are now
   global, applied above for all screen sizes). */
/* Exception canon (décision B) — chrome du header de jeu, voir §RESPONSIVE OVAL SIZE. */
@media (max-width: 740px) {
  /* Hauteur uniforme pour tous les btn-sm du header jeu */
  #s-game .header .btn-sm {
    height: 30px;
    padding: 0 9px;
    font-size: var(--fs-base);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
  }
  /* Boutons-icone (quitter, son, trophee, roue crantee) : le padding 0 9px
     ci-dessus laissait 16 px de contenu dans les 34 px -> les SVG 22 px se
     faisaient COMPRESSER (flex-shrink) et paraissaient plus petits qu'aux
     headers login/lobby. On restaure padding 0 -> icones 22 px partout. */
  #s-game .header .btn-icon { width: 34px; padding: 0; }
  /* Le menu ••• du jeu reprend le style "liste" (aligné à gauche, plus
     compact) au lieu d'hériter du centrage/hauteur fixe des boutons du
     header ci-dessus => menu nettement moins large. Sélecteur 2 ids pour
     l'emporter sur "#s-game .header .btn-sm". */
  #s-game .header #g-overflow-menu { width: 214px; }
  #s-game .header #g-overflow-menu .btn-sm {
    justify-content: flex-start;
    text-align: left;
    height: auto;
    padding: 8px 24px 8px 11px;
    font-size: var(--fs-xs);
  }
  #g-overflow-menu .menu-tick,
  #l-overflow-menu .menu-tick { right: 9px; }
  #s-game .header-brand {
    font-size: var(--fs-sm);
    flex-shrink: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Nom de table sur mobile : DANS LE FLUX flex (pas d'absolu → jamais sous
     les icones de droite) mais CENTRE entre la porte (quitter) et le premier
     bouton de droite (son) — demande narmod. flex:1 + justify center ; le
     nom tronque en « ... » et cede toujours la place aux boutons. */
  #s-game .header-brand--game {
    position: static;
    transform: none;
    max-width: none;
    flex: 1 1 auto;
    min-width: 0;
    justify-content: center;
  }
  /* header-pills : plus besoin de spacer — la brand flex:1 ci-dessus occupe
     deja tout l'espace libre et pousse les boutons a droite. */
  #s-game .header-pills { display: none; }
  #s-game .header { gap: var(--sp-1); padding: 3px 8px; }
  /* Hauteur de header UNIFORME (demande narmod) : meme 38 px que desktop,
     tablette et login — base topBar QML 2.1.3. Padding vertical 4 px
     (boutons 30 px + 4+4 = 38). L'ancien 48 px mobile est retire. */
  #s-game .header { padding-top: 4px; padding-bottom: 4px; min-height: var(--header-h, 38px); }
  /* Barre d'état : hauteur/tailles QML identiques au desktop (40 px, 13/11) —
     le mode landscapeCompact (28 px) est géré par sa propre media query. */
  /* Mobile : même asservissement --comm-scale que la règle de base, base un
     cran au-dessus (12 px ≈ l'ancien 0.68rem à échelle 1). */
  #pot-strip .blinds-next { font-size: clamp(10px, calc(12px * var(--comm-scale, 1)), 16px); }

  /* ── Lobby header on narrow phones ──
     On small/large-font devices the "POKERTH" word + redundant buttons
     squeezed the name + player-count pills (they got clipped). Free the
     space: collapse the brand to just the ♠ glyph, drop the standalone
     Fullscreen + language buttons (both still reachable via the ••• menu),
     and tighten the gap. The pills (flex:1) then get the room they need. */
  /* The ♠ acts as a toggle: tap it to slide POKERTH in/out. Collapsed by
     default on narrow screens (max-width:0 + fade), expanded when JS adds
     .brand-open to #lobby-brand. The width transition gives the small
     reveal effect. */
  #s-lobby .header-brand .brand-text {
    display: inline-block;
    max-width: 0;
    opacity: 0;
    overflow: hidden;
    white-space: nowrap;
    transition: max-width 0.28s ease, opacity 0.22s ease, margin 0.28s ease;
    margin-left: 0;
  }
  #s-lobby #lobby-brand.brand-open .brand-text {
    max-width: 8em;
    opacity: 1;
    margin-left: 0;
  }
  #s-lobby #fs-btn-lobby,
  #s-lobby #lang-toggle-lobby { display: none; }
  #s-lobby .header { gap: var(--sp-2); padding: 3px 8px; }
  /* Header du lobby aligné sur le header du jeu, pour une hauteur uniforme
     entre les écrans (demande narmod). Même variable que #s-game .header. */
  #s-lobby .header { padding-top: 4px; padding-bottom: 4px; min-height: var(--header-h, 38px); }
  #s-lobby .header-pills { flex: 1; min-width: 0; }
}


/* ═══════════════════════════════════════════════════════
   ANIMATIONS DE JEU
   ═══════════════════════════════════════════════════════ */

/* ── Distribution des cartes (hole cards) ── */
@keyframes dealCard {
  0%   { transform: translateY(60px) rotate(-8deg) scale(0.6); opacity: 0; }
  60%  { transform: translateY(-4px) rotate(1deg) scale(1.04); opacity: 1; }
  100% { transform: translateY(0) rotate(0deg) scale(1); opacity: 1; }
}
.pk-deal {
  animation: dealCard 0.38s cubic-bezier(.2,.8,.3,1) both;
}
.pk-deal:nth-child(2) { animation-delay: 0.15s; }

/* ── Flip cartes communes (amélioré) ── */
@keyframes cardFlip {
  0%   { transform: rotateY(90deg) scale(0.85); opacity: 0; }
  55%  { transform: rotateY(-6deg) scale(1.06); opacity: 1; }
  80%  { transform: rotateY(3deg) scale(0.99); }
  100% { transform: rotateY(0deg) scale(1); opacity: 1; }
}
/* Timings officiels (bible §9) : flop échelonné 0 / 120 / 240 ms, turn et
   river IMMÉDIATS — durée 260 ms partout (fondu QML ; on garde le flip web,
   héritage ShowFlipCardsAnimation du client Qt-Widgets, aux timings QML). */
.pk-flip { animation: cardFlip 0.26s cubic-bezier(.3,0,.2,1) both; }
.pk-flip:nth-child(1) { animation-delay: 0s; }
.pk-flip:nth-child(2) { animation-delay: 0.12s; }
.pk-flip:nth-child(3) { animation-delay: 0.24s; }
.pk-flip:nth-child(4) { animation-delay: 0s; }
.pk-flip:nth-child(5) { animation-delay: 0s; }

/* ── Joueur actif — anneau pulsant ── */
@keyframes activeRing {
  0%   { box-shadow: 0 0 0 0 rgba(var(--gold-rgb), 0.7), 0 0 12px rgba(var(--gold-rgb), 0.4); }
  50%  { box-shadow: 0 0 0 5px rgba(var(--gold-rgb), 0), 0 0 20px rgba(var(--gold-rgb), 0.6); }
  100% { box-shadow: 0 0 0 0 rgba(var(--gold-rgb), 0.7), 0 0 12px rgba(var(--gold-rgb), 0.4); }
}
.seat.active .seat-avatar {
  animation: activeRing 1.2s ease-in-out infinite !important;
}

/* ── Mon tour — barre actions pulse ── */
@keyframes myTurnBorder {
  0%,100% { border-top-color: var(--gold); box-shadow: 0 -3px 18px rgba(var(--gold-rgb), 0.25); }
  50%     { border-top-color: #ffe066;    box-shadow: 0 -3px 32px rgba(var(--gold-rgb), 0.55); }
}
.my-zone.my-turn-active {
  animation: myTurnBorder 1.5s ease-in-out infinite;
}

/* ── Gagnant — explosion d'étoiles ── */
@keyframes starPop {
  0%   { transform: translate(-50%,-50%) scale(0) rotate(0deg);   opacity: 1; }
  60%  { transform: translate(var(--tx),var(--ty)) scale(1) rotate(var(--rot)); opacity: 1; }
  100% { transform: translate(var(--tx),var(--ty)) scale(0.3) rotate(var(--rot)); opacity: 0; }
}
.win-star {
  position: fixed;
  font-size: var(--fs-xl);
  pointer-events: none;
  z-index: 600;
  animation: starPop 1.1s cubic-bezier(.2,.8,.3,1) forwards;
  left: 50%; top: 40%;
}

/* ── Action joueur — badge flash ── */
@keyframes actionFlash {
  0%   { transform: scale(0.5); opacity: 0; }
  50%  { transform: scale(1.2); opacity: 1; }
  100% { transform: scale(1);   opacity: 1; }
}
.seat-action-label.flash {
  animation: actionFlash 0.3s ease-out both;
}

/* ── Pot — comptage animé ── */
@keyframes potBump {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.18); color: #ffe066; }
  100% { transform: scale(1); }
}
.pot-bump { animation: potBump 0.35s ease-out; }

/* ── Pli (fold) — carte qui tombe ── */
@keyframes foldCard {
  0%   { transform: translateY(0) rotate(0deg); opacity: 1; }
  100% { transform: translateY(30px) rotate(15deg); opacity: 0; }
}
.pk-fold { animation: foldCard 0.28s ease-in forwards; }

/* ── Jeton volant ── */
.flying-chip {
  position: fixed;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #ffe066, #c8a820);
  border: 2px solid rgba(255,255,255,0.5);
  box-shadow: 0 2px 8px rgba(0,0,0,0.5), inset 0 1px 2px rgba(255,255,255,0.3);
  font-size: 0.4rem; color: #3a2000; font-weight: 900;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none; z-index: 500;
  transition: left 0.5s cubic-bezier(.4,0,.2,1),
              top  0.5s cubic-bezier(.25,0.46,0.45,0.94),
              opacity 0.18s ease 0.42s,
              transform 0.5s ease;
}

/* (Ancien bloc .pk-flip doublon supprimé en 2.1.8-web.6 : placé APRÈS le
   bloc officiel, il écrasait les timings QML — 0,32 s et délais 0,07/0,14/
   0,21/0,28 s — d'où la river (délai 0 !important) affichée avant la turn.) */

/* ══ CHIP ANIMATION ══ */
.flying-chip {
  position: fixed;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, #f0c040, #c8a820);
  border: 2px solid rgba(255,255,255,0.4);
  box-shadow: 0 2px 6px rgba(0,0,0,0.5);
  font-size: 0.45rem; color: #3a2000; font-weight: 900;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none; z-index: 500;
  transition: left 0.55s cubic-bezier(.4,0,.2,1),
              top  0.55s cubic-bezier(.4,0,.2,1),
              opacity 0.2s ease 0.45s,
              transform 0.55s ease;
}

/* ══ HAND STRENGTH DISPLAY ══ */
#hand-strength {
  /* Couche pleine largeur posee sur le bord haut de .player-bar.
     height:0 -> n'ajoute aucune hauteur : la barre garde sa taille. */
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 0;
  pointer-events: none;
  z-index: 3;
  --hs-col: var(--gold);
}
#hand-strength .hs-track {
  position: absolute;
  left: 10px; right: 10px; top: 0;
  height: 3px;
  border-radius: 0 0 3px 3px;
  background: rgba(255,255,255,0.12);
  overflow: hidden;
  box-shadow: 0 1px 4px rgba(0,0,0,0.25);
}
#hand-strength .hs-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--hs-col);
  border-radius: 0 0 3px 3px;
  transition: width 0.35s cubic-bezier(.4,0,.2,1), background 0.3s ease;
}
.pb-right-zone {
  flex: 1 1 auto;
  min-width: 0;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
}
.pb-act-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
}
.hs-lbl {
  flex: 0 0 auto;
  align-self: center;
  max-width: 100%;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 2px 8px;
  background: rgba(0,0,0,0.80);
  border: 1px solid rgba(255,255,255,0.16);
  border-radius: 5px;
  pointer-events: none;
}
:root:not([data-theme="pokerth-light"]) .hs-lbl {
  background: rgba(0,0,0,0.55);
  border: 1px solid rgba(255,255,255,0.18);
}
:root[data-theme="pokerth"] .hs-lbl,
:root[data-theme="dark"] .hs-lbl,
:root[data-theme="redfelt"] .hs-lbl {
  background: rgba(96,110,135,0.40);
  border: 1px solid rgba(255,255,255,0.13);
}
.hs-txt {
  font-family: var(--ff-display);
  font-size: var(--fs-xs);
  line-height: 1.15;
  letter-spacing: 0.01em;
  font-weight: 700;
  color: var(--gold);
  text-align: center;
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-shadow: 0 0 3px rgba(0,0,0,0.85), 0 1px 1px rgba(0,0,0,0.9);
}

/* ══ PANNEAU RÉACTIONS FLOTTANT (header) ══ */
#g-reaction-panel {
  flex-direction: column;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  padding: 10px 14px 12px;
  flex-shrink: 0;
}
@keyframes slideDown {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.react-panel-title {
  font-size: var(--fs-3xs);
  color: var(--gold-dim);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: 8px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.react-panel-close {
  background: none; border: none; color: var(--text);
  cursor: pointer; font-size: var(--fs-md); padding: 0 2px; line-height: 1;
}
/* Bouton "couper les reactions" : emoji barre d'une interdiction quand actif */
.react-title-actions { display: flex; align-items: center; gap: var(--sp-3); }
.react-mute-btn {
  position: relative;
  width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--inset);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: var(--fs-md); line-height: 1;
  padding: 0; cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.react-mute-btn:hover { background: rgba(var(--gold-rgb), 0.15); border-color: var(--gold-dim); }
/* Épingle du panneau réactions : même gabarit que le bouton mute. Non épinglé
   (défaut) = la fenêtre se ferme après chaque réaction (parité QML) ; épinglé
   = bord + fond or, la fenêtre reste ouverte. */
.react-pin-btn {
  position: relative;
  width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--inset);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: var(--fs-base); line-height: 1;
  padding: 0; cursor: pointer;
  filter: grayscale(1); opacity: 0.6;
  transition: background 0.15s, border-color 0.15s, filter 0.15s, opacity 0.15s;
}
.react-pin-btn:hover { background: rgba(var(--gold-rgb), 0.15); border-color: var(--gold-dim); }
.react-pin-btn:active { transform: scale(0.92); }
.react-pin-btn.pinned {
  filter: none; opacity: 1;
  border-color: var(--gold-dim);
  background: rgba(var(--gold-rgb), 0.2);
}
.react-mute-btn:active { transform: scale(0.92); }
.react-mute-btn .rm-emo { transition: filter 0.15s, opacity 0.15s; pointer-events: none; }
.react-mute-btn.muted { border-color: rgba(var(--red-rgb), 0.55); }
.react-mute-btn.muted .rm-emo { filter: grayscale(1); opacity: 0.5; }
.react-mute-btn::after {
  content: ""; position: absolute;
  width: 19px; height: 19px;
  border: 2px solid var(--red); border-radius: 50%;
  pointer-events: none;
}
.react-mute-btn::before {
  content: ""; position: absolute;
  width: 19px; height: 2px;
  background: var(--red); border-radius: 2px;
  transform: rotate(-45deg);
  pointer-events: none;
}
/* Grille grisee + inerte quand les reactions sont coupees */
/* ── Pagination du panneau réactions (3 pages × 30, 2026-08-28) ──
   Pager compact DANS la barre de titre, entre le libellé et l'épingle :
   ‹ [N/3] › — un seul encadré indicateur, mis à jour par setReactionPage.
   IMPORTANT : .react-grid { display:grid } écrase le display:none de
   l'attribut [hidden] (style UA) → on le rétablit ici, sinon TOUTES les
   pages s'affichent en même temps (bug constaté 2026-08-28). */
.react-grid[hidden] { display: none !important; }
/* Glissement discret de la page entrante (flèches et swipe tactile). */
@keyframes rpInLeft  { from { transform: translateX(24px);  opacity: 0.4; } to { transform: none; opacity: 1; } }
@keyframes rpInRight { from { transform: translateX(-24px); opacity: 0.4; } to { transform: none; opacity: 1; } }
.react-grid.rp-in-left  { animation: rpInLeft 0.18s ease-out; }
.react-grid.rp-in-right { animation: rpInRight 0.18s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .react-grid.rp-in-left, .react-grid.rp-in-right { animation: none; }
}
.react-panel-title .react-pager {
  display: inline-flex; align-items: center; gap: 4px;
  letter-spacing: normal; text-transform: none;
}
.react-pg-arrow {
  background: var(--inset); border: 1px solid var(--border); border-radius: 6px;
  /* var(--text) et non --text-dim/--gold : certains packs de thèmes
     redéfinissent --gold très sombre (ex. #1d222b) et le pager devenait
     illisible sur table sombre — même famille de correctif que la section
     « PokerTH clair : corrections de contraste ». Le ✕ voisin
     (.react-panel-close, var(--text)) est lisible sur tous les thèmes. */
  color: var(--text); opacity: 0.85; cursor: pointer; line-height: 1;
  font-size: var(--fs-md); padding: 3px 9px 4px;
  display: inline-flex; align-items: center; justify-content: center;
  transition: border-color 0.15s, opacity 0.15s;
}
.react-pg-arrow:hover { border-color: var(--gold-dim); opacity: 1; }
.react-pg-ind {
  background: var(--inset); border: 1px solid var(--border); border-radius: 6px;
  color: var(--text); padding: 4px 9px; line-height: 1;
  font-size: var(--fs-3xs); min-width: 30px; text-align: center;
  font-variant-numeric: tabular-nums;
}
.react-grid.react-muted { filter: grayscale(1); opacity: 0.4; }
.react-grid.react-muted .react-btn { pointer-events: none; cursor: default; }
.react-grid.react-muted .react-btn:hover { background: var(--inset); border-color: var(--border); transform: none; }
.react-grid {
  display: grid;
  /* Desktop: all 30 reactions on a single line. 1fr tracks fill the
     available width so they never overflow; max-width keeps the cells
     from ballooning on ultra-wide screens, margin auto re-centres. */
  grid-template-columns: repeat(30, 1fr);
  gap: 3px;
  max-width: 1120px;
  margin: 0 auto;
}
@media (max-width: 900px) {
  /* Tablet: 2 lines (15 + 15). */
  .react-grid { grid-template-columns: repeat(15, 1fr); max-width: none; }
}
@media (max-width: 600px) {
  /* Phone : grille montrant 2 lignes ; si tout ne rentre pas -> scroll vertical.
     Tablette (601-900px) et desktop gardent leur propre grille. */
  .react-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, 40px);
    justify-content: center;
    /* largeur definie + marge nulle : annule le `margin:0 auto` de base qui,
       sur un item flex, empechait l'etirement (=> 1 seule colonne). */
    width: 100%;
    max-width: none;
    margin: 0;
    gap: var(--sp-2);
    max-height: 88px;       /* ~2 lignes (cellule 40px + interligne) */
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
  }
  /* Use nearly the full panel width on phones (was 14px each side). */
  #g-reaction-panel { padding: 8px 5px 9px; }
}
.react-btn {
  aspect-ratio: 1;       /* square, height follows the column width */
  width: 100%;           /* fill the grid track (size set by .react-grid) */
  background: var(--inset);
  border: 1px solid var(--border);
  border-radius: 5px;
  font-size: var(--fs-xl);     /* desktop emoji size (cells ~34px) */
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: transform 0.12s ease, background 0.12s, border-color 0.12s;
  position: relative;
  padding: 0;            /* no padding → compact cell */
}
@media (max-width: 900px) {
  /* Tablet: bigger cells → bigger glyphs. */
  .react-btn { font-size: 1.7rem; }
}
@media (max-width: 600px) {
  /* Phone : pastilles carrees, taille de cellule pilotee par la grille. */
  .react-btn { font-size: 1.5rem; }
}
.react-btn:hover {
  background: rgba(var(--gold-rgb), 0.15);
  border-color: var(--gold-dim);
  transform: scale(1.2);
}
.react-btn:active { transform: scale(0.92); }
.react-btn .rc {
  position: absolute;
  bottom: 1px; right: 3px;
  font-size: 0.45rem;
  font-family: monospace;
  color: var(--gold);
  line-height: 1;
  pointer-events: none;
}

/* Ancien .reaction-bar (dans chat) — masqué */
/* ══ RÉACTIONS RAPIDES ══ */
.reaction-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 5px 10px;
  background: var(--panel);
  border-top: 1px solid var(--border);
  flex-shrink: 0;
  justify-content: center;
  flex-wrap: wrap;
}
.reaction-bar-label {
  font-size: var(--fs-3xs);
  color: var(--text);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  flex-basis: 100%;
  text-align: center;
  margin-bottom: 1px;
  opacity: 0.6;
}
.reaction-btn {
  background: var(--inset);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 4px 10px;
  font-size: var(--fs-lg);
  cursor: pointer;
  transition: transform 0.12s ease, background 0.15s, border-color 0.15s;
  display: flex; align-items: center; gap: var(--sp-1);
  line-height: 1;
  position: relative;
  overflow: visible;
}
.reaction-btn:hover {
  background: rgba(var(--gold-rgb), 0.15);
  border-color: var(--gold-dim);
  transform: scale(1.15);
}
.reaction-btn:active { transform: scale(0.92); }
.reaction-btn .react-count {
  font-size: var(--fs-2xs);
  font-family: monospace;
  color: var(--gold);
  min-width: 10px;
}

/* Emoji flottant animé au-dessus de la table */
@keyframes floatReaction {
  0%   { transform: translateY(0)  scale(0.5) rotate(-8deg); opacity: 0; }
  15%  { transform: translateY(-20px) scale(1.3) rotate(5deg);  opacity: 1; }
  70%  { transform: translateY(-120px) scale(1) rotate(-3deg); opacity: 1; }
  100% { transform: translateY(-180px) scale(0.8) rotate(8deg); opacity: 0; }
}
.floating-reaction {
  position: fixed;
  font-size: 2.2rem;
  pointer-events: none;
  z-index: 650;
  animation: floatReaction 2.2s cubic-bezier(.2,.8,.3,1) forwards;
  filter: drop-shadow(0 2px 8px rgba(0,0,0,0.5));
}

/* Badge réaction sur avatar joueur */
@keyframes reactionBadge {
  0%   { transform: scale(0) rotate(-20deg); opacity: 0; }
  30%  { transform: scale(1.3) rotate(5deg);  opacity: 1; }
  70%  { transform: scale(1)   rotate(0deg);  opacity: 1; }
  100% { transform: scale(0.9) rotate(0deg);  opacity: 0; }
}
.seat-reaction {
  position: absolute;
  top: -18px; left: 50%;
  transform: translateX(-50%);
  font-size: var(--fs-xl);
  pointer-events: none;
  animation: reactionBadge 2.5s ease forwards;
  z-index: 10;
  filter: drop-shadow(0 2px 6px rgba(0,0,0,0.6));
}

/* ══ RÉACTIONS ANIMÉES (chorégraphies CSS/JS) ══ */
.rfx { position: fixed; z-index: 700; pointer-events: none; width: 0; height: 0; }
.rfx .rfx-big { position: absolute; left: 0; top: 0; font-size: var(--fs-hero); display: inline-block;
  filter: drop-shadow(0 3px 10px rgba(0,0,0,0.55)); }
.rfx .rfx-pt { position: absolute; left: 0; top: 0; will-change: transform, opacity; line-height: 1; }
.rfx .rfx-ring { position: absolute; left: 0; top: 0; width: 30px; height: 30px;
  border: 3px solid var(--gold-hi); border-radius: 50%;
  transform: translate(-50%,-50%) scale(0.3); opacity: 0.9; animation: rfxRing 0.8s ease-out forwards; }
@keyframes rfxRing { to { transform: translate(-50%,-50%) scale(4); opacity: 0; } }
@keyframes rfxPop  {0%{transform:translate(-50%,-50%) scale(.2);opacity:0}25%{transform:translate(-50%,-50%) scale(1.45);opacity:1}55%{transform:translate(-50%,-60%) scale(1.05)}100%{transform:translate(-50%,-150%) scale(.9);opacity:0}}
@keyframes rfxFire {0%{transform:translate(-50%,-50%) scale(.3);opacity:0}20%{transform:translate(-50%,-55%) scale(1.4);opacity:1}40%{transform:translate(-50%,-70%) scale(1.2) rotate(-4deg)}60%{transform:translate(-50%,-90%) scale(1.35) rotate(4deg)}100%{transform:translate(-50%,-150%) scale(1);opacity:0}}
@keyframes rfxShake{0%{transform:translate(-50%,-50%) scale(.3);opacity:0}15%{transform:translate(-50%,-55%) scale(1.35) rotate(0);opacity:1}25%{transform:translate(-50%,-60%) rotate(-14deg)}35%{transform:translate(-50%,-65%) rotate(14deg)}45%{transform:translate(-50%,-72%) rotate(-12deg)}55%{transform:translate(-50%,-80%) rotate(12deg)}70%{transform:translate(-50%,-95%) rotate(-6deg)}85%{transform:translate(-50%,-115%) rotate(4deg)}100%{transform:translate(-50%,-150%) rotate(0);opacity:0}}
@keyframes rfxBeat {0%{transform:translate(-50%,-50%) scale(.2);opacity:0}20%{transform:translate(-50%,-55%) scale(1.5);opacity:1}33%{transform:translate(-50%,-60%) scale(1.05)}48%{transform:translate(-50%,-70%) scale(1.4)}63%{transform:translate(-50%,-82%) scale(1.1)}100%{transform:translate(-50%,-150%) scale(1);opacity:0}}
@keyframes rfxSpin {0%{transform:translate(-50%,-50%) scale(.3) rotate(0);opacity:0}25%{transform:translate(-50%,-60%) scale(1.45) rotate(200deg);opacity:1}100%{transform:translate(-50%,-150%) scale(1) rotate(720deg);opacity:0}}
@keyframes rfxShine{0%{transform:translate(-50%,-50%) scale(.3);opacity:0;filter:brightness(1)}25%{transform:translate(-50%,-58%) scale(1.45);opacity:1}50%{filter:brightness(2.2) drop-shadow(0 0 10px var(--gold-hi))}100%{transform:translate(-50%,-150%) scale(1.05);opacity:0;filter:brightness(1)}}
@keyframes rfxFlex {0%{transform:translate(-50%,-50%) scale(.3);opacity:0}20%{transform:translate(-50%,-55%) scale(1.5)}35%{transform:translate(-50%,-60%) scale(1.1)}50%{transform:translate(-50%,-70%) scale(1.45)}70%{transform:translate(-50%,-90%) scale(1.15)}100%{transform:translate(-50%,-150%) scale(1);opacity:0}}
/* Choreography durations = keyframe base (1.4-1.7 s) x 1.25, as upstream
   b8a1d18 (ReactionCatalog.qml durationScale / reactionfx.cpp kDurationScale):
   pop 1.6 s -> 2 s, the 2.1.7 flight time. Particles and rings are not scaled. */
.rfx-anim-pop  { animation: rfxPop  2s cubic-bezier(.2,.8,.3,1) forwards; }
.rfx-anim-fire { animation: rfxFire 2.125s ease-out forwards; }
.rfx-anim-shake{ animation: rfxShake 2.125s ease-out forwards; }
.rfx-anim-beat { animation: rfxBeat 2.125s ease-out forwards; }
.rfx-anim-spin { animation: rfxSpin 2s cubic-bezier(.2,.7,.3,1) forwards; }
.rfx-anim-shine{ animation: rfxShine 2s ease-out forwards; }
.rfx-anim-flex { animation: rfxFlex 2s ease-out forwards; }
/* 8 animations supplémentaires (2026-08-28) — 15 chorégraphies au total.
   Même contrat que les 7 historiques : montée vers -150% et fondu (sauf drop,
   qui retombe), durée 1.4–1.7 s, transform seul (compositing GPU). */
@keyframes rfxLaunch{0%{transform:translate(-50%,-30%) scale(.5) rotate(-8deg);opacity:0}20%{transform:translate(-50%,-50%) scale(1.3) rotate(-8deg);opacity:1}100%{transform:translate(-50%,-260%) scale(1) rotate(-8deg);opacity:0}}
@keyframes rfxDrop{0%{transform:translate(-50%,-170%) scale(.7);opacity:0}35%{transform:translate(-50%,-50%) scale(1.25);opacity:1}50%{transform:translate(-50%,-72%) scale(1.1)}65%{transform:translate(-50%,-50%) scale(1.2)}80%{transform:translate(-50%,-58%) scale(1.15)}100%{transform:translate(-50%,-70%) scale(1.1);opacity:0}}
@keyframes rfxWobble{0%{transform:translate(-50%,-50%) scale(.3) rotate(0);opacity:0}15%{transform:translate(-50%,-55%) scale(1.35) rotate(16deg);opacity:1}30%{transform:translate(-50%,-62%) rotate(-14deg)}45%{transform:translate(-50%,-70%) rotate(11deg)}60%{transform:translate(-50%,-80%) rotate(-8deg)}75%{transform:translate(-50%,-95%) rotate(5deg)}100%{transform:translate(-50%,-150%) rotate(0);opacity:0}}
@keyframes rfxFlip{0%{transform:perspective(240px) translate(-50%,-50%) scale(.4) rotateY(0);opacity:0}25%{transform:perspective(240px) translate(-50%,-58%) scale(1.4) rotateY(360deg);opacity:1}100%{transform:perspective(240px) translate(-50%,-150%) scale(1) rotateY(900deg);opacity:0}}
@keyframes rfxZoomout{0%{transform:translate(-50%,-50%) scale(3);opacity:0}20%{transform:translate(-50%,-50%) scale(2.4);opacity:1}45%{transform:translate(-50%,-55%) scale(1.05)}60%{transform:translate(-50%,-58%) scale(1.18)}100%{transform:translate(-50%,-150%) scale(1);opacity:0}}
@keyframes rfxHeartbeat{0%{transform:translate(-50%,-50%) scale(.2);opacity:0}12%{transform:translate(-50%,-52%) scale(1.45);opacity:1}22%{transform:translate(-50%,-56%) scale(1.05)}32%{transform:translate(-50%,-60%) scale(1.5)}42%{transform:translate(-50%,-66%) scale(1.08)}58%{transform:translate(-50%,-76%) scale(1.42)}72%{transform:translate(-50%,-90%) scale(1.1)}100%{transform:translate(-50%,-150%) scale(1);opacity:0}}
@keyframes rfxShiver{0%{transform:translate(-50%,-50%) scale(.3);opacity:0}12%{transform:translate(-52%,-54%) scale(1.3);opacity:1}24%{transform:translate(-48%,-58%) scale(1.3)}36%{transform:translate(-52%,-63%) scale(1.3)}48%{transform:translate(-48%,-68%) scale(1.3)}60%{transform:translate(-52%,-74%) scale(1.3)}72%{transform:translate(-48%,-82%) scale(1.25)}84%{transform:translate(-52%,-95%) scale(1.2)}100%{transform:translate(-50%,-150%) scale(1.1);opacity:0}}
@keyframes rfxTilt{0%{transform:translate(-50%,-50%) scale(.3) rotate(0);opacity:0}20%{transform:translate(-50%,-55%) scale(1.4) rotate(12deg);opacity:1}45%{transform:translate(-50%,-65%) scale(1.2) rotate(28deg)}100%{transform:translate(-50%,-160%) scale(1) rotate(560deg);opacity:0}}
.rfx-anim-launch   { animation: rfxLaunch 1.75s cubic-bezier(.3,.1,.4,1) forwards; }
.rfx-anim-drop     { animation: rfxDrop 1.875s ease-out forwards; }
.rfx-anim-wobble   { animation: rfxWobble 2.125s ease-out forwards; }
.rfx-anim-flip     { animation: rfxFlip 2s cubic-bezier(.2,.7,.3,1) forwards; }
.rfx-anim-zoomout  { animation: rfxZoomout 2s ease-out forwards; }
.rfx-anim-heartbeat{ animation: rfxHeartbeat 2.125s ease-out forwards; }
.rfx-anim-shiver   { animation: rfxShiver 2s linear forwards; }
.rfx-anim-tilt     { animation: rfxTilt 2s ease-in forwards; }
/* 16e animation (2026-08-28) : recul d'arme — double coup vers la droite
   (le glyphe 🔫 pointe à gauche), léger cabrage, puis montée et fondu. */
@keyframes rfxRecoil{0%{transform:translate(-50%,-50%) scale(.4);opacity:0}12%{transform:translate(-50%,-50%) scale(1.35);opacity:1}20%{transform:translate(calc(-50% + 14px),-52%) scale(1.3) rotate(9deg)}34%{transform:translate(calc(-50% + 4px),-52%) scale(1.25) rotate(2deg)}42%{transform:translate(calc(-50% + 10px),-54%) scale(1.28) rotate(6deg)}56%{transform:translate(-50%,-58%) scale(1.2) rotate(0)}100%{transform:translate(-50%,-150%) scale(1);opacity:0}}
.rfx-anim-recoil   { animation: rfxRecoil 1.875s ease-out forwards; }
/* Onde de choc du preset 'boom' (💣) : plus large, plus épaisse, orange feu. */
.rfx .rfx-ring-boom { border-color: #ff9040; border-width: 4px; animation: rfxRingBoom 0.9s ease-out forwards; }
@keyframes rfxRingBoom { to { transform: translate(-50%,-50%) scale(6.5); opacity: 0; } }

/* ══ RECONNECT BANNER ══ */
/* In-app "new version available" banner (bottom, gold accent). Created on
   the fly by the inline SW-update checker when /__ver changes after a deploy. */
#update-banner {
  position: fixed; left: 50%; bottom: 16px; transform: translateX(-50%);
  z-index: 1000;
  display: flex; align-items: center; gap: var(--sp-5);
  background: var(--modal-bg);
  border: 1px solid var(--gold);
  border-radius: 8px;
  padding: 9px 12px 9px 16px;
  box-shadow: 0 6px 22px rgba(0,0,0,0.55);
  font-family: var(--ff-display); font-size: var(--fs-base);
  color: var(--cream); letter-spacing: 0.04em;
  max-width: calc(100% - 24px);
  animation: fadeIn 0.3s ease;
}
#update-banner button {
  background: var(--gold); color: var(--on-gold); border: none;
  border-radius: 5px; padding: 6px 14px; cursor: pointer;
  font-family: var(--ff-display); font-weight: 700; font-size: var(--fs-sm);
  letter-spacing: 0.06em; white-space: nowrap;
  transition: filter 0.12s;
}
#update-banner button:hover { filter: brightness(1.12); }

#reconnect-banner {
  position: fixed; top: 0; left: 0; right: 0; z-index: 999;
  background: rgba(var(--red-rgb), 0.95);
  color: #fff; text-align: center;
  padding: 6px 12px; font-size: var(--fs-sm);
  font-family: var(--ff-display); letter-spacing: 0.08em;
  display: none; align-items: center; justify-content: center; gap: var(--sp-4);
  animation: fadeIn 0.3s ease;
}
#reconnect-banner.visible { display: flex; }
#reconnect-banner .rc-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: #fff; animation: pulse 1s infinite;
}
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} }

/* ── Pastille de connexion sur le feutre ──
   Remplace le bandeau plein écran quand la table est affichée (voir
   _showBanner). Ancrée comme .comm-row (centre des cartes communes,
   --comm-shift-y compris) ; DANS #g-table-scaler → hérite du scale générique
   de la table, et la police suit --comm-scale (même modèle que
   .win-hand-badge / .pot-badge) → grossit/rétrécit avec la résolution. */
.conn-pill {
  position: absolute;
  top: calc(50% + var(--comm-shift-y, 0px)); left: 50%;
  transform: translate(-50%, -50%);
  z-index: 60;                 /* au-dessus des cartes et du badge de main */
  display: flex; align-items: center; justify-content: center;
  gap: max(5px, calc(8px * var(--comm-scale, 1)));
  padding: max(3px, calc(5px * var(--comm-scale, 1))) max(9px, calc(14px * var(--comm-scale, 1)));
  border-radius: var(--r-pill);
  background: rgba(var(--red-rgb), 0.95);
  border: 1px solid rgba(255, 255, 255, 0.55);
  box-shadow: 0 0 14px rgba(var(--red-rgb), 0.5), 0 4px 14px rgba(0, 0, 0, 0.45);
  color: #fff;
  font-family: var(--ff-display); letter-spacing: 0.08em;
  font-size: max(10px, calc(12px * var(--comm-scale, 1)));
  font-weight: 700;
  white-space: nowrap;
  pointer-events: none;        /* n'intercepte jamais un tap destiné au feutre */
  animation: fadeIn 0.3s ease;
}
/* ── Annulation de la reconnexion (parité QML 2.1.6, abortAutoReconnect) ──
   Bouton ✕ dans la pastille et bouton « Annuler » dans le bandeau : mêmes
   codes visuels que leurs conteneurs (bord blanc 55 %, fond transparent,
   police display, tailles asservies à --comm-scale pour la pastille). La
   sous-ligne « siège réservé » passe la pastille en 2 lignes (flex-wrap)
   uniquement quand elle est affichée (rejoin armé). */
.conn-pill { flex-wrap: wrap; }
.conn-pill .cp-x {
  flex: none; cursor: pointer;
  width: max(15px, calc(19px * var(--comm-scale, 1)));
  height: max(15px, calc(19px * var(--comm-scale, 1)));
  padding: 0; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: transparent; color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.55);
  font-size: max(8px, calc(10px * var(--comm-scale, 1)));
  font-family: inherit;
}
.conn-pill .cp-x:hover { background: rgba(255, 255, 255, 0.18); }
.conn-pill .cp-x:active { transform: scale(0.94); }
.conn-pill .cp-sub {
  flex-basis: 100%; text-align: center;
  font-size: max(8px, calc(9.5px * var(--comm-scale, 1)));
  letter-spacing: 0.03em; opacity: 0.92;
}
#reconnect-banner .rc-sub { font-size: var(--fs-xs); opacity: 0.92; }
#reconnect-banner .rc-cancel {
  cursor: pointer; padding: 2px 10px; border-radius: var(--r-pill);
  background: transparent; color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.55);
  font-family: var(--ff-display); letter-spacing: 0.08em;
  font-size: var(--fs-xs);
}
#reconnect-banner .rc-cancel:hover { background: rgba(255, 255, 255, 0.18); }
#reconnect-banner .rc-cancel:active { transform: scale(0.97); }
.conn-pill .rc-dot {
  width: max(6px, calc(8px * var(--comm-scale, 1)));
  height: max(6px, calc(8px * var(--comm-scale, 1)));
  border-radius: 50%; background: #fff; animation: pulse 1s infinite;
  flex: none;
}

/* ══ HANDS HELP OVERLAY ══ */
#hands-overlay {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  background: transparent; pointer-events: none;  /* fenêtre flottante, pas de backdrop */
  padding: var(--sp-5);
}
#hands-card-inner { pointer-events: auto; }
.hand-row {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: 7px 6px; border-bottom: 1px solid rgba(var(--gold-rgb), 0.07);
  border-radius: 4px; transition: background 0.12s;
}
.hand-row:hover { background: var(--inset-hi); }
.hand-rank {
  width: 20px; text-align: center; flex-shrink: 0;
  font-size: var(--fs-2xs); color: var(--gold-dim); font-family: var(--ff-display);
}
.hand-cards { display: flex; gap: var(--sp-0); flex-shrink: 0; }
.hc {
  width: 22px; height: 32px; border-radius: var(--r-xs);
  background: #f8f5ee; border: 1px solid #ccc;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  font-size: var(--fs-3xs); font-weight: 700; line-height: 1.1;
  color: #111; box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.hc.r { color: #c0392b; }
.hand-info { flex: 1; min-width: 0; }
.hand-name {
  font-size: var(--fs-sm); color: var(--text-hi); font-weight: 600;
  font-family: var(--ff-display); letter-spacing: 0.04em;
  display: flex; align-items: center; gap: var(--sp-2);
}
.hand-ico { font-size: var(--fs-md); line-height: 1; flex-shrink: 0; }
.hand-desc { font-size: var(--fs-2xs); color: var(--text); margin-top: 1px; }
.hand-desc .hand-ex { color: var(--gold-hi); }
/* Rail de force (vert fort → gris faible) à gauche de chaque ligne */
.hand-rail { width: 4px; border-radius: var(--r-xs); flex-shrink: 0; align-self: stretch; }
/* Carte qui COMPTE = éclairée ; carte inutile = estompée */
.hc.key  { box-shadow: 0 0 0 1.5px var(--gold-hi), 0 0 7px rgba(224,192,112,0.55); }
.hc.kick { opacity: 0.34; filter: grayscale(0.55); }
/* Légende pédagogique */
.hands-legend {
  display: flex; align-items: center; justify-content: center;
  gap: var(--sp-5); flex-wrap: wrap; font-size: var(--fs-2xs); color: var(--text);
  padding: 8px 0 7px; margin-bottom: 4px;
  border-bottom: 1px solid rgba(var(--gold-rgb), 0.07);
}
.hands-legend .hl { display: flex; align-items: center; gap: 5px; }
.hl-mini { width: 13px; height: 18px; border-radius: 2px; background: #f8f5ee; border: 1px solid #ccc; display: inline-block; }
.hl-mini.bright { box-shadow: 0 0 0 1.5px var(--gold-hi), 0 0 5px rgba(224,192,112,0.6); }
.hl-mini.dim { opacity: 0.34; filter: grayscale(0.5); }
.hl-grad { width: 46px; height: 7px; border-radius: 4px; display: inline-block;
  background: linear-gradient(90deg,#2ecc71,#c9b144,#6f6353); }

/* ══ ANIMATIONS — Distribution, jetons, flip, stats ══ */

/* Carte volante lors de la distribution */
.fly-card {
  position: fixed; pointer-events: none; z-index: 400;
  width: 26px; height: 37px;
  background: var(--card-back) center / 100% 100% no-repeat, #16301a;
  border-radius: var(--r-xs); border: 1px solid rgba(255,255,255,0.25);
  box-shadow: 0 3px 10px rgba(0,0,0,0.5);
  transition: left 0.42s cubic-bezier(.2,.8,.3,1),
              top  0.42s cubic-bezier(.2,.8,.3,1),
              transform 0.42s ease, opacity 0.15s ease;
}

/* Jeton volant vers le pot */
.fly-chip {
  position: fixed; pointer-events: none; z-index: 400;
  width: 20px; height: 20px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #f5e67a, #c8a850 60%, #8b6914);
  border: 2px solid #5a3800;
  box-shadow: 0 2px 6px rgba(0,0,0,0.6);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.38rem; font-weight: 900; color: #3d2b00;
  transition: left 0.5s cubic-bezier(.3,.9,.4,1),
              top  0.5s cubic-bezier(.3,.9,.4,1),
              opacity 0.2s ease 0.35s, transform 0.5s ease;
}

/* Flip 3D des cartes communes */
@keyframes flipCard3D {
  0%   { transform: perspective(400px) rotateY(-90deg) scale(0.85); opacity: 0; }
  55%  { transform: perspective(400px) rotateY(8deg)   scale(1.04); opacity: 1; }
  80%  { transform: perspective(400px) rotateY(-3deg)  scale(1); }
  100% { transform: perspective(400px) rotateY(0deg)   scale(1); opacity: 1; }
}
.pk.flip-reveal {
  animation: flipCard3D 0.45s cubic-bezier(.25,.8,.25,1) both;
}

/* Panneau statistiques de session */
#stats-overlay {
  position: fixed; top: 36px; right: 0;
  width: 240px; max-height: calc(100vh - 36px);
  background: var(--panel);
  border-left: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  z-index: 200; overflow-y: auto;
  animation: slideDown 0.2s ease;
}
.stats-header {
  padding: 8px 12px; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; justify-content: space-between;
  font-size: var(--fs-2xs); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--gold-dim);
}
.stats-body { padding: 10px 12px; display: flex; flex-direction: column; gap: var(--sp-3); }
.stat-row { display: flex; justify-content: space-between; align-items: center; }
.stat-label { font-size: var(--fs-2xs); color: var(--text); }
.stat-val { font-size: var(--fs-xs); font-weight: 700; font-family: var(--ff-mono); }
.stat-val.pos { color: var(--pos); }
.stat-val.neg { color: var(--neg); }
.stat-divider { border: none; border-top: 1px solid var(--border); margin: 4px 0; }
/* Stats panel tabs (Session / Total / Ranking) */
.stats-tabs { display: flex; border-bottom: 1px solid var(--border); }
.stats-tab {
  flex: 1; background: none; border: none; cursor: pointer;
  padding: 7px 4px; font-size: var(--fs-2xs); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text); font-family: var(--ff-display);
  border-bottom: 2px solid transparent; transition: color 0.12s, border-color 0.12s;
}
.stats-tab.active { color: var(--sel,var(--gold)); border-bottom-color: var(--sel,var(--gold)); }
.stat-note { font-size: var(--fs-3xs); color: var(--gold-dim); line-height: 1.3; margin-bottom: 2px; }
.stat-empty { font-size: var(--fs-2xs); color: var(--text); text-align: center; padding: 14px 0; }
.stats-reset {
  margin-top: 2px; background: rgba(var(--red-rgb), 0.12); color: var(--red);
  border: 1px solid rgba(var(--red-rgb), 0.4); border-radius: 5px; cursor: pointer;
  padding: 7px; font-size: var(--fs-2xs); font-family: var(--ff-display); letter-spacing: 0.06em;
  transition: background 0.12s;
}
.stats-reset:hover { background: rgba(var(--red-rgb), 0.22); }
/* Leaderboard rows: rank · avatar · name · net on top, sub-line below */
.board-list { display: flex; flex-direction: column; }
.board-row {
  display: grid; align-items: center;
  grid-template-columns: 1.6rem 1.4rem 1fr auto;
  grid-template-areas: "rank av name net" "sub sub sub sub";
  column-gap: 6px; row-gap: 1px;
  padding: 6px 0; border-bottom: 1px solid var(--border);
}
.board-row.me { background: rgba(var(--gold-rgb), 0.10); border-radius: 4px; padding-left: 4px; padding-right: 4px; }
.board-rank { grid-area: rank; font-size: var(--fs-sm); text-align: center; }
.board-av   { grid-area: av; font-size: var(--fs-md); text-align: center; line-height: 1; }
.board-name { grid-area: name; font-size: var(--fs-xs); color: var(--cream); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-net  { grid-area: net; font-size: var(--fs-xs); font-weight: 700; font-family: var(--ff-mono); }
.board-net.pos { color: var(--pos); }
.board-net.neg { color: var(--neg); }
.board-sub  { grid-area: sub; font-size: var(--fs-3xs); color: var(--gold-dim); font-family: var(--ff-mono); }
.board-sort { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: 6px; }
.board-sort > span { color: var(--gold-dim); font-size: var(--fs-xs); }
.board-sort select {
  flex: 1; background: var(--field-bg); border: 1px solid var(--border);
  border-radius: var(--r-xs); color: var(--cream); padding: 3px 6px;
  font-family: var(--ff-mono); font-size: var(--fs-xs); outline: none; cursor: pointer;
}
.board-myrank {
  font-size: var(--fs-2xs); color: var(--gold); font-family: var(--ff-mono);
  text-align: center; padding: 2px 0 6px; letter-spacing: 0.04em;
}
.hand-hist-item {
  background: var(--inset); border: 1px solid var(--border);
  border-radius: 4px; padding: 6px 8px; font-size: var(--fs-3xs);
}
.hand-hist-cards { display: flex; gap: 3px; margin-top: 3px; }
.hand-hist-result { margin-top: 3px; font-weight: 700; }



/* Loading dots */
@keyframes blink {
  0%,80%,100% { opacity: 0.2; }
  40%          { opacity: 1; }
}
.ld span { animation: blink 1.3s infinite; }
.ld span:nth-child(2) { animation-delay: 0.22s; }
.ld span:nth-child(3) { animation-delay: 0.44s; }

.btn-join {
  background: #2b3444;
  color: #ffffff;
  border: none;
  padding: 4px 12px;
  font-family: var(--ff-display);
  font-size: var(--fs-xs);
  font-weight: 700;
  border-radius: 2px;
  cursor: pointer;
  letter-spacing: 0.1em;
  transition: background 0.15s;
  flex-shrink: 0;
}
.btn-join:hover { background: #384252; }
.gl-guestlock { align-self: center; font-size: var(--fs-2xs); color: var(--text); opacity: 0.55; white-space: nowrap; padding: 3px 6px; border: 1px dashed var(--border); border-radius: var(--r-sm); }
.game-row { cursor: pointer; }

/* ═══ MOBILE: compact buttons in the available-tables list ═══
   On screens up to 540px, the Rejoindre + Regarder buttons were
   eating the entire row width, truncating the table name to
   "WebGame-o..." (see screenshot IMG_8007). The cluster on the
   right used to be: '2/2  ▶ REJOINDRE  👁 REGARDER'. We shrink the
   buttons and the count badge, drop the letter-spacing so the
   labels are tighter, and use shorter padding. The .game-info
   container already has flex:1 + min-width:0 (line 438), so any
   space freed here is automatically given to the name. */
@media (max-width: 599.98px) {
  .game-row { gap: var(--sp-3); padding: 10px 12px; }
  .game-name { font-size: var(--fs-base); }
  .game-meta { font-size: var(--fs-xs); gap: var(--sp-2); }
  .game-count { font-size: var(--fs-sm); }
  .btn-join {
    padding: 4px 8px;
    font-size: var(--fs-2xs);
    letter-spacing: 0.04em;
  }
  .btn-watch {
    padding: 4px 7px;
    font-size: var(--fs-2xs);
    letter-spacing: 0.04em;
  }
  /* Shrink the gap inside the right-hand button cluster */
  .game-row > div:last-child { gap: 6px !important; }
}

/* Even tighter on very narrow phones (e.g. iPhone SE, ~320-400px wide).
   The Rejoindre label has been "REJOINDRE / Partie rapide" stuff and
   risks dominating the row again. Just trim further. */
@media (max-width: 400px) {
  .game-row { padding: 9px 10px; gap: var(--sp-2); }
  .btn-join  { padding: 3px 6px; font-size: var(--fs-3xs); }
  .btn-watch { padding: 3px 5px; font-size: var(--fs-3xs); }
  .game-count { font-size: var(--fs-xs); }
  .game-row > div:last-child { gap: 4px !important; }
}

/* ═══ GAME SCREEN ═══ */
#s-game { overflow: visible; flex-direction: column; }

.game-area {
  position: relative; /* ancre des boutons de zoom de la table */
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 300px; /* fallback si flex échoue */
  overflow: visible;
  /* Space for fixed bottom bars */
  padding-bottom: clamp(110px, 17dvh, 140px);
}

.table-zone { display:flex; align-items:center; justify-content:center; overflow:visible; position:relative;
  flex: 1;
  min-height: 160px; /* garde-fou : ne jamais s'effondrer */
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}
.table-scaler { overflow: visible;
  padding: 30px 45px;
  display: flex;
  align-items: center;
  justify-content: center;
  transform-origin: center center;
  flex-shrink: 0;
  min-width: 300px;  /* garantit scrollWidth > 0 */
  min-height: 150px;
}

.felt-oval::before {
  content: '';
  position: absolute;
  inset: 10px;
  border-radius: 50% / 42%;
  border: 2px solid rgba(255,255,255,0.07);
  pointer-events: none;
}
.felt-oval { overflow: visible;
  width: min(520px, 90vw);
  height: min(240px, min(46vw, calc(100dvh - 250px)));
  min-height: 110px;
  background:
    /* Halo éclairage central (lampe au-dessus) */
    radial-gradient(ellipse 55% 45% at 50% 45%, rgba(255,255,220,0.07) 0%, transparent 70%),
    /* Ombres de bord (profondeur) */
    radial-gradient(ellipse 90% 80% at 50% 50%, transparent 60%, rgba(0,0,0,0.35) 100%),
    /* Texture tissu — lignes diagonales très fines */
    repeating-linear-gradient(
      45deg,
      transparent,
      transparent 3px,
      rgba(0,0,0,0.06) 3px,
      rgba(0,0,0,0.06) 4px
    ),
    repeating-linear-gradient(
      -45deg,
      transparent,
      transparent 3px,
      rgba(0,0,0,0.04) 3px,
      rgba(0,0,0,0.04) 4px
    ),
    /* Couleur de base dégradée */
    var(--felt-img, radial-gradient(ellipse 80% 60% at 50% 50%, var(--felt-base-hi) 0%, var(--felt-base-mid) 55%, var(--felt-base-lo) 100%));
  border-radius: 50% / 45%;
  border: 8px solid var(--rail);
  box-shadow:
    0 0 0 3px var(--rail-dark),
    0 0 0 5px var(--rail-glow),
    inset 0 2px 16px rgba(0,0,0,0.5),
    inset 0 -4px 20px rgba(0,0,0,0.3),
    0 20px 60px rgba(0,0,0,0.8),
    0 0 40px rgba(0,0,0,0.4);
  position: relative;
  overflow: visible;
  flex-shrink: 0;
}

/* Community cards — centred inside oval.
   The previous value top:56% pushed the row 6% below center, which on
   mobile/tablet visibly drifted the cards toward the bottom rim of the
   felt. Pure 50% centers them on the vertical axis of the oval (the
   gradient halo and shadow ring are already designed around 50%/45%). */
/* ── Cartes communes : modele QML 2.1.3 (bible §9) — slot 46x64 (ratio
   120:168 ≈ 0.714), spacing 3, le tout multiplie par --comm-scale
   (equivalent communityScale QML). UNE seule regle de taille pour tous les
   appareils ; seule l'echelle change par media ci-dessous (valeurs calees
   sur les rendus existants pour eviter toute regression) :
   desktop 1.35 (≈62x87) · tablette/base 1.0 (46x64 natif) ·
   phone portrait 1.15 (≈53x74) · paysage compact 0.62 (≈29x40). */
:root { --comm-scale: 1; }
@media (min-width: 900px) and (min-height: 600px) { :root { --comm-scale: 1.35; } }
/* Exception canon (décision B) — voir §RESPONSIVE OVAL SIZE. */
@media (max-width: 740px) and (orientation: portrait) { :root { --comm-scale: 1.15; } }
@media (orientation: landscape) and (max-height: 600px) { :root { --comm-scale: 0.62; } }
.comm-row {
  position: absolute;
  /* --comm-shift-y : décalage vertical piloté par renderSeats (parité QML
     communityArea — barycentre des boxes en paysage, ~centre en portrait). */
  top: calc(50% + var(--comm-shift-y, 0px)); left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  gap: calc(3px * var(--comm-scale, 1));
  align-items: center;
  justify-content: center; /* #g-comm est en flux pleine largeur (ID position:relative) -> centrer les cartes dedans */
}
.comm-row .pk {
  width: calc(46px * var(--comm-scale, 1));
  height: calc(64px * var(--comm-scale, 1));
  font-size: calc(1.02rem * var(--comm-scale, 1));
  border-radius: calc(5px * var(--comm-scale, 1));
}

/* Pot display */
.pot-display { display: none; }

/* Badge pot au-dessus des cartes (parité pot badge QML §9). Positionné dans le
   feutre, juste au-dessus de la rangée de cartes communes. Affiché sur TOUTES
   les tailles (mobile compris, demande narmod) quand le pot est non nul
   (classe .has-pot posée par setPot). */
.pot-badge {
  display: none;
  position: absolute;
  left: 50%;
  top: calc(50% + var(--comm-shift-y, 0px)); /* suit la rangée de cartes */
  /* Ancrage QML : bottom du badge = top des cartes communes + marge 8px,
     le tout suivant communityScale (--comm-scale). Demi-hauteur de carte
     32px + marge 8px = 40px x echelle -> position ET taille identiques au
     client officiel, en portrait comme en paysage, sur tous les appareils. */
  --pot-badge-lift: calc(40px * var(--comm-scale, 1));
  transform: translate(-50%, calc(-100% - var(--pot-badge-lift)));
  z-index: 6;
  align-items: center;
  gap: var(--sp-2);
  padding: max(2px, calc(3px * var(--comm-scale, 1))) max(7px, calc(10px * var(--comm-scale, 1)));
  background: rgba(0, 0, 0, 0.5);
  border: 1.5px solid var(--sel, #E3C800);   /* accent or QML (colorAccent), stable clair/sombre */
  border-radius: var(--r-pill);
  color: var(--sel, #E3C800);
  font-family: var(--ff-display);
  font-weight: 700;
  font-size: max(10px, calc(13px * var(--comm-scale, 1))); /* suit communityScale */
  letter-spacing: 0.05em;
  white-space: nowrap;
  box-shadow: 0 2px 9px rgba(0, 0, 0, 0.42);
  pointer-events: none;
}
.pot-badge::before {
  content: '';
  width: max(9px, calc(12px * var(--comm-scale, 1)));
  height: max(9px, calc(12px * var(--comm-scale, 1)));
  border-radius: 50%;
  background: radial-gradient(circle at 50% 38%, #ffe89a 0%, #f2c94c 55%, #c99a1e 100%);
  border: 2px dashed rgba(0, 0, 0, 0.28);
  box-sizing: border-box;
  flex: 0 0 auto;
}
.pot-badge.has-pot { display: inline-flex; }
/* Le badge se positionne via transform:translate ; l'animation générique
   .pot-pop (transform:scale) écraserait ce translate → le badge sautait sur
   les cartes au flop. Pop dédié qui conserve le translate, et pas de
   display:inline-block forcé (respecte la visibilité tablette/desktop). */
.pot-badge.pot-pop { display: none; animation: potBadgePop 0.3s ease-out; }
.pot-badge.has-pot.pot-pop { display: inline-flex; }
@keyframes potBadgePop {
  0%, 100% { transform: translate(-50%, calc(-100% - var(--pot-badge-lift))) scale(1); }
  40%      { transform: translate(-50%, calc(-100% - var(--pot-badge-lift))) scale(1.16); }
}

.pot-bar {
  text-align: center;
  padding: 3px 0;
  font-family: var(--ff-display);
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--gold);
  letter-spacing: 0.12em;
  background: rgba(0,0,0,0.25);
  flex-shrink: 0;
}

.my-seat-row {
  display: flex;
  justify-content: center;
  padding: 4px 0;
  flex-shrink: 0;
}

/* Seats */
.seat {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-0);
  min-width: 44px;
  max-width: 68px;
}
/* Sièges cliquables : tap/clic → popup d'info joueur. #g-seats reste en
   pointer-events:none ; on réactive l'interaction au niveau du siège. Les
   sièges « fantômes » (joueur parti) restent non cliquables. Le halo de survol
   (drop-shadow, suit la forme ronde + la palette) est limité aux pointeurs
   précis pour éviter un survol « collé » au tap. */
.seat { pointer-events: auto; cursor: pointer; }
.seat-ghost { pointer-events: none; cursor: default; }
@media (hover: hover) {
  .seat:hover .seat-avatar { filter: drop-shadow(0 0 6px rgba(var(--gold-rgb), 0.85)); }
}

.seat-avatar {
  width: 28px; height: 28px;
  border-radius: 50%;
  background: rgba(0,0,0,0.5);
  border: 2px solid rgba(var(--gold-rgb), 0.3);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-xs); color: var(--text-hi);
  font-family: var(--ff-display);
  position: relative;
  transition: border-color 0.3s, box-shadow 0.3s;
}

/* ── PokerTH official avatar (downloaded from pokerth.net) ──
   When a player has uploaded an avatar on their pokerth.net account, we
   slot it in as an <img class="seat-pth-img"> inside the circular avatar
   wrapper. The wrapper gets .has-pth-avatar so we know to:
     - hide the text fallback (.seat-initial)
     - drop the background tint (the image fills the whole area)
   The image clips itself to a circle via border-radius:50% (no need to
   clip the parent). This is CRITICAL: setting overflow:hidden on the
   .seat-avatar would also clip the SB/BB chip (positioned top:-8px,
   left:-8px) and the dealer chip (top:-8px, right:-8px), which sit
   intentionally outside the circle for visibility. By keeping the
   parent overflow:visible and clipping only the <img> itself, we get
   the round avatar AND the chips remain visible on top. */
.seat-avatar.has-pth-avatar { background: rgba(0,0,0,0.2); }
.seat-avatar.has-pth-avatar > .seat-initial { display: none; }
.seat-avatar > .seat-pth-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* border-radius on a replaced element (<img>) clips the image content
     itself -- no overflow:hidden on the parent needed. */
  border-radius: 50%;
  /* Image must not block pointer events on overlapping chips/badges. */
  pointer-events: none;
  /* Stay BELOW the overlays. Badges/timer have explicit z-index >= 3. */
  z-index: 0;
}
/* Same treatment for the player-bar avatar (your own seat at the bottom).
   No overflow:hidden here either -- the green ring and shadow stay
   intact, the image clips itself via border-radius. */
.pb-avatar.has-pth-avatar { padding: 0; }
.pb-avatar > .pb-pth-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  pointer-events: none;
}
.me-oval-seat .seat-avatar {
  border: 2px solid var(--green) !important;
  box-shadow: 0 0 8px rgba(var(--green-rgb), 0.4);
}
.seat.active .seat-avatar {
  border-color: var(--gold);
  box-shadow: 0 0 12px rgba(var(--gold-rgb), 0.6);
}
/* Countdown arc around active seat avatar */
.seat-timer {
  position: absolute;
  width: 160%; height: 160%;
  top: -30%; left: -30%;
  pointer-events: none;
  overflow: visible;
  z-index: 2;
}
.seat-timer circle.bg  { fill:none; stroke:rgba(0,0,0,0.45); stroke-width:4.5; }
.seat-timer circle.arc { fill:none; stroke-width:4; stroke-linecap:round; }
/* Fond opaque derrière le chiffre */
.seat-timer circle.center-bg {
  fill: transparent;
  stroke: none;
}
.seat-timer text {
  font-size: 10px;
  font-family: monospace;
  font-weight: 900;
  text-anchor: middle;
  dominant-baseline: middle;
}
/* Quand le timer tourne : initiale ET emoji toujours visibles (au-dessus du SVG arc) */
.seat-avatar.timing > .seat-initial {
  display: flex !important;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 6;
}
.seat-avatar.emoji-av.timing > .seat-initial { display: flex; position: relative; z-index: 6; }
/* Avatar image (photo perso / PokerTH) : ne JAMAIS afficher l'initiale par-dessus,
   même pendant le timer. Plus specifique + !important pour battre la regle .timing. */
.seat-avatar.timing.has-pth-avatar > .seat-initial { display: none !important; }
.seat.active .seat-name,
.seat.active .seat-money { opacity: 0.35; transition: opacity 0.3s; }
/* Dealer chip : rendu via SVG chipSvg() dans renderSeats */
.dealer-chip {
  position: absolute; top:-8px; right:-8px;
  z-index: 3; pointer-events: none;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.7));
  animation: chipPop 0.4s cubic-bezier(.17,.67,.35,1.45) both;
}
/* Badges SB / BB */
.blind-chip {
  position: absolute; top:-8px; left:-8px;
  z-index: 4; pointer-events: none;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.6));
  animation: chipPop 0.4s cubic-bezier(.17,.67,.35,1.45) both;
}
/* Drapeau pays sur l'avatar (coin bas-droite, n'empiète pas sur SB/BB/Dealer
   qui sont en haut). Masqué si absent (non rendu). */
.seat-flag {
  position: absolute; bottom: -6px; right: -6px;
  z-index: 4; pointer-events: none;
  line-height: 0;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.8));
  animation: chipPop 0.4s cubic-bezier(.17,.67,.35,1.45) both;
}
.seat-flag .cc-flag { width: 18px; height: 13px; display: block; }
@keyframes chipPop {
  0%   { transform: scale(0) rotate(-40deg); opacity: 0; }
  65%  { transform: scale(1.25) rotate(8deg); opacity: 1; }
  100% { transform: scale(1) rotate(0deg); opacity: 1; }
}
.seat.folded .seat-avatar::before {
  content: '✕';
  position: absolute;
  font-size: var(--fs-xs);
  color: var(--neg);
  font-weight: 900;
  z-index: 3;
  text-shadow: 0 1px 3px rgba(0,0,0,0.8);
}
.seat.me .seat-avatar { border-color: var(--green); }

/* Eliminated / sitting-out seat: stays in place around the felt so the
   other players don't visually rotate between hands, but appears faded
   with a dim border and a small "OUT" badge. The slight grayscale also
   defuses any colour cue (turn highlight, dealer chip) that may linger
   from a previous hand. */
/* .seat-out reste cliquable : pointer-events:auto + cursor:pointer herites
   de .seat -> un tap ouvre la popup d'info du joueur elimine/absent, comme
   en jeu (le handler de clic ne saute que .seat-ghost).
   Estomper le contenu, pas la pastille : l'opacite d'un parent plafonne
   celle de ses enfants (::after compris) -> le badge resterait invisible.
   On grise avatar/nom/tapis/mise separement et on garde la pastille a
   pleine opacite, par-dessus, dans une couleur qui suit le theme. */
.seat.seat-out .seat-plate,
.seat.seat-out .seat-foot {
  opacity: 0.4;
  filter: grayscale(0.6);
}
.seat.seat-out::after {
  content: 'OUT';
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%) rotate(-10deg);
  font-family: var(--ff-display);
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: 0.12em;
  color: #fff;
  background: var(--red);
  padding: 2px 7px;
  border: 1px solid rgba(255,255,255,0.25);
  border-radius: 4px;
  box-shadow: 0 1px 5px rgba(0,0,0,0.45);
  z-index: 4;
  pointer-events: none;
}

.seat-name {
  font-size: var(--fs-xs); color: var(--text-hi);
  white-space: nowrap; text-overflow: ellipsis;
  max-width: 68px; overflow: hidden; text-align: center;
}
.seat-money {
  font-size: var(--fs-xs); color: var(--gold);
  font-family: var(--ff-mono);
}
.seat-bet {
  font-size: var(--fs-3xs);
  background: rgba(0,0,0,0.6); border: 1px solid var(--border);
  border-radius: 20px; padding: 1px 5px;
  color: var(--cream); white-space: nowrap;
}
.seat-action-label {
  font-size: var(--fs-3xs); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text);
}

/* ── Vignette d'action ovale posée sur la box (fidélité client officiel) ──
   Texte traduit (i18n), couleur par action : Fold rouge · Check/Call bleu ·
   Bet/Raise vert · All-in rouge sombre (palette Theme.qml, bible §7). */
.seat-action-badge {
  position: absolute;
  /* Posée sur les cartes (moitié droite de la rangée avatar+cartes), comme le
     client officiel — la rangée du haut est centrée dans .seat, on décale donc
     la vignette vers la droite pour la caler au-dessus des cartes. */
  top: -5px; left: calc(50% + 17px); transform: translateX(-50%);
  z-index: 7;
  white-space: nowrap;
  font-family: var(--ff-display);
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.03em;
  padding: 2px 10px; border-radius: var(--r-pill);
  color: #fff;
  border: 1px solid rgba(0,0,0,0.35);
  box-shadow: 0 2px 6px rgba(0,0,0,0.45);
  pointer-events: none;
}
/* Couleurs = colonne « Badge » du Theme QML (actionBadgeColor : plus sombre que
   le Bottom des boutons), bord = Edge. Surchargeables par thème via var(). */
.seat-action-badge.act-c1 { background: var(--act-badge-fold, #5a1010);  border-color: var(--act-edge-fold, #e87070); } /* Fold */
.seat-action-badge.act-c2,
.seat-action-badge.act-c3 { background: var(--act-badge-call, #122a55);  border-color: var(--act-edge-call, #6aa0e8); } /* Check / Call */
.seat-action-badge.act-c4,
.seat-action-badge.act-c5 { background: var(--act-badge-raise, #123f0b); border-color: var(--act-edge-raise, #7ad06a); } /* Bet / Raise */
.seat-action-badge.act-c6 { background: var(--act-badge-allin, #3c0a0a); border-color: var(--act-edge-allin, #ef5350); } /* All-in */

@keyframes pulse-gold {
  0%,100% { box-shadow: 0 0 10px rgba(var(--gold-rgb), 0.5); }
  50%      { box-shadow: 0 0 22px rgba(var(--gold-rgb), 0.9); }
}

/* ═══ POKER CARDS ═══ */
.pk {
  width: 42px; height: 60px;
  border-radius: 4px;
  position: relative;
  background: #fff; color: #111;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  font-size: var(--fs-md); font-weight: 800;
  /* Ombre portée multi-couche pour effet 3D */
  box-shadow:
    0 1px 0 rgba(255,255,255,0.8) inset,
    0 -1px 0 rgba(0,0,0,0.1) inset,
    2px 4px 8px rgba(0,0,0,0.45),
    4px 8px 20px rgba(0,0,0,0.25),
    0 0 0 1px rgba(0,0,0,0.08);
  border: 1px solid rgba(0,0,0,0.15);
  line-height: 1.1; user-select: none; flex-shrink: 0;
}
/* ♥ cœur : rouge profond classique */
.pk.red { color: #c0392b; }
/* ♦ carreau : rouge plus orangé/vermillon pour le distinguer du ♥ (narmod)
   — les deux étant identiquement rouges, les confondre était facile. */
.pk.diamond { color: #e85a2c; }
/* Deck 4 couleurs (parite 2.1.2 nobus_4c_classic) : ♦ bleu, ♣ vert ;
   ♥ rouge et ♠ noir inchanges. Glyphes seulement (un deck d'images prime). */
/* Dos de carte « Medaillon + Treillis » (theme feutre/or) : SVG vectoriel
   net et scalable a toutes les tailles, rasterise une fois, zero cout runtime.
   Defini une seule fois dans --card-back, partage avec .fly-card. */
.pk.back {
  background: var(--card-back) center / 100% 100% no-repeat, #16301a;
  color: transparent; font-size: 0;
}
.pk.back::after { content: none; }
/* ── Slot vide des community cards : placeholder QML officiel (Bible §9) ──
   Rectangle noir 30 % + bord blanc 38 %, PAS de dos de carte ni d'ombre 3D.
   La carte n'apparaît que lors de la révélation (flip inchangé). */
.pk.comm-slot {
  background: rgba(0,0,0,0.30);
  border: 1px solid rgba(255,255,255,0.38);
  box-shadow: none;
  color: transparent; font-size: 0;
}
.pk.comm-slot::after { content: none; }

/* ── Jeu de cartes : deck SVG (axe "Cartes", data-deck="svg") ──
   Defaut = glyphes (.c-rank/.c-suit). Avec data-deck="svg", chaque face .pk
   affiche son SVG (chemin pose en variable inline --cf par cardToHtml/cardHtml)
   et les glyphes sont masques ; le dos (et les fly-cards de distribution)
   passent par --card-back surcharge ci-dessous. Switch 100% CSS, instantane,
   sans re-render. Assets : /cards/svg/*.svg (PokerTH, AGPL-3.0). */
:root[data-deck] .pk:not(.back):not(.comm-slot) {
  background-image: var(--cf, none);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  background-position: center;
  background-color: transparent;
}
:root[data-deck] .pk:not(.back):not(.comm-slot) .c-rank,
:root[data-deck] .pk:not(.back):not(.comm-slot) .c-suit { display: none; }
/* Dos : --card-back est pose en inline sur <html> par le boot + _refreshDeck
   (deck-aware), pour .pk.back ET .fly-card, quel que soit le deck. */
.pk.sm { width: 32px; height: 46px; font-size: var(--fs-sm); font-weight: 700; }
.pk.md { width: 46px; height: 66px; font-size: var(--fs-lg); font-weight: 800; }
.pk.xsm { width: 20px; height: 29px; font-size: var(--fs-3xs); font-weight: 700; border-radius: var(--r-xs); }
.pk.lg { width: 58px; height: 84px; font-size: var(--fs-xl); font-weight: 900; }
/* Plus grosses lettres + plus gros symboles de couleur pour lisibilité (narmod).
   La 'couleur' (suit) prend ~1.3em au lieu de 0.9em, le rang ~1.35em au lieu de 1em.
   Avec `line-height:1` et flex-column, deux lignes de ~2.6em tiennent
   dans toutes les tailles de carte (xsm/sm/md/lg) sans débordement. */
/* Carte Classique : agencement standard facon PokerTH — index (rang+symbole)
   en haut-gauche et bas-droite (pivote), grand symbole au centre. */
.pk .c-rank { font-size: 1.35em; line-height: 1; }
.pk .c-suit { font-size: 1.3em; line-height: 1; margin-top: 1px; }
/* Petites cartes : on garde un seul index (coin haut-gauche) + le centre, pour rester lisible. */
/* ♠ pique : fond légèrement bleuté pour distinguer de ♣ trèfle (tous deux noirs) */
.pk.spade { background: linear-gradient(160deg, #f4f7ff 0%, #edf1fc 100%); }

/* ── Bottom zone: my cards + actions ── */
.my-zone {
  position: fixed;
  bottom: calc(56px + env(safe-area-inset-bottom)); /* above player-bar, clears gesture nav */
  left: 0; right: 0;
  z-index: 29;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px 12px;
  background: var(--panel);
  border-top: 1px solid var(--border);
  gap: var(--sp-3);
  /* Nécessaire pour que #hand-strength se positionne par rapport à this */
  overflow: visible;
}

.my-cards {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
}

.my-label {
  font-size: var(--fs-sm);
  color: var(--green);
  letter-spacing: 0.05em;
  font-weight: 600;
}
.my-stack {
  font-size: var(--fs-xs);
  color: var(--gold);
  margin-top: 2px;
}
.me-info {
  display: flex;
  flex-direction: column;
  margin-right: 8px;
}

/* Conteneur centre a la largeur de la barre (--bottom-w) : ancre le pin et
   l'oeil sur la barre, pas sur le bord de l'ecran (tablette/desktop). */
.action-wrap {
  position: relative;
  flex: 1;
  min-width: 0;
  max-width: var(--bottom-w);
  display: flex;
  align-items: center;
  justify-content: center;
}
.action-bar {
  display: flex;
  gap: var(--sp-2);
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
  flex: 1;
}

.btn-action {
  padding: 8px 14px;
  border-radius: 8px;
  font-family: var(--ff-display);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  cursor: pointer;
  border: none;
  transition: filter 0.12s, transform 0.08s;
  white-space: nowrap;
}
.btn-action:active { transform: scale(0.96); }
.btn-action:hover  { filter: brightness(1.15); }

.btn-fold  { background: var(--btn-fold-bg, #6b2020); color: var(--btn-fold-fg, #ffd0d0); }
.btn-check { background: var(--btn-check-bg, #1a5c1a); color: var(--btn-check-fg, #c0ffc0); }
.btn-call  { background: var(--btn-call-bg, #1a4a8a); color: var(--btn-call-fg, #c0d8ff); }
.btn-raise { background: var(--btn-raise-bg, var(--gold)); color: var(--btn-raise-fg, var(--on-gold)); }
/* Compact auto check/fold toggle, sits next to Call/Check. Discreet at
   rest; turns green when armed for the next turn. Icon-only on narrow. */
.btn-auto {
  flex: 0 0 auto;
  min-width: 46px;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  background: rgba(var(--gold-rgb), 0.08);
  color: var(--gold);
  border: 1px solid var(--border);
}
.btn-auto .auto-tx { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.06em; }
.btn-auto .auto-ic { width: 16px; height: 16px; display: block; flex: none; }
.btn-auto .auto-ic.ic-check { color: #5fd089; } /* will auto-check */
.btn-auto .auto-ic.ic-fold  { color: #e08a6f; } /* will auto-fold  */
.btn-auto:hover { background: rgba(var(--gold-rgb), 0.15); border-color: var(--gold-dim); }
.btn-auto.armed {
  background: rgba(var(--green-rgb), 0.22);
  border-color: var(--green);
  color: var(--on-green);
  box-shadow: 0 0 0 1px rgba(var(--green-rgb), 0.35), 0 0 12px rgba(var(--green-rgb), 0.25);
}
.btn-auto.armed .auto-ic { color: var(--on-green); } /* light glyph for contrast on green */
.action-top-row .btn-auto { flex: 0 0 auto; min-width: 46px; }
@media (max-width: 480px) { .btn-auto { min-width: 48px; } }

/* ── Panneau "aperçu des actions" (ouvert en tapant ses cartes) ── */
.pb-cards { cursor: pointer; }
/* Aperçu hors-tour : le MÊME panneau d'action que pendant notre tour, mais
   rendu non interactif — tout est neutralisé au clic SAUF le bouton AUTO,
   le seul réglage qu'on peut armer à l'avance. */
.actions-preview {
  position: relative;
  width: 100%;
  box-sizing: border-box;
  pointer-events: none;            /* coupe Fold / Call / % / relance / All-In */
  opacity: 0.96;
}
.actions-preview .btn-auto { pointer-events: auto; opacity: 1; }  /* AUTO reste activable */
/* Hors-tour (apercu) : touches d'action grisees (desaturees + attenuees) pour
   signaler qu'elles ne sont pas jouables. Le dropdown de mode reste intact.
   grayscale() porte sur les couleurs du theme en cours -> conforme a toutes
   les palettes (y compris boutons-images). */
/* Pré-sélection : les 4 boutons d'action restent CLIQUABLES et colorés en
   aperçu — on peut armer/désarmer une action avant son tour (client officiel).
   Un bouton désactivé (relance indisponible) reste grisé et inactif. */
.actions-preview .btn-action { pointer-events: auto; filter: saturate(0.5); opacity: 0.62; }
.actions-preview .btn-action:disabled { pointer-events: none; filter: grayscale(0.9); opacity: 0.4; }
.actions-preview .btn-action.prearmed { filter: none; opacity: 1; }  /* action pre-armee : pleine couleur */
/* Rien a decider pour l'instant — main jetee, tapis, ou j'ai deja parle et
   personne ne m'a relance depuis : les 4 touches d'action forment une zone
   morte jusqu'a ce que la parole revienne (relance adverse ou street
   suivante), comme dans les clients Qt-Widgets et QML. Ni clic ni
   pre-armement, meme rendu qu'un bouton desactive pour que ce soit lisible
   d'un coup d'oeil. */
.actions-preview.no-action .btn-action,
.actions-preview.no-action .btn-action.prearmed {
  pointer-events: none;
  filter: grayscale(0.9);
  opacity: 0.4;
}
/* « Show » (montrer ses cartes volontairement entre deux mains) : ce bouton
   n'existe QUE hors-tour, donc toujours dans .actions-preview, et la manche
   vient de se terminer -> .no-action est presque toujours posée. Sans cette
   exception il héritait de pointer-events:none et aucun clic ne passait, d'où
   l'impossibilité de montrer ses cartes (rapport forum 02/08/2026). Il reste
   pleinement coloré : c'est la seule action disponible à cet instant. */
.actions-preview .btn-action.btn-show,
.actions-preview.no-action .btn-action.btn-show,
.actions-preview.no-action .btn-action.btn-show.prearmed {
  pointer-events: auto;
  filter: none;
  opacity: 1;
}

/* Contrôles de montant : encore inactifs en aperçu (la pré-relance part au min). */
.actions-preview .btn-pct,
.actions-preview .raise-slider,
.actions-preview .raise-display {
  /* Hors-tour : couleurs conservees mais ternies (desaturees + attenuees),
     signal uniforme « pas ton tour » sans virer au gris. */
  filter: saturate(0.5);
  opacity: 0.62;
}
.actions-preview .mode-sel-wrap { pointer-events: auto; opacity: 1; }  /* mode dropdown reste activable en aperçu */

/* Action pré-armée : bord or + point or en haut à droite (bible §5.3). */
.btn-action.prearmed {
  position: relative;
  outline: 2px solid #FFD700;
  outline-offset: -2px;
  box-shadow: 0 0 0 1px #FFD700, 0 0 12px rgba(255,215,0,0.55);
}
.btn-action.prearmed::after {
  content: '';
  position: absolute;
  top: -4px; right: -4px;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: #FFD700;
  box-shadow: 0 0 4px rgba(255,215,0,0.9);
}
/* Hidden by default; shown only when the user enables it from the ••• menu. */
/* All-In : bouton orange compact dans la ligne 1/3-1/2-Pot (layout officiel).
   Les themes a images de boutons (data-btn-img) repeignent via border-image. */
.btn-allin {
  background: var(--btn-allin-bg, linear-gradient(180deg, #e08a2e, #b5651d));
  color: var(--btn-allin-fg, #fff);
  border: 1px solid var(--btn-allin-bd, #f0a350); outline: none;
  padding: 8px 12px; font-size: var(--fs-xs); letter-spacing: 0.06em;
}
.btn-allin:hover { filter: brightness(1.08); }
.btn-allin b { color: var(--btn-allin-fg-b, #fff); }
/* Image-backed action buttons (9-slice SVG/PNG via the Buttons axis). */
:root[data-btn-img] .btn-action { border: 9px solid transparent; background: transparent; padding: 1px 9px !important; }
:root[data-btn-img] .btn-fold  { border-image: var(--btn-fold-img) 9 fill / 9px / 0 stretch; color: var(--btn-fold-fg, #fff); }
:root[data-btn-img] .btn-check { border-image: var(--btn-check-img) 9 fill / 9px / 0 stretch; color: var(--btn-check-fg, #fff); }
:root[data-btn-img] .btn-call  { border-image: var(--btn-call-img) 9 fill / 9px / 0 stretch; color: var(--btn-call-fg, #fff); }
:root[data-btn-img] .btn-raise { border-image: var(--btn-raise-img) 9 fill / 9px / 0 stretch; color: var(--btn-raise-fg, #fff); }
:root[data-btn-img] .btn-allin { border-image: var(--btn-allin-img) 9 fill / 9px / 0 stretch; color: var(--btn-allin-fg, #fff); }
:root[data-btn-img] .btn-allin b { color: var(--btn-allin-fg-b, #fff); }


.raise-input {
  background: var(--field-bg);
  border: 1px solid var(--border-hi);
  border-radius: 2px;
  color: var(--cream);
  padding: 7px 8px;
  font-family: var(--ff-mono);
  font-size: var(--fs-base);
  width: 72px;
  outline: none;
}
.raise-input:focus { border-color: var(--gold); }

/* ══ PANNEAUX FLOTTANTS (chat + réactions) — hors du flux flex ══
   position:fixed → la zone de table garde toujours toute sa hauteur */
#g-chat-panel {
  position: fixed !important;
  top: var(--ovl-top, 48px);           /* juste sous le header */
  left: 0; right: 0;
  z-index: 100;
  max-height: min(280px, 38dvh) !important;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 4px 20px rgba(0,0,0,0.6);
}
#g-reaction-panel {
  position: fixed !important;
  top: var(--ovl-top, 48px);
  left: 0; right: 0;
  z-index: 100;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 4px 20px rgba(0,0,0,0.6);
}
/* Log panel — same visual treatment as #g-chat-panel so the two
   feel like siblings (the user opens them with adjacent header
   buttons). Only difference is z-index 99 vs 100, keeping the chat
   one tick above when both are open. */
#g-log-panel {
  position: fixed !important;
  top: var(--ovl-top, 48px);
  left: 0; right: 0;
  z-index: 100;
  max-height: min(140px, 22dvh) !important;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 4px 20px rgba(0,0,0,0.6);
  display: flex; /* makes flex-direction:column from inline style effective */
  flex-direction: column;
}
/* Log body — scrollable like .chat-msgs (this is the class on #g-log-body).
   min-height:0 is required so the flex child can shrink and scroll instead
   of overflowing the clamped panel — that absence was why the journal never
   scrolled before. */
.log-panel-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 12px;
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  line-height: 1.45;
  color: var(--cream);
}
.log-panel-body::-webkit-scrollbar { width: 6px; }
.log-panel-body::-webkit-scrollbar-track { background: transparent; }
.log-panel-body::-webkit-scrollbar-thumb { background: rgba(var(--gold-rgb), 0.18); border-radius: var(--r-xs); }
.log-panel-body::-webkit-scrollbar-thumb:hover { background: rgba(var(--gold-rgb), 0.32); }
/* Log message rows — mirror the .chat-msgs basic look without the
   bubble-style backgrounds (log lines are denser, more like a
   monospace journal). */
.log-msgs {
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  line-height: 1.45;
  color: var(--chatlog-text, var(--cream));
}
.log-msgs > div {
  padding: 1px 0;
  border-bottom: 1px solid var(--inset);
}
.log-msgs > div:last-child { border-bottom: none; }
/* Custom scrollbar matching the chat panel */
.log-msgs::-webkit-scrollbar { width: 6px; }
.log-msgs::-webkit-scrollbar-track { background: transparent; }
.log-msgs::-webkit-scrollbar-thumb {
  background: rgba(var(--gold-rgb), 0.18);
  border-radius: var(--r-xs);
}
.log-msgs::-webkit-scrollbar-thumb:hover { background: rgba(var(--gold-rgb), 0.32); }

/* Gouttiere de scrollbar figee sur les trois panneaux defilants (journal de
   jeu, chat de partie, chat lobby). Sans elle, l'apparition de la barre quand
   le contenu depasse la hauteur recale le texte d'une quinzaine de pixels sur
   les navigateurs a scrollbars classiques (desktop). C'est le recalage que le
   client QML a supprime en figeant sa gouttiere a 14 px (ChatBox.scrollGutter,
   ou la largeur conditionnelle creait en plus un cycle de binding). Sans effet
   la ou les scrollbars sont en superposition (iOS, macOS par defaut). */
#g-log-body, #g-chat-msgs, #chat { scrollbar-gutter: stable; }

.g-chat-panel-header { display:flex; align-items:center; justify-content:space-between; padding:4px 10px; background:var(--chrome-tint); border-bottom:1px solid var(--border); flex-shrink:0; }
/* Poignée de redimensionnement du chat (bord bas) — glissable au doigt */
.chat-resize-handle {
  flex-shrink: 0; height: 18px;
  display: flex; align-items: center; justify-content: center;
  cursor: ns-resize; touch-action: none;
  -webkit-user-select: none; user-select: none;
  background: var(--chrome-tint); border-top: 1px solid var(--border);
}
.chat-resize-handle .crh-grip {
  width: 42px; height: 4px; border-radius: var(--r-xs);
  background: var(--gold-dim); transition: background .15s, width .15s;
}
.chat-resize-handle:hover .crh-grip,
.chat-resize-handle.dragging .crh-grip { background: var(--gold-hi); width: 54px; }
.gchat-input { display:flex; gap:5px; padding:5px 10px; background:var(--panel); border-bottom:1px solid var(--border); flex-shrink:0; }
.gchat-input input { flex:1; background:var(--field-bg); border:1px solid var(--border); border-radius:2px; color:var(--cream); padding:5px 8px; font-family: var(--ff-mono); font-size:var(--fs-sm); outline:none; }
.gchat-input input:focus { border-color:var(--gold); }
.gchat-input button { background:rgba(var(--gold-rgb), 0.12); border:1px solid var(--gold-dim); color:var(--gold); padding:5px 10px; border-radius:2px; cursor:pointer; font-size:var(--fs-sm); }

.waiting-msg {
  color: var(--text); font-size: var(--fs-sm);
  font-style: italic; padding: 4px 0;
}
/* ── Spectateur : hauteur de #g-actions réservée ──────────────────────────
   Un joueur assis garde une barre d'action TOUJOURS présente pendant la
   partie, précisément pour que #g-actions ne s'effondre pas et ne relance
   pas le calcul de la table (cf. _pinShow dans renderGameWaiting). Ce garde
   est explicitement désactivé en spectateur — et un spectateur n'a pas de
   barre à afficher. Résultat : #g-actions passait de « une ligne d'attente »
   à « rien » entre les mains, la zone perdait 23 px et TOUTE la table était
   redimensionnée (zone 1263x764 -> 1263x741, boxScale 1.579 -> 1.531,
   commScale 1.775 -> 1.719 — relevé narmod, table WEC 22:00).
   On réserve donc la hauteur d'une ligne .waiting-msg : la zone reste
   constante que le message soit là ou non, sans donner au spectateur la
   moindre commande de jeu. */
body.spectator-nosend #g-actions {
  /* A FIXED box, not a floor. min-height only guarantees the empty case: as
     soon as a message is present its own box can exceed the reservation, the
     zone shrinks and the whole table is recomputed — measured on a recording,
     the table lost 4px of width at the exact frame "next hand" appeared and
     never got it back. A fixed height makes #g-actions invariant whatever it
     holds; the message is centred in it and a freak two-line string is
     clipped rather than allowed to move the table. */
  height: calc(var(--fs-sm) * 2.2 + 10px);
  min-height: calc(var(--fs-sm) * 2.2 + 10px);
  max-height: calc(var(--fs-sm) * 2.2 + 10px);
  box-sizing: border-box; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
body.spectator-nosend #g-actions .waiting-msg {
  padding: 0; margin: 0; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}
/* ══ PASSWORD PROMPT ══ */
#password-prompt {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.75);
}
.pp-card {
  background: var(--modal-bg); border: 1px solid var(--gold-dim);
  border-top: 3px solid var(--gold); border-radius: 8px;
  padding: 20px 24px; width: min(340px, 92vw);
  display: flex; flex-direction: column; gap: var(--sp-5);
  box-shadow: 0 0 60px rgba(0,0,0,0.8);
}
.pp-title { font-family: var(--ff-display); font-size: var(--fs-md); color: var(--text-hi); letter-spacing: 0.1em; }
.pp-label { font-size: var(--fs-2xs); color: var(--gold-dim); letter-spacing: 0.15em; text-transform: uppercase; margin-bottom: 4px; }
.pp-input { background: var(--field-bg); border: 1px solid var(--border); border-radius: 2px; color: var(--cream); padding: 9px 11px; font-family: var(--ff-mono); font-size: var(--fs-md); outline: none; width: 100%; }
.pp-input:focus { border-color: var(--gold); }
.pp-btns { display: flex; gap: var(--sp-3); }
.pp-btns .btn-primary { flex: 1; padding: 9px; font-size: var(--fs-sm); }
.pp-cancel { flex: 0 0 auto; background: transparent; border: 1px solid var(--border); color: var(--text); padding: 9px 16px; border-radius: 2px; cursor: pointer; font-family: var(--ff-mono); font-size: var(--fs-sm); }
.pp-cancel:hover { border-color: var(--border-hi); color: var(--text-hi); }

/* ══ INSTALL POPUP (PWA add-to-home-screen instructions) ══ */
#install-popup {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.75);
  padding: var(--sp-5);
}
.ip-card {
  background: var(--modal-bg); border: 1px solid var(--gold-dim);
  border-top: 3px solid var(--gold); border-radius: 8px;
  padding: 20px 24px; width: min(360px, 92vw);
  display: flex; flex-direction: column; gap: 14px;
  box-shadow: 0 0 60px rgba(0,0,0,0.8);
}
.ip-title { font-family: var(--ff-display); font-size: var(--fs-md); color: var(--text-hi); letter-spacing: 0.08em; text-align: center; }
.ip-body { font-size: var(--fs-base); color: var(--cream); line-height: 1.55; }
.ip-step { display: flex; align-items: flex-start; gap: 9px; margin-bottom: 10px; }
.ip-step:last-child { margin-bottom: 0; }
.ip-step .ip-ico { flex: 0 0 auto; min-width: 18px; color: var(--gold-dim); font-weight: 700; }
.ip-step b { color: var(--gold); font-weight: 600; }
.ip-btns { display: flex; justify-content: center; }
.ip-btns .pp-cancel { padding: 9px 30px; }

/* ══ WINNER OVERLAY ══ */
.winner-overlay {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  /* PAS de voile noir (demande narmod) : la table reste visible derriere,
     seuls la carte et son ombre apparaissent. pointer-events:none sur le
     conteneur -> la table reste cliquable autour ; la carte re-active ses
     propres clics. */
  background: transparent;
  pointer-events: none;
  padding: var(--sp-5);
  animation: fadeIn 0.25s ease;
}
.winner-overlay .winner-card { pointer-events: auto; }
@keyframes fadeIn { from { opacity:0 } to { opacity:1 } }
.winner-card {
  background: var(--modal-bg);
  border: 1px solid var(--gold-dim); border-top: 3px solid var(--gold);
  border-radius: var(--r-bar); padding: 18px 22px;
  max-width: min(440px, 94vw); width: 100%;
  /* Flex column so the players list can be the ONLY scrollable part,
     keeping the header / common cards / Continue button always visible
     even with a full 10-player results list. Previously the whole card
     was a single overflow:auto block, so with 10 players the Continue
     button fell below the fold and was unreachable on small phones. */
  display: flex; flex-direction: column;
  max-height: 88dvh; overflow: hidden;
  box-shadow: 0 0 80px rgba(var(--gold-rgb), 0.2), 0 24px 60px rgba(0,0,0,0.8);
  animation: popIn 0.3s cubic-bezier(0.175,0.885,0.32,1.275);
}
@keyframes popIn { from { transform:scale(0.75); opacity:0 } to { transform:scale(1); opacity:1 } }
.wc-header { display:flex; align-items:center; gap:var(--sp-4); padding-bottom:12px; border-bottom:1px solid var(--border); margin-bottom:12px; }
.wc-trophy { font-size:2rem; line-height:1; }
.wc-titles { flex:1; }
.wc-label { font-family: var(--ff-display); font-size:var(--fs-2xs); letter-spacing:0.22em; text-transform:uppercase; color:var(--gold-dim); }
.wc-name { font-family: var(--ff-display); font-size:clamp(1rem,4vw,1.4rem); font-weight:700; color:var(--gold); }
.wc-gain { font-size:clamp(0.9rem,3.5vw,1.15rem); color:var(--pos); font-weight:700; text-align:right; }
.wc-stats { display:flex; gap:var(--sp-2); margin-bottom:12px; flex-wrap:wrap; }
.wc-stat { flex:1; min-width:65px; background:var(--inset); border:1px solid var(--border); border-radius:4px; padding:6px 8px; text-align:center; }
.wc-stat-label { font-size:var(--fs-3xs); color:var(--gold-dim); letter-spacing:0.1em; text-transform:uppercase; margin-bottom:2px; }
.wc-stat-value { font-size:var(--fs-base); color:var(--cream); font-weight:700; }
.wc-section { font-size:var(--fs-3xs); letter-spacing:0.18em; text-transform:uppercase; color:var(--gold-dim); margin-bottom:5px; }
.wc-cards-row { display:flex; gap:var(--sp-1); margin-bottom:12px; flex-wrap:wrap; }
.wc-players {
  margin-bottom:14px;
  /* Only this region scrolls; flex:1 lets it take the remaining space,
     min-height:0 allows it to shrink below its content height so the
     overflow actually kicks in inside the flex column. */
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  /* a touch of right padding so the scrollbar doesn't overlap deltas */
  padding-right: 2px;
}
/* Grid (et non flex) : colonnes figées => les cartes de chaque joueur
   restent alignées verticalement quel que soit le montant du delta.
   Col1 avatar | Col2 info (élastique) | Col3 cartes (largeur constante)
   | Col4 delta (largeur FIXE, ne pousse plus les cartes). */
.wc-player-row { display:grid; grid-template-columns:24px minmax(0,1fr) auto 90px; align-items:center; gap:7px; padding:5px 7px; border-radius:4px; margin-bottom:3px; background:var(--inset); }
.wc-player-row.wc-winner { background:rgba(var(--gold-rgb), 0.08); border:1px solid rgba(var(--gold-rgb), 0.2); }
.wc-player-row.wc-me-row { border-left:3px solid var(--green); }
.wc-player-av { width:24px; height:24px; border-radius:50%; background:rgba(0,0,0,0.5); border:1px solid var(--border); display:flex; align-items:center; justify-content:center; font-size:var(--fs-2xs); color:var(--text-hi); flex-shrink:0; }
.wc-player-info { min-width:0; }
.wc-player-name { font-size:var(--fs-xs); color:var(--text-hi); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.wc-player-stack { font-size:var(--fs-3xs); color:var(--text); }
.wc-player-cards { display:flex; gap:var(--sp-0); justify-content:flex-end; }
.wc-player-cards .pk.placeholder { opacity:0.22; }
.wc-player-delta { font-size:var(--fs-sm); font-weight:700; text-align:right; white-space:nowrap; }
.wc-player-delta.pos { color:var(--pos); }
.wc-player-delta.neg { color:var(--neg); }
/* Bouton au style bouton QML (bible §5.5 + §12.3, action primaire verte) :
   degrade Top->Bottom, bord Edge, radius 9, bold ls 0.5, press scale 0.96. */
/* « Continuer » : mêmes couleurs que le bouton Connexion de la page de login
   (.btn-primary : ardoise #2b3444, texte blanc, radius 2, ombre douce). */
.winner-dismiss { background:#2b3444; color:#ffffff; border:none; padding:12px 24px; font-family: var(--ff-display); font-size:var(--fs-base); font-weight:700; border-radius:2px; cursor:pointer; letter-spacing:0.15em; box-shadow:0 4px 16px rgba(0,0,0,0.28); transition:background 0.15s, transform 0.1s, box-shadow 0.15s; display:block; width:100%; margin-top:4px; flex:0 0 auto; }
.winner-dismiss:hover { background:#384252; box-shadow:0 6px 24px rgba(0,0,0,0.4); }
.winner-dismiss:active { transform:scale(0.98); }


/* ── Player bar + my-zone : cachées hors jeu (contrôlées par body.in-game) ── */
body:not(.in-game) .player-bar,
body:not(.in-game) .my-zone {
  display: none !important;
}

/* ── Player bar (me) ── */
.player-bar {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  z-index: 30;
  display: flex;
  justify-content: center;
  padding: 3px 6px;
  padding-bottom: max(3px, env(safe-area-inset-bottom));
  background: var(--panel);
  border-top: 1px solid rgba(var(--green-rgb), 0.3);
}
.player-bar-left {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--sp-4);
  width: 100%;
  max-width: var(--bottom-w);
  background: rgba(0,0,0,0.35);
  border: 1px solid rgba(var(--green-rgb), 0.4);
  border-radius: 8px;
  padding: 5px 14px;
}
.pb-avatar {
  width: 36px; height: 36px; border-radius: 50%;
  background: rgba(0,0,0,0.5);
  border: 2px solid var(--green);
  box-shadow: 0 0 8px rgba(var(--green-rgb), 0.5);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--ff-display);
  font-size: var(--fs-md); color: var(--text-hi);
  flex-shrink: 0;
}
.pb-info { display: flex; flex-direction: column; gap: var(--sp-0); min-width: 80px; }
.pb-name { font-size: var(--fs-sm); color: var(--green); font-weight: 700; }
.pb-money { font-size: var(--fs-xs); color: var(--gold); }
.pb-cards {
  display: flex;
  flex-direction: row;
  gap: 5px;
  align-items: center;
}
/* Override pk size specifically in player bar */
.pb-cards .pk { width: 42px; height: 60px; font-size: var(--fs-md); font-weight: 800; }
.pb-action {
  font-size: var(--fs-xs); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--text);
  min-width: 50px;
}
.player-bar.pb-active .player-bar-left {
  border-color: var(--gold);
  box-shadow: 0 0 12px rgba(var(--gold-rgb), 0.4);
  animation: pulse-gold 1.5s infinite;
}

/* Action grid */
/* Largeur partagee du bas d'ecran : le dock (.player-bar-left) et les
   boutons d'action (.action-grid) gardent exactement la meme largeur.
   Un seul chiffre a changer pour bouger les deux. */
:root { --bottom-w: 560px; }
.action-grid {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  width: 100%;
  max-width: var(--bottom-w);
  margin: 0 auto;
  box-sizing: border-box; /* inclut padding+border : evite le debordement pleine largeur mobile */
}
/* ── Barre d'action : panneau centré borné (fidélité client officiel) ──
   Sur desktop/tablette, le fond épouse la largeur des boutons (radius,
   comme le QML officiel, bible §5) au lieu de s'étirer sur toute la largeur
   de l'écran. Actif aussi sur tout desktop/souris (hover+pointer:fine),
   fenêtre étroite comprise. Les téléphones tactiles (pointer:coarse) gardent
   la bande pleine largeur collée au bas. */
@media (min-width: 900px) and (min-height: 600px), (hover: hover) and (pointer: fine) {
  .my-zone { background: transparent; border-top: none; }
  .my-zone .action-grid {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 8px 10px;
    box-shadow: 0 6px 20px rgba(0,0,0,0.38);
  }
  /* Tour actif : le halo doré est porté par le panneau BORNÉ, pas par une
     bande dorée pleine largeur sur .my-zone. */
  .my-zone.my-turn-active { animation: none; border-top: none; }
  .my-zone.my-turn-active .action-grid {
    border-color: var(--gold);
    animation: myTurnPanelGlow 1.5s ease-in-out infinite;
  }
}
@keyframes myTurnPanelGlow {
  0%,100% { box-shadow: 0 6px 20px rgba(0,0,0,0.38), 0 0 0 1px var(--gold), 0 0 18px rgba(var(--gold-rgb), 0.35); }
  50%     { box-shadow: 0 6px 20px rgba(0,0,0,0.38), 0 0 0 1px #ffe066,     0 0 30px rgba(var(--gold-rgb), 0.6); }
}
/* ── Mon tour : halo doré NET sur le panneau d'action (TOUTES tailles) ──
   L'ancien halo fort (myTurnPanelGlow) etait verrouille derriere
   @media(min-width:900px) ; sur telephone/paysage court, le seul signal
   etait la pulsation de la bordure-haut de .my-zone sur 1px -> quasi
   invisible. Ici : cadre or pulse + petit "pop" a chaque nouveau tour,
   partout. .action-grid est recree a chaque tour -> le pop se rejoue seul.
   Fidelite QML §4.3 : « cadre or pulse (turn glow) quand c'est mon tour ». */
.my-zone.my-turn-active .action-grid {
  border-radius: var(--r-lg);
  animation: actBarTurnPop 0.55s cubic-bezier(.2,.9,.3,1.15) both,
             actBarTurnPulse 1.4s ease-in-out 0.5s infinite;
}
@keyframes actBarTurnPop {
  0%   { box-shadow: 0 0 0 0 rgba(var(--gold-rgb), 0);            transform: scale(1); }
  35%  { box-shadow: 0 0 0 2px var(--gold), 0 0 36px 8px rgba(var(--gold-rgb), 0.9);  transform: scale(1.03); }
  100% { box-shadow: 0 0 0 2px var(--gold), 0 0 18px 2px rgba(var(--gold-rgb), 0.45); transform: scale(1); }
}
@keyframes actBarTurnPulse {
  0%,100% { box-shadow: 0 0 0 2px var(--gold), 0 0 16px 2px rgba(var(--gold-rgb), 0.42); }
  50%     { box-shadow: 0 0 0 2px #ffe066,     0 0 32px 7px rgba(var(--gold-rgb), 0.74); }
}
/* Layout officiel : 3 zones (montant+slider / pastilles+All-In+mode / Fold-Call-Raise) */
/* Bas : Fold | Call/Check | Raise — trois boutons egaux */
.act-buttons-row { display: flex; gap: var(--sp-2); }
/* Echelle CONTINUE de la barre d'action avec la fenetre (--bar-k, posee par
   updateBottomLayout : 1 a partir de 1400x860, plancher 0.78) — la barre se
   redimensionne au fil du resize Safari/Chrome comme le client QML. Les
   blocs mobiles (portrait/paysage) surchargent avec leurs valeurs QML
   fixes et ne sont pas affectes. */
.act-buttons-row .btn-action { flex: 1; min-width: 0; padding: calc(10px * var(--bar-k, 1)) calc(12px * var(--bar-k, 1)); min-height: calc(54px * var(--bar-k, 1) - 10px); border-radius: var(--act-btn-radius, 9px); font-size: calc(0.9rem * var(--bar-kf, 1)); }
/* Haut : champ montant editable + slider */
.bet-row { display: flex; gap: var(--sp-3); align-items: center; }
.bet-row .raise-slider { flex: 1; }
.raise-amt-field {
  flex: 0 0 auto; width: 78px; /* largeur QML fixe, conservee aussi en paysage -> continuite */
  background: var(--field-bg);
  border: 2px solid var(--btn-raise-bg, #4CAF50);
  border-radius: 7px;
  color: var(--btn-raise-bg, #4CAF50);
  padding: calc(8px * var(--bar-k, 1)) calc(6px * var(--bar-k, 1)); font-family: var(--ff-mono); font-size: calc(0.81rem * var(--bar-kf, 1));
  font-weight: 700; text-align: center; outline: none;
}
.raise-amt-field:focus { border-color: var(--gold); }
.raise-amt-field:disabled { opacity: 0.3; cursor: not-allowed; filter: grayscale(0.6); }
/* ── Pave numerique de mise (ajout web) ────────────────────────────────────
   Sur pointeur grossier le champ passe en lecture seule : le clavier systeme
   n'apparait plus (il masquait la table et repoussait la mise en page sur
   iOS). Le pave prend la place des rangees du milieu et des actions — meme
   hauteur totale, donc AUCUN reflow de la table pendant la saisie. */
.bet-row .kp-open-btn {
  flex: 0 0 auto; width: 34px; align-self: stretch; height: auto; min-height: 0; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--field-bg); border: 1px solid var(--btn-raise-bg, #4CAF50);
  border-radius: 7px; color: var(--btn-raise-bg, #4CAF50); cursor: pointer;
}
.bet-row .kp-open-btn:hover { background: rgba(76, 175, 80, 0.18); }
.bet-row .kp-open-btn:disabled { opacity: 0.3; cursor: not-allowed; filter: grayscale(0.6); }
.action-grid.kp-open > .mid-row,
.action-grid.kp-open > .act-buttons-row { display: none; }
/* ── Pave FLOTTANT (pointeur fin / desktop) ────────────────────────────────
   La barre d'action reste entierement visible et en place : le pave s'ouvre
   en surimpression juste au-dessus du panneau, compact (touches 34 px), avec
   un court fondu. Clic en dehors = Annuler (gere en JS). Le mode tactile
   (.kp-open) garde le remplacement in-place, seul a eviter le clavier OS. */
.action-grid.kp-open-float { position: relative; }
.action-grid.kp-open-float > .bet-keypad.kp-float {
  position: absolute; left: 0; right: 0; bottom: calc(100% + 8px);
  z-index: 40;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);
  transform-origin: bottom center;
  animation: kpFloatIn 0.14s ease-out;
}
.action-grid.kp-open-float > .bet-keypad.kp-float .kp-grid button { height: 34px; min-height: 34px; }
.action-grid.kp-open-float > .bet-keypad.kp-float .kp-foot .btn-action { min-height: 34px; padding: 6px 10px; }
@keyframes kpFloatIn {
  from { opacity: 0; transform: translateY(6px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .action-grid.kp-open-float > .bet-keypad.kp-float { animation: none; }
}
/* Palette : celle de la barre d'action, a l'identique.
   · touches de saisie  = celle du champ de mise (.raise-amt-field) :
     fond --field-bg, bord et texte var(--btn-raise-bg, #4CAF50)
   · colonne raccourcis = celle des mises rapides (.btn-pct), codees EN DUR
     dans GameActionBar QML (#1b5e20 / #4CAF50 / hover #388e3c / press
     #2e7d32) et non themables — un tapis importe les affiche vertes aussi
   · bouton de validation = celui de Relancer (.btn-raise), donc
     var(--btn-raise-bg, var(--gold)) et police d'affichage
   Les deux valeurs de repli de --btn-raise-bg different volontairement :
   c'est la convention deja en place entre .raise-amt-field et .btn-raise. */
.bet-keypad {
  border: 1px solid var(--btn-raise-bg, #4CAF50); border-radius: 8px;
  background: var(--panel); padding: 7px;
}
.kp-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 7px; }
.kp-amt {
  font-family: var(--ff-mono); font-size: 1.3rem; font-weight: 700;
  color: var(--btn-raise-bg, #4CAF50);
}
.kp-amt.kp-bad { color: #e74c3c; }
.kp-range { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--text); }
.kp-grid { display: grid; grid-template-columns: repeat(3, 1fr) 1.25fr; gap: 5px; }
.kp-grid button {
  height: 42px; min-height: 42px; border-radius: var(--act-btn-radius, 7px); cursor: pointer;
  transition: filter 0.12s, transform 0.08s;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
}
/* All-In est exclu de l'habillage local : il porte .btn-allin et doit donc
   garder l'orange (ou l'image) que le theme lui donne dans la barre. */
.kp-grid button:not(.btn-allin) {
  background: var(--field-bg); border: 1px solid var(--btn-raise-bg, #4CAF50);
  color: var(--btn-raise-bg, #4CAF50);
  font-family: var(--ff-mono); font-size: 1rem; font-weight: 700;
}
.kp-grid button:hover  { filter: brightness(1.15); }
.kp-grid button:active { transform: scale(0.96); }
.kp-grid .kp-s:not(.btn-allin) {
  background: #1b5e20; border-color: #4CAF50; color: #fff;
  font-family: inherit; font-size: var(--fs-xs); font-weight: 700;
}
.kp-grid .kp-s:not(.btn-allin):hover  { background: #388e3c; filter: none; }
.kp-grid .kp-s:not(.btn-allin):active { background: #2e7d32; }
/* .btn-action impose min-width: 80px et un padding pense pour la rangee
   d'actions : a l'etroit dans une colonne de grille, on les neutralise sans
   toucher aux couleurs. */
.kp-grid .btn-allin {
  min-width: 0; padding: 0 4px; font-size: var(--fs-3xs, 0.62rem);
  letter-spacing: 0.04em; white-space: nowrap; overflow: hidden;
}
.kp-foot { display: flex; gap: 5px; margin-top: 6px; }
/* Le pied porte .btn-action / .btn-raise : forme, couleurs, images de boutons
   du theme, etat desactive et animations viennent donc des regles existantes
   de la barre. On ne pose ici que la repartition et le cas d'Annuler, qui n'a
   pas d'equivalent dans la barre. */
.kp-foot .btn-action { min-height: 40px; min-width: 0; padding: 8px 10px; }
/* Annuler prend l'habillage des touches, PAS celui de Se coucher : les themes
   a skin (_injectButtons) SUPPRIMENT --btn-fold-bg et ne reposent que les
   couleurs de texte, si bien qu'un contour var(--btn-fold-bg, #6b2020)
   retombait sur un rouge sombre par defaut — illisible sur un fond nuit comme
   Star Trek. Les touches, elles, ne dependent d'aucun token qu'un theme
   puisse retirer. */
.kp-foot .kp-cancel {
  flex: 1;
  background: var(--field-bg); border: 1px solid var(--btn-raise-bg, #4CAF50);
  color: var(--btn-raise-bg, #4CAF50); font-size: var(--fs-xs);
}
.kp-foot .kp-ok { flex: 1.7; }
.kp-foot .kp-ok b { font-family: var(--ff-mono); }
/* Themes a images de boutons : les touches prennent la forme du bouton
   Relancer (le pave appartient a la zone de mise) et Annuler celle du bouton
   Se coucher, faute de quoi la regle generique de data-btn-img les rendrait
   transparents et donc invisibles. Les raccourcis Min/1-2/Pot/All-In gardent
   le vert dur des mises rapides : comme .btn-pct, ils ne sont pas themables
   dans le client officiel. */
:root[data-btn-img] .kp-grid .kp-k {
  border: 9px solid transparent; background: transparent; padding: 0;
  border-image: var(--btn-raise-img) 9 fill / 9px / 0 stretch;
  color: var(--btn-raise-fg, #fff);
}
:root[data-btn-img] .kp-foot .kp-cancel {
  border: 9px solid transparent; background: transparent;
  border-image: var(--btn-raise-img) 9 fill / 9px / 0 stretch;
  color: var(--btn-raise-fg, #fff);
}
/* Slider au doigt : on AGRANDIT LA ZONE TACTILE (hauteur de la boite) sans
   toucher au rendu natif — la piste et la poignee restent celles du QML
   (poignee 18 px, accent-color), qu'un ::-webkit-slider-thumb custom aurait
   fait sauter. touch-action: none evite que le geste parte en scroll. Le pas
   utile vient de raiseStepFor (parite QML), pose sur l'attribut step. */
@media (pointer: coarse) {
  .raise-slider { height: 26px; touch-action: none; }
  .my-zone .bet-row .raise-slider { height: 26px; }
  .my-zone .bet-row .raise-amt-field { min-height: 34px; height: 34px; }
}
/* Milieu : 1/3 1/2 Pot | All-In | mode */
/* Parité GameActionBar.panelWidth (QML) : en paysage le panneau d'action est
   CENTRÉ, large de max(largeur visuelle des cartes communes, 380 px) — posé
   par renderSeats dans --abar-w. Portrait / hors placement officiel : la
   variable est absente -> pleine largeur (comportement historique). */
/* !important : les blocs média portrait/paysage plus bas posent max-width:100%
   et gagneraient la cascade ; --abar-w n'est posée QUE quand la règle QML
   s'applique (paysage officiel), sinon repli none = aucun effet. */
/* margins auto en !important : (1) #g-actions est un flex justify-content:
   flex-end -> sans auto-margins la grille clampée partait à droite ;
   (2) le bloc média fs+hide-pbar portrait pose margin:0. Les auto-margins
   ne décalent rien quand max-width vaut none (grille pleine largeur). */
.my-zone .action-grid { max-width: var(--abar-w, none) !important; margin-left: auto !important; margin-right: auto !important; }
/* ── Zone PORTRAIT (placement officiel, data-abar posé par renderSeats) :
   budget vertical de GameActionBar QML — raiseSection topPadding 4 /
   leftPadding-rightPadding 8 / bottomPadding 2, spacing 3 ; rangée de
   boutons : marges internes 5 (haut, via gap+2) et 6 (bas). L'enrobage web
   (my-zone 5px, panneau 8/10, gaps 6) coûtait ~25 px de tableZone → cartes
   plus petites que le client officiel à fenêtre égale. ── */
html[data-abar="portrait"] .my-zone { padding-top: 0 !important; padding-bottom: 0 !important; }
html[data-abar="portrait"] .my-zone .action-grid { padding: 4px 8px 2px !important; gap: 3px !important; }
html[data-abar="portrait"] .my-zone .act-buttons-row { margin-top: 2px; margin-bottom: 6px; gap: var(--sp-3); }
.mid-row { display: flex; gap: var(--sp-2); align-items: stretch; }
/* Parite QML GameActionBar : quick-bets a taille fixe, All-In colle juste
   apres Pot, seul le dropdown de mode est pousse a droite (margin-left auto).
   Avant, pct-row s'etirait (flex 1 1 auto) et poussait Tapis contre le
   dropdown dans les fenetres 740-900 px. */
.mid-row .pct-row { flex: 0 0 auto; width: auto; }
.mid-row .btn-allin { flex: 0 0 auto; min-width: 0; white-space: nowrap; }
.mode-sel-wrap { flex: 0 0 auto; }
.mid-row .mode-sel-wrap { flex: 0 1 calc(132px * var(--bar-kf, 1)); margin-left: auto; }
/* First two buttons side by side */
.action-top-row {
  display: flex; gap: var(--sp-2);
}
.action-top-row .btn-action:not(.btn-auto) { flex: 1; }

.pct-row {
  display: flex; gap: 5px;
}
.btn-pct {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  background: rgba(var(--gold-rgb), 0.1); border: 1px solid var(--gold-dim);
  color: var(--gold); padding: calc(7px * var(--bar-k, 1)) calc(4px * var(--bar-k, 1)); min-height: calc(6.5px + 31.5px * var(--bar-k, 1)); /* 38px a k=1, 24px pile a la frontiere paysage (k=0.556) */ border-radius: 7px; cursor: pointer;
  font-family: var(--ff-mono); text-align: center;
}
.btn-pct .pct-p { font-size: calc(0.74rem * var(--bar-kf, 1)); font-weight: 700; color: var(--gold); }
.btn-pct .pct-amt { font-size: calc(0.6rem * var(--bar-kf, 1)); font-weight: 400; color: var(--text); }
.btn-pct:hover { background: rgba(var(--gold-rgb), 0.22); }
/* Bets rapides : vert QML sur TOUTES les tables (GameActionBar 2.1.3 les code
   EN DUR — #1b5e20 / bord #4CAF50 / hover #388e3c / press #2e7d32 — ils ne
   sont PAS themables via StyleProvider : meme un tapis importe les affiche
   verts dans le client officiel). L'ancienne restriction aux tapis maison
   faisait diverger le web des tables importees (retour narmod 15/07). */
.btn-pct { background: #1b5e20; border-color: #4CAF50; color: #fff; }
.btn-pct .pct-p { color: #fff; }
.btn-pct:hover { background: #388e3c; }
.btn-pct:active { background: #2e7d32; }
.btn-pct:disabled,
.btn-action:disabled {
  opacity: 0.28;
  cursor: not-allowed;
  filter: grayscale(0.7);
  pointer-events: none;
}
.raise-input:disabled {
  opacity: 0.3;
  cursor: not-allowed;
  background: var(--field-bg) !important;
}
.raise-row { display: flex; gap: 5px; align-items: stretch; }
.raise-row .raise-input { flex: 1; }
.raise-row .btn-raise { flex: 2; }
/* Sélecteur de mode (remplace l'ancien bouton AUTO dans la rangée d'action) */
.mode-sel-wrap select { font-size: var(--fs-sm); padding: 6px 22px 6px 8px; height: 100%; }
/* Mode auto actif (Auto Check/Call ou Auto Check/Fold) : le dropdown est
   encadré d'or — même or de pré-sélection que le QML (#FFD700). Remplace
   l'ancien toast « Auto checked/called » (demande narmod 2026-07-18). */
.mode-sel-wrap.mode-auto select {
  border-color: #FFD700 !important;
  box-shadow: 0 0 0 1px #FFD700, 0 0 10px rgba(255, 215, 0, 0.45);
}
.mode-sel-wrap .sel-arr { right: 7px; font-size: var(--fs-xs); }

/* Action log */
.game-log {
  position: fixed;
  bottom: 160px; left: 8px;
  width: 190px; max-height: 130px;
  overflow-y: auto;
  background: rgba(0,0,0,0.6);
  border-radius: 4px; padding: 5px 7px;
  font-size: var(--fs-2xs); color: var(--text);
  pointer-events: none;
  z-index: 5;
  border: 1px solid var(--inset);
}
.log-line { padding: 1px 0; border-bottom: 1px solid var(--inset); }
.g-chat-input { display:flex; gap:var(--sp-2); padding:5px 10px; background:var(--panel); border-bottom:1px solid var(--border); }
.g-chat-input input { flex:1; background:var(--field-bg); border:1px solid var(--border); border-radius:2px; color:var(--cream); padding:4px 7px; font-family: var(--ff-mono); font-size:var(--fs-sm); outline:none; }
.g-chat-input input:focus { border-color:var(--gold); }

/* Badge timer sous le cercle avatar */
.seat-timer-badge {
  font-size: var(--fs-xs);
  font-family: var(--ff-mono);
  font-weight: 900;
  color: var(--timer-normal, #ffe234);
  letter-spacing: 0.05em;
  line-height: 1;
  min-width: 26px;
  text-align: center;
  /* Compteur de timeout AU-DESSUS de l'avatar (order:-1 dans la colonne flex
     .seat) plutôt que dessous. Petite marge basse = écart vers l'avatar. */
  order: -1;
  margin-top: 0;
  margin-bottom: 4px;
  padding: 1px 5px;
  background: var(--timer-bg, rgba(0,0,0,0.62));
  border-radius: var(--r-xs);
  border: 1px solid rgba(var(--gold-rgb), 0.35);
  text-shadow: var(--timer-glow, 0 1px 2px rgba(0,0,0,0.9));
  box-shadow: 0 1px 5px rgba(0,0,0,0.55);
}

/* ══ Slider de mise mobile (design A : conteneur + bulle de valeur) ══ */
.raise-row-mobile {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  background: rgba(0,0,0,0.3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 6px 8px;
}
.raise-slider {
  flex: 1;
  accent-color: var(--gold, rgb(var(--gold-rgb)));
  height: 4px;
  cursor: pointer;
  min-width: 0;
}
/* Slider vert QML (#4CAF50) sur les tapis maison PokerTH + Spectator, comme les
   bets rapides ; les tapis a theme importe gardent l'accent dore. */
html:not([data-table]) .raise-slider,
html[data-table=""] .raise-slider,
html[data-table="pokerth-live"] .raise-slider { accent-color: #4CAF50; }
.raise-display {
  font-family: var(--ff-mono);
  font-size: var(--fs-sm);
  color: var(--on-gold);
  font-weight: 700;
  min-width: 52px;
  text-align: center;
  flex-shrink: 0;
  background: var(--gold, rgb(var(--gold-rgb)));
  border-radius: 14px;
  padding: 3px 11px;
}
.raise-row-mobile .btn-raise { flex: 0 0 auto; padding: 10px 20px; }

/* Case « Ne plus afficher cette fenêtre » de l'overlay winner : discrète,
   centrée au-dessus du bouton Continuer ; pilote l'option avancée
   winner_popup (même clé que la case des Options avancées). */
.wc-hide-opt {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  margin: 6px 0 2px;
  font-size: var(--fs-xs); color: var(--gold-dim);
  cursor: pointer; user-select: none;
}
.wc-hide-opt input { accent-color: var(--gold, rgb(var(--gold-rgb))); cursor: pointer; }
.wc-hide-opt:hover { color: var(--gold); }

/* ══ Combinaison gagnante dans l'overlay winner ═════════════ */
.wc-best-hand {
  text-align: center;
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--gold, rgb(var(--gold-rgb)));
  background: rgba(var(--gold-rgb), 0.1);
  border: 1px solid rgba(var(--gold-rgb), 0.3);
  border-radius: var(--r-sm);
  padding: 8px 14px;
  margin: 8px 0 4px;
  letter-spacing: 0.04em;
}


/* ══ WAITING PANEL ══
   Rich waiting-room display shown between JoinGameAck and
   GameStartInitial. Lives inside the same #g-actions container that
   normally hosts the action buttons; replaces the previous plain
   .waiting-msg italic string. */
.waiting-panel {
  display: flex; flex-direction: column; align-items: center;
  gap: 7px; padding: 10px 14px; max-width: 480px; margin: 0 auto;
}
.wp-title {
  font-family: var(--ff-display); font-size: var(--fs-md); letter-spacing: 0.1em;
  color: var(--text-hi); text-transform: uppercase;
}
.wp-count {
  font-size: var(--fs-base); color: var(--cream);
}
.wp-count b { color: var(--gold); font-weight: 700; }
.wp-bar {
  display: flex; gap: var(--sp-2); margin: 2px 0;
}
.wp-dot {
  width: 10px; height: 10px; border-radius: 50%;
  border: 1px solid var(--gold-dim);
}
.wp-dot-on  { background: var(--gold); box-shadow: 0 0 6px rgba(var(--gold-rgb), 0.7); }
.wp-dot-off { background: transparent; }
.wp-ok, .wp-need {
  font-size: var(--fs-sm); font-weight: 600; letter-spacing: 0.04em;
  padding: 2px 0;
}
.wp-ok   { color: rgb(var(--green2-rgb)); }
.wp-need { color: var(--wp-need-fg); }
.wp-list {
  list-style: none; margin: 4px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 10px;
  width: 100%;
}
.wp-player {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  background: var(--inset);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  padding: 3px 10px 3px 4px;
  font-size: var(--fs-sm);
}
.wp-av {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--inset);
}
.wp-av.emoji-av { font-size: var(--fs-lg); line-height: 1; }
.wp-av-letter   { font-size: var(--fs-xs); font-weight: 700; color: var(--gold); }

/* ── Unified avatar-chip rules for compact UI lists ──
   The _avatarChipHtml helper produces <span class="<chipClass>
   has-pth-avatar"><img class="chip-pth-img" src="..."></span> when a
   PokerTH image is available. We need a single CSS rule that handles
   the image regardless of which chipClass (wp-av, eg-winner-av,
   wc-player-av) wraps it. Trick: target the image directly via its
   own class, and rely on the fact that <img> is a replaced element
   so border-radius alone clips its content -- no overflow:hidden on
   the parent needed (which would clip any sibling badge). */
.chip-pth-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  display: block;
  pointer-events: none;
}
/* Drop the background tint when the chip hosts an image; the image
   already fills the circle, the tint would only show through if the
   image were transparent (the PokerTH logo placeholder has a tiny
   transparent margin -- a darker tint reads better than white). */
.wp-av.has-pth-avatar,
.eg-winner-av.has-pth-avatar,
.wc-player-av.has-pth-avatar {
  background: rgba(0,0,0,0.2);
  padding: 0;
  overflow: visible;  /* badges (none currently here, but future-proof) */
}
.wp-name  { color: var(--cream); }
.wp-me    {
  font-size: var(--fs-2xs); color: var(--gold);
  text-transform: uppercase; letter-spacing: 0.1em;
  background: rgba(var(--gold-rgb), 0.12); padding: 1px 5px; border-radius: var(--r-xs);
}
.wp-hint {
  font-size: var(--fs-xs); color: var(--gold-dim);
  font-style: italic; margin-top: 4px; text-align: center;
}
.wp-hint-admin { color: rgb(var(--green2-rgb)); font-style: normal; font-weight: 600; }

/* ── Wait-page (parité GameWaitPage QML) : recouvre le feutre avant le début.
   Remplie par renderWaitingPanel() dans #g-wait-page, masquée à
   GameStartInitial. Options différenciées admin (case bots + Démarrer +
   Quitter) / joueur simple (Quitter seul). ── */
.wait-page {
  position: absolute; inset: 0; z-index: 40;
  display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-5); overflow-y: auto;
  padding: 20px 14px calc(20px + env(safe-area-inset-bottom, 0px));
  background: var(--field-bg);
}
.wait-page .wp-card {
  width: 100%; max-width: 440px;
  display: flex; flex-direction: column; align-items: stretch; gap: var(--sp-4);
}
.wait-page .wp-title {
  text-align: center; font-size: var(--fs-md); font-weight: 700;
  color: var(--text-hi); letter-spacing: 0.02em; margin: 0;
}
.wp-info {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 12px 14px;
  display: flex; flex-direction: column; gap: 3px;
}
.wp-info-name { font-weight: 700; color: var(--cream); font-size: var(--fs-md); margin-bottom: 3px; }
.wp-info-row { font-size: var(--fs-sm); color: var(--gold-dim); }
.wp-info-row b { color: var(--gold); font-weight: 700; }
.wp-players-hdr {
  font-size: var(--fs-xs); color: var(--gold-dim); text-transform: uppercase;
  letter-spacing: 0.12em; text-align: center; margin-top: 2px;
}
.wp-fillbots {
  display: flex; align-items: center; gap: 9px; cursor: pointer;
  font-size: var(--fs-base); color: var(--text); padding: 6px 2px; user-select: none;
}
.wp-fillbots input { width: 16px; height: 16px; accent-color: var(--green); cursor: pointer; flex: 0 0 auto; }
.wp-actions { display: flex; gap: var(--sp-3); margin-top: 4px; }
.wp-actions .wp-btn {
  flex: 1; padding: 12px 10px; border-radius: var(--r-md);
  font-weight: 700; letter-spacing: 0.4px; font-size: var(--fs-base);
  cursor: pointer; border: 1px solid var(--border);
  background: var(--panel-hi, var(--panel)); color: var(--text);
  transition: transform .09s, filter .12s;
}
/* Quitter la partie : tuile rouge comme « Annuler » de la création
   (.btn-cf-cancel) — piloté par la palette QML via --red / --red-rgb.
   Scopé sous .wp-actions pour battre la spécificité de .wp-actions .wp-btn. */
.wp-actions .wp-btn-leave { color: var(--red); background: rgba(var(--red-rgb), 0.10); border-color: rgba(var(--red-rgb), 0.5); }
.wp-actions .wp-btn-leave:not(:disabled):hover { background: rgba(var(--red-rgb), 0.20); }
/* Démarrer la partie : même couleur que « Créer une partie » du lobby
   (.lfb-create / .cf-create-btn) — navy neutre QML, texte blanc. */
.wp-actions .wp-btn-start {
  color: #ffffff; border-color: transparent;
  background: #2b3444;
}
.wp-actions .wp-btn-start:not(:disabled):hover { background: #384252; }
.wp-btn-start:disabled { opacity: 0.45; cursor: not-allowed; filter: grayscale(0.45); }
.wp-btn:not(:disabled):active { transform: scale(0.97); }
/* When the action-bar hosts the waiting panel (no real action buttons
   yet), override its default right-alignment so the panel sits in the
   middle of the bottom strip. Once the game starts, .waiting-panel is
   replaced by buttons and the default flex-end alignment kicks back in. */
.action-bar:has(.waiting-panel) {
  justify-content: center;
}
/* Fallback for browsers that don't support :has() — we also widen the
   panel itself so it occupies the full bar width and centres its inner
   contents via its own align-items:center. Modern browsers (Chrome 105+,
   Safari 15.4+, Firefox 121+) all support :has(), but this belt-and-
   braces approach makes the centring work even on older mobile Safari. */
.waiting-panel { width: 100%; }

/* Start-now banner — shown when the admin can launch with bots.
   Pulsing animation draws attention without being too noisy. */
.wp-ready-row {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  margin-top: 8px; padding: 10px 14px;
  background: linear-gradient(180deg, rgba(var(--green2-rgb), 0.10), rgba(var(--green2-rgb), 0.04));
  border: 1px solid rgba(var(--green2-rgb), 0.4);
  border-radius: var(--r-sm);
  animation: wpReadyAppear 0.4s ease-out;
}
@keyframes wpReadyAppear {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.wp-ready-label {
  font-size: var(--fs-sm); color: #88e4a3; font-weight: 600;
  letter-spacing: 0.04em;
}
.wp-ready-btn {
  padding: 9px 18px;
  background: linear-gradient(180deg, rgba(var(--green2-rgb), 0.35), rgba(var(--green2-rgb), 0.18));
  border: 1px solid rgb(var(--green2-rgb));
  border-radius: 4px;
  color: #d0ffd8;
  font-family: var(--ff-display);
  font-size: var(--fs-base);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  animation: wpReadyPulse 1.6s ease-in-out infinite;
  transition: transform 0.12s;
}
.wp-ready-btn:hover {
  background: linear-gradient(180deg, rgba(var(--green2-rgb), 0.55), rgba(var(--green2-rgb), 0.3));
  transform: scale(1.04);
}
.wp-ready-btn:active { transform: scale(0.97); }
@keyframes wpReadyPulse {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(var(--green2-rgb), 0.5),
                0 1px 4px rgba(0,0,0,0.4) inset;
  }
  50% {
    box-shadow: 0 0 0 8px rgba(var(--green2-rgb), 0),
                0 1px 4px rgba(0,0,0,0.4) inset;
  }
}

/* On mobile, keep things compact */
@media (max-width: 599.98px) {
  .waiting-panel { padding: 8px 10px; gap: 5px; }
  .wp-ready-row { padding: 8px 10px; gap: 5px; }
  .wp-ready-label { font-size: var(--fs-xs); }
  .wp-ready-btn { padding: 7px 14px; font-size: var(--fs-xs); }
  .wp-title { font-size: var(--fs-base); }
  .wp-player { font-size: var(--fs-xs); padding: 2px 8px 2px 3px; }
  .wp-av { width: 20px; height: 20px; }
}


/* ══ END-OF-GAME OVERLAY ══
   Full-screen panel shown when the tournament finishes (EndOfGame
   message). Style is heavier than the per-hand winner-overlay:
   bigger trophy, dramatic gold gradient, dedicated stats section. */
.endgame-overlay {
  position: fixed; inset: 0; z-index: 110;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.85);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  animation: endgameFadeIn 0.4s ease-out;
}
@keyframes endgameFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.endgame-card {
  background: var(--modal-bg);
  border: 1px solid var(--gold);
  border-top: 4px solid var(--gold);
  border-radius: var(--r-lg);
  padding: 22px 28px;
  max-width: 480px; width: calc(100% - 32px);
  box-shadow: 0 8px 40px rgba(0,0,0,0.8), 0 0 60px rgba(var(--gold-rgb), 0.15);
  display: flex; flex-direction: column; gap: 14px;
  animation: endgamePop 0.55s cubic-bezier(0.2,0.9,0.3,1.1);
}
@keyframes endgamePop {
  0%   { transform: scale(0.85) translateY(20px); opacity: 0; }
  100% { transform: scale(1)    translateY(0);    opacity: 1; }
}
.eg-trophy {
  text-align: center; font-size: 3rem; line-height: 1;
  filter: drop-shadow(0 4px 12px rgba(var(--gold-rgb), 0.4));
  animation: trophyBob 2.2s ease-in-out infinite;
}
@keyframes trophyBob {
  0%,100% { transform: translateY(0) rotate(-2deg); }
  50%     { transform: translateY(-4px) rotate(2deg); }
}
.eg-title {
  text-align: center;
  font-family: var(--ff-display);
  font-size: 1.25rem; letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-hi);
  text-shadow: 0 2px 8px rgba(var(--gold-rgb), 0.3);
}
.eg-winner {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-4);
  padding: 10px 14px;
  background: rgba(var(--gold-rgb), 0.08);
  border: 1px solid var(--gold-dim);
  border-radius: var(--r-sm);
}
.eg-winner-av {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--inset);
  font-size: var(--fs-2xl); line-height: 1;
  border: 2px solid var(--gold);
}
.eg-winner-av.letter { font-size: var(--fs-lg); font-weight: 700; color: var(--gold); }
.eg-winner-label { font-size: var(--fs-2xs); color: var(--gold-dim);
  text-transform: uppercase; letter-spacing: 0.12em; }
.eg-winner-name  { font-size: var(--fs-lg); color: var(--cream); font-weight: 600; }
.eg-winner.me {
  background: rgba(var(--green2-rgb), 0.12);
  border-color: rgba(var(--green2-rgb), 0.5);
}
.eg-winner.me .eg-winner-av { border-color: rgb(var(--green2-rgb)); }
.eg-stats-section {
  background: rgba(0,0,0,0.35);
  border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 12px 14px;
}
.eg-stats-title {
  font-size: var(--fs-2xs); color: var(--gold-dim);
  text-transform: uppercase; letter-spacing: 0.15em;
  margin-bottom: 8px;
}
.eg-stat-row {
  display: flex; justify-content: space-between; align-items: center;
  font-size: var(--fs-sm); padding: 3px 0;
}
.eg-stat-label { color: var(--text); }
.eg-stat-val   { color: var(--cream); font-weight: 600; font-family: var(--ff-mono); }
.eg-stat-val.pos { color: var(--pos); }
.eg-stat-val.neg { color: var(--neg); }
.eg-stat-divider {
  border: none; border-top: 1px solid var(--border);
  margin: 6px 0;
}
/* Encart session : fond noir 0.35 pensé pour le sombre → en thème clair il vire au gris
   moyen et écrase le contraste des libellés. On l'éclaircit en thème clair (palette QML). */
:root[data-theme="pokerth-light"] .eg-stats-section {
  background: rgba(0,0,0,0.05);
  border-color: rgba(0,0,0,0.10);
}
.eg-actions {
  display: flex; gap: var(--sp-4); margin-top: 4px;
}
.eg-btn {
  flex: 1; padding: 10px 14px;
  background: var(--inset);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--cream);
  font-family: var(--ff-display);
  font-size: var(--fs-sm);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.eg-btn:hover { background: var(--inset-hi); border-color: var(--gold-dim); }
.eg-btn.primary {
  background: linear-gradient(180deg, rgba(var(--gold-rgb), 0.25), rgba(var(--gold-rgb), 0.12));
  border-color: var(--gold);
  color: var(--gold-hi);
}
.eg-btn.primary:hover {
  background: linear-gradient(180deg, rgba(var(--gold-rgb), 0.4), rgba(var(--gold-rgb), 0.2));
}
@media (max-width: 599.98px) {
  .endgame-card { padding: 16px 18px; gap: var(--sp-4); }
  .eg-trophy { font-size: 2.4rem; }
  .eg-title { font-size: var(--fs-md); letter-spacing: 0.14em; }
  .eg-winner-av { width: 32px; height: 32px; font-size: var(--fs-xl); }
  .eg-winner-name { font-size: var(--fs-md); }
  .eg-stat-row { font-size: var(--fs-xs); }
}


/* ══ AUTO CHECK/FOLD CHECKBOX ══
   Rendered above the action grid by renderMyTurnActions() so the user
   can arm the auto-action for their NEXT turn within the same hand.
   Deliberately discreet — small, low-contrast — because it represents
   a fully optional courtesy feature, not a primary action button. */
.auto-cf-row {
  display: flex; align-items: center; gap: 9px;
  background: rgba(var(--gold-rgb), 0.06);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 7px 11px; margin: 0 0 7px;
  cursor: pointer; user-select: none;
  flex-basis: 100%;
  transition: background 0.15s, border-color 0.15s;
}
.auto-cf-row:hover { background: rgba(var(--gold-rgb), 0.1); border-color: var(--gold-dim); }
.auto-cf-label { font-size: var(--fs-xs); color: var(--gold-dim); font-weight: 500; font-family: var(--ff-mono); }
.auto-cf-row:has(input:checked) .auto-cf-label { color: var(--cream); font-weight: 700; }
.auto-cf-hint { margin-left: auto; font-size: var(--fs-3xs); color: var(--gold-dim); font-style: italic; }
/* iOS-style toggle switch */
.auto-switch { position: relative; display: inline-block; width: 38px; height: 21px; flex-shrink: 0; }
.auto-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.auto-switch-track { position: absolute; inset: 0; background: rgba(255,255,255,0.12);
  border: 1px solid var(--gold-dim); border-radius: 21px; transition: 0.2s; }
.auto-switch-track::before { content: ""; position: absolute; height: 15px; width: 15px; left: 2px; top: 2px;
  background: var(--cream); border-radius: 50%; transition: 0.2s; }
.auto-switch input:checked + .auto-switch-track { background: rgba(var(--green-rgb), 0.5); border-color: var(--green); }
.auto-switch input:checked + .auto-switch-track::before { transform: translateX(17px); background: var(--on-green); }
@media (max-width: 599.98px) {
  .auto-cf-row { padding: 6px 9px; margin-bottom: 5px; gap: var(--sp-3); }
  .auto-cf-label { font-size: var(--fs-xs); }
  .auto-cf-hint { font-size: var(--fs-3xs); }
}


/* ══ SPECTATOR SEND-LOCK ══
   Marker set by updateSpectatorStrip (sp0ck 31/07/2026, supersedes the 24/07
   full chat block): spectators SEE the game chat and the emoji reactions but
   must not SEND either. Hidden: the chat input row (text field + send + emoji
   insert), the emoji-insert panel, the 😊 reactions button and the reactions
   panel. The chat panel itself, its 💬 button and the incoming reaction
   animations stay fully visible. */
body.spectator-nosend #g-chat-panel .chat-input,
body.spectator-nosend #g-chat-emoji-panel,
body.spectator-nosend #react-toggle-btn,
body.spectator-nosend #g-reaction-panel { display: none !important; }

/* ══ SPECTATOR BANNER ══
   Shown at the top of the game screen (just below the header,
   above the pot strip) when the user is watching as a spectator.
   Subtle blue tint to differentiate from regular player view, with
   a small animated pulse on the eye emoji to draw attention without
   being noisy. */
/* Statut spectateur intégré au bandeau du pot #pot-strip (parité GameStatusBar
   QML §7) : pastille or « Spectating » (visible si je suis spectateur) + œil
   avec compteur (visible dès qu'au moins un spectateur regarde). g-pot (flex:1)
   pousse ces éléments à droite, juste avant la phase (#g-round). */
#g-spec-badge {
  display: none;               /* activé par JS via .on quand _amSpectator */
  align-items: center;
  /* Modele generique --comm-scale (comme .blinds-next / .conn-pill /
     .win-hand-badge) : le badge est COLLE au badge des blindes dans .gsb-mid,
     il doit donc grandir avec lui. Valeurs strictement identiques a
     .blinds-next, sinon les deux pastilles voisines divergent des que
     --comm-scale quitte 1 (desktop = 1.35). */
  height: clamp(18px, calc(19px * var(--comm-scale, 1)), 28px);
  box-sizing: border-box;
  padding: 0 clamp(8px, calc(9px * var(--comm-scale, 1)), 14px);
  margin-right: 8px;
  border-radius: var(--r-pill);
  border: 1px solid var(--sel-bar);
  background: rgba(var(--sel-bar-rgb), 0.22);
  color: var(--sel-bar);
  font-size: clamp(10px, calc(11px * var(--comm-scale, 1)), 16px);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  flex-shrink: 0;
}
#g-spec-badge.on { display: inline-flex; }
#g-spec-count {
  display: none;               /* activé par JS via .on quand count > 0 */
  align-items: center;
  gap: 3px;
  margin-right: 8px;
  color: #eff1f5;
  font-size: clamp(10px, calc(11px * var(--comm-scale, 1)), 16px);
  font-weight: 700;
  flex-shrink: 0;
  cursor: default;
}
#g-spec-count.on { display: inline-flex; }
#g-spec-count .sc-eye {
  font-size: 0.92em;
  opacity: 0.9;
  animation: sbEyePulse 2.4s ease-in-out infinite;
}
@keyframes sbEyePulse {
  0%,100% { opacity: 0.7; transform: scale(1); }
  50%     { opacity: 1;   transform: scale(1.12); }
}
@media (max-width: 599.98px) {
  /* Mobile : la pastille texte « Spectating » est redondante avec l'oeil
     juste a cote et mange ~100px du bandeau -> masquee. Quand JE regarde
     (.me pose par updateSpectatorStrip), l'OEIL herite du contour jaune de
     la pastille : l'info reste, en ~30px. */
  #g-spec-badge.on { display: none; }
  #g-spec-count { margin-right: 6px; }
  #g-spec-count.me {
    height: 18px; box-sizing: border-box; padding: 0 7px;
    border-radius: var(--r-pill);
    border: 1px solid var(--sel-bar);
    background: rgba(var(--sel-bar-rgb), 0.22);
    color: var(--sel-bar);
  }
}

/* Replacement message that occupies the action area when the user
   is a spectator. Same vertical footprint as the action buttons so
   the layout doesn't jump around between hands. */



/* ══ QUIT-GAME CONFIRMATION DIALOG ══
   Centred modal with 2 buttons: Quit / Cancel. Shown when the user
   clicks the ✕ from inside a game. Gold-bordered card matching the
   rest of the UI, with backdrop blur and a soft pop animation. */
/* Shared modal container for the three confirmation dialogs:
   #leave-dialog (quit a game in progress)
   #disconnect-dialog (disconnect from the lobby)
   #quick-create-dialog (set player count on a quick-create) — this one
   already has its own rule because it was added in a separate commit
   with the same body; consolidating could be a future cleanup. */
#leave-dialog,
#live-spectate-dialog,
#disconnect-dialog {
  position: fixed; inset: 0; z-index: 120;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.7);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  animation: ldFade 0.25s ease-out;
}
@keyframes ldFade { from { opacity: 0; } to { opacity: 1; } }
.ld-card {
  background: var(--modal-bg);
  border: 1px solid var(--border-hi);
  border-radius: 14px;
  padding: 22px 24px;
  max-width: 400px; width: calc(100% - 32px);
  box-shadow: 0 8px 32px rgba(0,0,0,0.7);
  animation: ldPop 0.35s cubic-bezier(0.2,0.9,0.3,1.1);
}
@keyframes ldPop {
  from { transform: scale(0.9) translateY(8px); opacity: 0; }
  to   { transform: scale(1)   translateY(0);   opacity: 1; }
}
.ld-title {
  font-family: var(--ff-display);
  font-size: 1.15rem; font-weight: 700; letter-spacing: 0.01em;
  color: var(--text-hi); text-align: left; margin-bottom: 10px;
}
.ld-body {
  font-size: var(--fs-md); color: var(--cream);
  text-align: left; margin-bottom: 18px; line-height: 1.45;
}
/* row-reverse : Annuler (2e enfant) a gauche, action (1er enfant) a droite,
   comme le dialogue officiel — sans toucher au HTML. */
.ld-buttons {
  display: flex; flex-direction: row-reverse; gap: var(--sp-4);
}
.ld-btn {
  flex: 1 1 0; min-width: 0;
  padding: 11px 16px;
  background: var(--inset);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--cream);
  font-family: var(--ff-display);
  font-size: var(--fs-sm);
  letter-spacing: 0.1em; text-transform: uppercase;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.1s;
}
.ld-btn:hover  { background: var(--inset-hi); }
.ld-btn:active { transform: scale(0.98); }
.ld-btn.ld-quit {
  background: transparent;
  border: 1px solid var(--border-hi);
  color: var(--cream);
}
.ld-btn.ld-quit:hover  { background: var(--inset); }
.ld-btn.ld-quit:active { transform: scale(0.98); }
.ld-btn.ld-cancel {
  background: transparent;
  border: 1px solid var(--border-hi);
  color: var(--cream);
}
.ld-btn.ld-cancel:hover  { background: var(--inset); }
.ld-btn.ld-cancel:active { transform: scale(0.98); }
@media (max-width: 599.98px) {
  .ld-card { padding: 16px 18px; }
  .ld-title { font-size: var(--fs-md); }
  .ld-body { font-size: var(--fs-sm); }
  .ld-btn { padding: 9px 12px; font-size: var(--fs-xs); }
}


/* ══ QUICK-CREATE DIALOG ══
   Reuses the .ld-* styles from the leave-dialog (gold card, backdrop,
   buttons). Adds a centred input row and a green 'Create' button to
   match the semantic 'positive action' colour used elsewhere. */
#quick-create-dialog {
  position: fixed; inset: 0; z-index: 120;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.7);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  animation: ldFade 0.25s ease-out;
}
.qc-control {
  display: flex; align-items: center; justify-content: center;
  gap: var(--sp-5); margin: 0 0 18px;
}
.qc-control label {
  font-family: var(--ff-display);
  font-size: var(--fs-sm); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--gold-dim);
}
.qc-control input[type="number"] {
  width: 70px;
  padding: 8px 10px;
  background: var(--field-bg);
  border: 1px solid var(--gold-dim);
  border-radius: 4px;
  color: var(--text-hi);
  font-family: var(--ff-mono);
  font-size: var(--fs-lg);
  text-align: center;
  outline: none;
  transition: border-color 0.15s;
}
.qc-control input[type="number"]:focus { border-color: var(--gold); }
.ld-btn.ld-create {
  background: var(--qml-pos-grad);
  border-color: var(--qml-pos-edge);
  color: var(--qml-pos-fg); font-weight: 600;
}
.ld-btn.ld-create:hover { background: var(--qml-pos-grad-hi); }
@media (max-width: 599.98px) {
  .qc-control { gap: var(--sp-3); }
  .qc-control input[type="number"] { font-size: var(--fs-md); width: 60px; padding: 6px 8px; }
}


/* ══ PLAYERS ONLINE DROPDOWN PANEL ══
   Triggered by clicking the #h-players pill in the lobby header.
   Same visual chrome as #g-chat-panel / #g-log-panel so the three
   dropdowns feel like siblings. Adds a search input row above the
   scrollable list for quick filtering when many players are online. */
#players-panel {
  position: fixed !important;
  top: var(--ovl-top, 48px);
  left: 0; right: 0;
  z-index: 100;
  max-height: min(360px, 50dvh) !important;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 4px 20px rgba(0,0,0,0.6);
  display: flex;
  flex-direction: column;
}
.players-search {
  padding: 6px 10px;
  background: var(--chrome-tint);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.players-search input {
  width: 100%;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  color: var(--cream);
  padding: 5px 10px;
  font-family: var(--ff-mono);
  font-size: var(--fs-sm);
  outline: none;
  box-sizing: border-box;
}
.players-search input:focus { border-color: var(--gold); }
/* Palette claire : champ de recherche gris plein arrondi, sans bordure,
   placeholder col300 (parite QML Universal light). */
:root[data-theme="pokerth-light"] .players-search input {
  background:#b9bbbf; border-color:transparent; border-radius:var(--r-sm); color:#1d222b;
}
:root[data-theme="pokerth-light"] .players-search input::placeholder { color:#687285; }
:root[data-theme="pokerth-light"] .players-search input:focus { border-color:#9a9ca1; }
/* Body container — the inline style on the element sets flex/min-height/
   overflow/padding/background to match the chat panel pattern exactly.
   This rule keeps only the bits inline styles don't cover. */
.players-list {
  font-family: var(--ff-mono);
  font-size: var(--fs-sm);
}
.players-list .pl-row {
  display: grid;
  grid-template-columns: var(--pl-cols, 22px minmax(0,1fr) 16px 22px 48px 48px);
  align-items: center;
  gap: var(--sp-3);
  padding: 4px 0;          /* container provides the 12px lateral padding now */
  font-family: var(--ff-mono);
  font-size: var(--fs-sm);
  color: var(--cream);
  border-bottom: 1px solid var(--inset);
}
.players-list .pl-row:last-child { border-bottom: none; }
.players-list .pl-row:hover { background: rgba(var(--gold-rgb), 0.08); }
.players-list .pl-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Liste des joueurs : mêmes marques, du même côté qu'au siège — après le
   pseudo, et HORS du lien, pour que l'ellipse ne mange que le nom et jamais
   les badges. Collées au pseudo plutôt que repoussées au bord de la colonne :
   à cette largeur elles s'éloigneraient du nom qu'elles qualifient. */
.players-list .pl-name.has-nv { display: flex; align-items: center; }
.players-list .pl-name.has-nv .pl-name-link {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.players-list .pl-name.has-nv .seat-nv { flex: 0 0 auto; margin-left: 5px; }
.players-list .pl-name.has-nv .seat-note-tag { margin-right: 0; }
.players-list .pl-name.has-nv .seat-note-tag + .seat-note-stars { margin-left: 3px; }
.players-list .pl-name.has-nv .seat-note-stars { margin-right: 0; }
.players-list .pl-name-link { color: inherit; text-decoration: none; cursor: pointer; }
.players-list .pl-name-link:hover, .players-list .pl-name-link:active { color: var(--gold-hi); }
.players-list .pl-flag { flex: none; display: inline-flex; align-items: center; justify-content: flex-start; gap: var(--sp-1); font-size: var(--fs-md); line-height: 1; }
.players-list .pl-cc { font-size: var(--fs-2xs); color: var(--gold-dim); font-family: var(--ff-mono); letter-spacing: 0.04em; white-space: nowrap; }
.players-list .pl-flag .cc-flag { width: 20px; height: 15px; }
/* Colonne ★ : mon étoile sur MA ligne, ma note en étoiles sur celle des
   autres — les deux ne se rencontrent jamais (une note n'est pas rendue sur ma
   propre ligne), d'où une seule colonne pour les deux. */
.players-list .pl-star { flex: none; text-align: center; font-size: var(--fs-md); line-height: 1; color: var(--star); white-space: nowrap; }
.players-list .pl-star .seat-note-stars { margin: 0; font-size: var(--fs-xs); }
.players-list .pl-id   { flex: none; min-width: 4.2em; text-align: right; color: var(--gold-dim); font-size: var(--fs-xs); }
.players-list .pl-row.pl-me { color: var(--gold); }
/* Ma ligne : pseudo en gras. La couleur seule ne suffisait pas à la repérer
   d'un coup d'œil dans une liste de quarante lignes — et dans le thème
   pokerth, --gold est justement la couleur du texte courant. */
.players-list .pl-row.pl-me .pl-name { font-weight: 700; }
/* ── Avatar chip in the players-online list ──
   Same priority order as everywhere else (real PokerTH image >
   placeholder logo > emoji > 🤖 > initial letter). The chip-pth-img
   rule (defined globally for compact lists) handles image clipping. */
.pl-av {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--inset);
  font-size: var(--fs-lg);
  line-height: 1;
}
.pl-av.letter { font-size: var(--fs-xs); font-weight: 700; color: var(--gold); }
.pl-av.has-pth-avatar {
  background: rgba(0,0,0,0.2);
  padding: 0;
  overflow: visible;
}
.players-list .pl-empty {
  padding: var(--sp-5);
  text-align: center;
  font-style: italic;
  color: var(--gold-dim);
  font-size: var(--fs-xs);
}
/* Custom scrollbar matching chat/log */
.players-list::-webkit-scrollbar { width: 6px; }
.players-list::-webkit-scrollbar-track { background: transparent; }
.players-list::-webkit-scrollbar-thumb {
  background: rgba(var(--gold-rgb), 0.18);
  border-radius: var(--r-xs);
}
.players-list::-webkit-scrollbar-thumb:hover { background: rgba(var(--gold-rgb), 0.32); }
/* ═══════════════════════════════════════════════════════════════════
   Inline SVG flag (used by the language-toggle button).
   We size against the inherited font-size so the flag scales with
   the button text — same look on mobile and desktop. The aspect
   ratio (2:1) matches the viewBox in i18n.mjs.
   ═══════════════════════════════════════════════════════════════════ */
.lang-flag {
  width: 1.5em;
  height: 0.75em;
  vertical-align: middle;
  display: inline-block;
  border-radius: 1.5px;
  box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.4);
  pointer-events: none;
}

/* Overflow-menu language entries (lobby + game headers): the flag SVG is
   followed directly by the language label, separated only by a literal
   space that collapses against the inline-block flag. Give the flag a
   right margin so flag and label don't touch. Scoped to these two
   buttons only — the icon-only header buttons and the dropdown picker
   (which already uses flex `gap`) are untouched. */
#lang-toggle-game-mob .lang-flag,
#lang-toggle-lobby-mob .lang-flag {
  margin-right: 8px;
}

/* ═══════════════════════════════════════════════════════════════════
   Connect-screen top-right button bar.
   The lang/refresh/fullscreen trio sits outside any `.header` block,
   so the global `.header .btn-sm` uniformization rule doesn't reach
   them. Apply the same dimensions here so the three (or four) buttons
   line up neatly.
   ═══════════════════════════════════════════════════════════════════ */
#s-connect > div:first-child .btn-icon,
#s-connect .corner-links .btn-icon {
  height: 30px;
  min-width: 30px;
  padding: 0 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  font-size: var(--fs-sm);
  line-height: 1;
  color: var(--text-hi);
  /* Frameless : icône nue sur le bandeau (plus de chip). */
  background: transparent;
  border: 1px solid transparent;
  box-shadow: none;
  transition: background 0.2s, border-color 0.2s, color 0.2s;
}
#s-connect > div:first-child .btn-icon:hover,
#s-connect .corner-links .btn-icon:hover {
  background: rgba(255,255,255,0.07);
  border-color: transparent;
}
#s-connect > div:first-child .btn-icon,
#s-connect .corner-links .btn-icon {
  width: 30px;
  padding: 0;
  background: transparent;
}

#s-connect .corner-links .btn-icon{ text-decoration:none; }

/* ═══ Bandeau header de la page de connexion ═══════════════════════════
   Barre unique en haut, même chrome que le header lobby/jeu (.header :
   fond --panel + bord bas --border). position:absolute pour ne pas
   décentrer la carte ; overflow:visible + ancrage positionné pour que
   #connect-overflow-menu tombe sous la barre (même principe que
   #s-lobby .header). ☰ liens + 🏆 à gauche · install + ••• à droite.
   Le style/taille fin des boutons est traité à l'étape suivante. */
#s-connect .connect-header{
  position:absolute; top:0; left:0; right:0; z-index:20;
  overflow:visible;
  justify-content:space-between;
}
#s-connect .connect-header .hdr-group{ display:flex; align-items:center; gap:var(--sp-2); }
/* Ancrage des menus overflow sur LEUR bouton (et non sur toute la barre)
   pour éviter le décalage horizontal du padding — comme le menu ☰.
   Connexion utilise le même wrapper .hdr-ovf-wrap que lobby/jeu. */
.hdr-ovf-wrap{ position:relative; display:inline-flex; align-items:center; }
/* Icônes pleines : plus d'encadré (ni fond, ni bord, ni ombre) — l'icône
   seule sur la barre. */
#s-connect .connect-header .btn-icon{
  background:transparent;
  border-color:transparent;
  box-shadow:none;
  backdrop-filter:none; -webkit-backdrop-filter:none;
}
#s-connect .connect-header .btn-icon:hover{
  background:rgba(255,255,255,0.07);
  border-color:transparent;
}
/* Couleurs des icônes : neutres au repos (le trophée aussi — narmod 23/07,
   il ne passe en or doré que fenêtre ouverte, cf. .win-open), install vert.
   Les SVG utilisent currentColor -> on colore le bouton. */
#s-connect .connect-header #cl-links-connect > summary{ color:var(--text-hi); }
#s-connect .connect-header #ranking-btn-connect{ color:var(--text-hi); }
#s-connect .connect-header #install-btn{ color:#3ba55d; }
#s-connect .connect-header #connect-overflow-btn{ color:var(--text-hi); }

/* ═══ Headers lobby + jeu : même traitement que connexion ═══════════════
   Icônes pleines (sans encadré) + couleurs. Frameless scopé aux .btn-icon
   pour ne pas toucher aux boutons-texte admin (Start/Close/Kick). */
#s-lobby .header .btn-icon,
#s-game .header .btn-icon,
#s-create .cp-header .btn-icon,
#privacy-page .cp-header .btn-icon{
  background:transparent;
  border-color:transparent;
  box-shadow:none;
}
#s-lobby .header .btn-icon:hover,
#s-game .header .btn-icon:hover,
#s-create .cp-header .btn-icon:hover,
#privacy-page .cp-header .btn-icon:hover{
  background:rgba(255,255,255,0.07);
  border-color:transparent;
}
/* Aucun contour au focus/clic non plus (comme le client officiel) : pas de
   ring navigateur, pas de bord, pas d'ombre — repos, hover, focus, actif. */
#s-connect .connect-header .btn-icon,
#s-connect .connect-header .btn-icon:focus,
#s-connect .connect-header .btn-icon:focus-visible,
#s-connect .connect-header .btn-icon:active,
#s-connect .connect-header #cl-links-connect > summary:focus,
#s-connect .connect-header #cl-links-connect > summary:focus-visible,
#s-lobby .header .btn-icon,
#s-game .header .btn-icon,
#s-create .cp-header .btn-icon,
#s-lobby .header .btn-icon:focus,
#s-game .header .btn-icon:focus,
#s-create .cp-header .btn-icon:focus,
#s-lobby .header .btn-icon:focus-visible,
#s-game .header .btn-icon:focus-visible,
#s-create .cp-header .btn-icon:focus-visible,
#s-lobby .header .btn-icon:active,
#s-game .header .btn-icon:active,
#s-create .cp-header .btn-icon:active,
#privacy-page .cp-header .btn-icon,
#privacy-page .cp-header .btn-icon:focus,
#privacy-page .cp-header .btn-icon:focus-visible,
#privacy-page .cp-header .btn-icon:active{
  outline:none;
  border-color:transparent;
  box-shadow:none;
}
#s-lobby .header #ranking-btn-lobby{ color:var(--text-hi); }
/* Page « Créer une partie » : flèche retour + roue crantée neutres. */
#s-create .cp-header #cr-overflow-btn{ color:var(--text-hi); }
#privacy-page .cp-header #pv-overflow-btn{ color:var(--text-hi); }
#s-create .cp-header #ranking-btn-create{ color:var(--text-hi); }
#s-lobby .header #lobby-chat-btn{ color:var(--text-hi); }
#s-lobby .header #l-overflow-btn{ color:var(--text-hi); }
#s-game .header #sound-toggle-btn{ color:var(--text-hi); }
#s-game .header #ranking-btn-game{ color:var(--text-hi); }
#s-game .header #g-overflow-btn{ color:var(--text-hi); }

/* Mode clair : les icônes acier #c3ccda deviennent quasi invisibles sur la
   barre claire -> on les fonce (près du noir) pour un contraste marqué. */
:root[data-theme="pokerth-light"] #s-connect .connect-header #cl-links-connect > summary,
:root[data-theme="pokerth-light"] #s-connect .connect-header #connect-overflow-btn,
:root[data-theme="pokerth-light"] #s-lobby .header #lobby-chat-btn,
:root[data-theme="pokerth-light"] #s-lobby .header #l-overflow-btn,
:root[data-theme="pokerth-light"] #s-game .header #sound-toggle-btn,
:root[data-theme="pokerth-light"] #s-game .header #g-overflow-btn,
:root[data-theme="pokerth-light"] #s-connect .connect-header #ranking-btn-connect,
:root[data-theme="pokerth-light"] #s-lobby .header #ranking-btn-lobby,
:root[data-theme="pokerth-light"] #s-create .cp-header #ranking-btn-create,
:root[data-theme="pokerth-light"] #s-game .header #ranking-btn-game{
  color:#1d222b;
}
/* ... sauf quand leur fenêtre est ouverte : l'or doré reprend la main. */
:root[data-theme="pokerth-light"] .btn-icon.win-open{ color:var(--sel, #9a7d0a) !important; }
#s-connect .corner-links .btn-icon img{ display:block; width:18px; height:18px; }

/* ═══════════════════════════════════════════════════════════════════
   Credits footer (connect screen only).
   Sits at the bottom of #s-connect without disturbing the centered
   login card. Hidden on lobby/game screens to avoid eating playable
   space. Mentions only the repo for this fork — no upstream PokerTH
   link (to stay respectfully unaffiliated).
   ═══════════════════════════════════════════════════════════════════ */
.connect-footer {
  position: absolute;
  bottom: 10px;
  left: 0;
  right: 0;
  text-align: center;
  color: var(--text-hi);
  font-family: var(--ff-mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.04em;
  line-height: 1.5;
  opacity: 0.95;
  text-shadow: 0 1px 4px rgba(0,0,0,0.95), 0 0 2px rgba(0,0,0,0.8);
  pointer-events: auto;
  z-index: 1;
}
/* Icône Discord au-dessus de la ligne de crédits (pied de page classique).
   Pastille ronde blurple officielle + logo blanc, comme les icônes sociales
   d'un footer de site. Sélecteur .connect-footer .cf-discord pour battre la
   règle générique .connect-footer a (couleur or + soulignement pointillé). */
/* Rangée d'icônes sociales (Discord · GitHub · pokerth.net) : pastilles
   translucides et discrètes au repos, pleine couleur de marque au survol. */
.connect-footer .connect-social {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--sp-4);
  margin-bottom: 6px;
}
.connect-footer .cf-social {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border-bottom: none;
  background: rgba(20,24,34,0.55);      /* pastille sombre : ressort sur le fond clair chargé */
  border: 1px solid rgba(255,255,255,0.22);
  box-shadow: 0 1px 4px rgba(0,0,0,0.4);
  opacity: 1;
  transition: background 0.2s, transform 0.2s, opacity 0.2s, box-shadow 0.2s;
}
.connect-footer .cf-social svg,
.connect-footer .cf-social img { opacity: 1; filter: drop-shadow(0 1px 1px rgba(0,0,0,0.5)); }
.connect-footer .cf-social:hover {
  opacity: 1;
  transform: scale(1.08);
  box-shadow: 0 2px 8px rgba(0,0,0,0.5);
}
/* Couleurs de marque révélées au survol uniquement. */
.connect-footer .cf-discord:hover { background: #5865F2; }   /* blurple Discord */
.connect-footer .cf-github:hover  { background: #24292e; }   /* gris GitHub */
.connect-footer .cf-web:hover     { background: rgba(40,46,60,0.75); }  /* logo coloré : pas de teinte or */

.connect-footer-line {
  margin: 1px 0;
}
.connect-footer a {
  color: var(--gold-hi);
  text-decoration: none;
  text-shadow: 0 1px 4px rgba(0,0,0,0.95);
  border-bottom: 1px dotted rgba(var(--gold-rgb), 0.5);
  transition: color 0.2s, border-color 0.2s;
}
.connect-footer a:hover {
  color: var(--gold);
  border-bottom-color: var(--gold);
}
/* pokerth-light: footer overlays the dark flames, so the theme's dark
   tokens would vanish -> warm-light values that harmonise with the fire. */
:root[data-theme="pokerth-light"] .connect-footer { color: #ece8dc; }
:root[data-theme="pokerth-light"] .connect-footer a {
  color: #f0d68a;
  border-bottom-color: rgba(240,214,138,0.5);
}
:root[data-theme="pokerth-light"] .connect-footer a:hover {
  color: #ffe9ad;
  border-bottom-color: #ffe9ad;
}
/* On very short screens (e.g. landscape mobile) hide the footer so
   the login card has more room. */
@media (max-height: 599.98px) {
  .connect-footer { display: none; }
}

/* Là où le cadre de login chevauche le footer, le cadre passe AU-DESSUS
   (z-index) et recouvre les raccourcis/crédits. Aucune modification de taille
   du cadre ni du footer — simple ordre d'empilement.
   Était limité à @media (max-width:640px) : sur desktop/tablette le footer
   repassait devant le bas du cadre (constaté narmod 22/07). Règle désormais
   valable sur toutes les tailles. */
#s-connect > .card { position: relative; z-index: 2; }



/* ── Kick player modal (opened by admin from header) ──
   Lists table players with an inline trash button to kick. Same
   visual language as #player-info-modal and #game-info-modal. */
#kick-modal,
#kick-confirm-modal,
#report-confirm-modal,
#invite-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-6);
}
.km-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,0.65);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.km-card {
  position: relative;
  background: var(--modal-bg);
  border: 1px solid var(--gold-dim);
  border-radius: 8px;
  padding: 18px 18px 16px;
  width: min(360px, 100%);
  max-height: min(80vh, 600px);
  overflow-y: auto;
  box-shadow: 0 8px 32px rgba(0,0,0,0.6);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  -webkit-overflow-scrolling: touch;
  animation: slideDown 0.18s ease;
}
.km-close {
  position: absolute;
  top: 8px;
  right: 10px;
  background: none;
  border: none;
  color: var(--text);
  cursor: pointer;
  font-size: var(--fs-md);
  padding: 2px 6px;
  line-height: 1;
}
.km-close:hover { color: var(--gold); }
.km-title {
  font-size: var(--fs-lg);
  color: var(--cream);
  font-family: var(--ff-display);
  font-weight: 600;
  text-align: center;
  padding-right: 24px;
  letter-spacing: 0.04em;
}
.km-subtitle {
  font-size: var(--fs-xs);
  color: var(--gold-dim);
  text-align: center;
  letter-spacing: 0.05em;
  border-bottom: 1px solid rgba(var(--gold-rgb), 0.15);
  padding-bottom: 8px;
  margin-bottom: 2px;
}
.km-list {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.km-row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: 8px 10px;
  background: var(--inset);
  border: 1px solid var(--inset);
  border-radius: 4px;
}
.km-row .km-av {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--inset);
  font-size: var(--fs-xl);
  line-height: 1;
}
.km-row .km-av.letter { font-size: var(--fs-md); font-weight: 700; color: var(--gold); }
.km-row .km-av.has-pth-avatar { background: rgba(0,0,0,0.2); padding: 0; overflow: visible; }
.km-row .km-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-0);
}
.km-row .km-name {
  color: var(--cream);
  font-family: var(--ff-mono);
  font-size: var(--fs-base);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.km-row .km-meta {
  color: var(--gold-dim);
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  letter-spacing: 0.05em;
}
.km-row .km-kick {
  flex-shrink: 0;
  background: rgba(var(--red-rgb), 0.18);
  border: 1px solid rgba(var(--red-rgb), 0.4);
  color: var(--red);
  border-radius: 4px;
  padding: 6px 10px;
  font-size: var(--fs-md);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.05s;
}
.km-row .km-kick:hover {
  background: rgba(var(--red-rgb), 0.35);
  border-color: var(--red);
}
.km-row .km-kick:active { transform: scale(0.95); }
.km-row.km-self {
  /* Yourself: shown for context but no kick button */
  opacity: 0.7;
}
.km-row.km-self .km-name::after {
  content: ' (vous, admin)';
  color: var(--gold-dim);
  font-size: var(--fs-xs);
}
.km-empty {
  text-align: center;
  color: var(--gold-dim);
  font-style: italic;
  padding: 18px;
}

/* ── Server timeout warning ── global alert dialog.
   Sits above every other surface: it can fire while the help window, the
   options or a ranking modal is open, and missing it costs the connection.
   Mirrors the QML popup, which lives on Overlay.overlay. */
#timeout-warn-modal,
#conn-lost-modal {
  position: fixed;
  inset: 0;
  z-index: 10050;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-6);
}
/* Secondary line, quieter than the countdown itself (QML: secondary.col300). */
#tow-hint {
  font-size: var(--fs-sm);
  color: var(--gold-dim);
}
#tow-ok:disabled { opacity: 0.5; cursor: default; }

/* ── Kick-confirm modal ── small nested alert dialog */
.kcm-card {
  position: relative;
  background: var(--modal-bg);
  border: 1px solid var(--border-hi);
  border-radius: 14px;
  padding: 22px 24px 18px;
  width: min(360px, 100%);
  box-shadow: 0 8px 32px rgba(0,0,0,0.7);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-4);
  animation: slideDown 0.18s ease;
}
/* Le dialogue officiel n'a pas d'icone — on la masque pour rester fidele. */
.kcm-icon { display: none; }
.kcm-title {
  font-size: 1.15rem;
  color: var(--text-hi);
  font-family: var(--ff-display);
  font-weight: 700;
  letter-spacing: 0.01em;
  text-align: left;
}
.kcm-msg {
  font-size: var(--fs-md);
  color: var(--cream);
  text-align: left;
  line-height: 1.45;
  word-break: break-word;
}
.kcm-btns {
  display: flex;
  gap: var(--sp-4);
  width: 100%;
  margin-top: 4px;
}
.kcm-cancel,
.kcm-confirm {
  flex: 1;
  padding: 10px 12px;
  border-radius: var(--r-md);
  font-family: var(--ff-display);
  font-size: var(--fs-base);
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.1s;
}
.kcm-cancel {
  background: transparent;
  border: 1px solid var(--border-hi);
  color: var(--cream);
}
.kcm-cancel:hover  { background: var(--inset); }
.kcm-cancel:active { transform: scale(0.98); }
.kcm-confirm {
  background: transparent;
  border: 1px solid var(--border-hi);
  color: var(--cream);
}
.kcm-confirm:hover  { background: var(--inset); }
.kcm-confirm:active { transform: scale(0.98); }
/* Cadre de focus clavier des boutons de dialogue (parité QML b170786,
   useSystemFocusVisuals) : au clavier seulement, jamais après un clic. */
.ld-btn:focus-visible, .kcm-cancel:focus-visible, .kcm-confirm:focus-visible,
#game-invite-banner button:focus-visible { outline: 2px solid var(--gold-hi); outline-offset: 2px; }


/* ── Waiting-panel start buttons (2-button variant) ──
   The .wp-ready-row used to host a single LANCER MAINTENANT button.
   With the introduction of startNoBots() the row now offers two:
   "▶ Démarrer" (humans only) + "▶ + Bots" (fill with bots).
   They sit side by side and wrap to two lines on very narrow widths. */
.wp-ready-btn-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-3);
  width: 100%;
}
.wp-ready-btn-row .wp-ready-btn {
  flex: 1 1 auto;
  min-width: 110px;
  max-width: 220px;
}
/* "+ Bots" variant: same green family but slightly darker tint so
   the user can tell the two apart without having to read the labels. */
.wp-ready-btn.wp-ready-btn-bots {
  background: linear-gradient(180deg, rgba(var(--green2-rgb), 0.22), rgba(var(--green2-rgb), 0.10));
  border-color: rgba(var(--green2-rgb), 0.65);
  /* Slightly faded — "start" is the primary action, "+ bots" the secondary. */
  opacity: 0.94;
}
.wp-ready-btn.wp-ready-btn-bots:hover {
  background: linear-gradient(180deg, rgba(var(--green2-rgb), 0.4), rgba(var(--green2-rgb), 0.2));
  opacity: 1;
}
/* On very small phones, give the buttons a touch more vertical room
   so the labels don't wrap awkwardly inside a single button. */
@media (max-width: 380px) {
  .wp-ready-btn-row .wp-ready-btn {
    flex: 1 1 100%;
    max-width: 280px;
  }
}


/* ── Spectators section in #game-info-modal ──
   Shown only when there's at least one spectator watching the table.
   The list reuses the same row pattern as the kick modal (.km-row)
   but with smaller avatars (28px) since this is informational, not
   actionable. */
.gim-section-count {
  color: var(--gold-dim);
  font-size: var(--fs-xs);
  font-weight: normal;
  letter-spacing: 0.05em;
}
.gim-spec-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  margin-top: 6px;
}
.gim-spec-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 5px 8px;
  background: var(--inset);
  border: 1px solid var(--inset);
  border-radius: var(--r-xs);
}
.gim-spec-row .gim-spec-av {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--inset);
  font-size: 1.2rem;
  line-height: 1;
}
.gim-spec-row .gim-spec-av.letter {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--gold);
}
.gim-spec-row .gim-spec-av.has-pth-avatar {
  background: rgba(0,0,0,0.2);
  padding: 0;
  overflow: visible;
}
.gim-spec-row .gim-spec-name {
  flex: 1;
  color: var(--cream);
  font-family: var(--ff-mono);
  font-size: var(--fs-base);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gim-spec-me {
  color: var(--gold-dim);
  font-size: var(--fs-xs);
  font-style: italic;
  margin-left: 4px;
}
.gim-badge.gim-badge-spec {
  background: rgba(var(--blue-rgb), 0.15);
  border-color: rgba(var(--blue-rgb), 0.4);
  color: var(--info);
}


/* ── Ghost seat — player has LEFT the table (not just eliminated) ──
   A player who left during a game stays in seats[] to preserve the
   pixel positions of every other seat around the felt (re-shuffling
   them is disorienting; see the seat-out commentary). But unlike a
   merely-eliminated player they should look essentially gone:
   no name, no money, no actions, no OUT badge. The avatar fades to
   ~15% opacity so the position is still discoverable on close
   inspection but doesn't compete for the player's attention.

   Difference at a glance:
     .seat-out   = eliminated, still at the table → opacity 0.35 + OUT badge
     .seat-ghost = left the table → opacity 0.15, no text, no badge */
.seat.seat-ghost {
  opacity: 0.15;
  filter: grayscale(1);
  pointer-events: none;
}
.seat.seat-ghost .seat-name,
.seat.seat-ghost .seat-money,
.seat.seat-ghost .seat-bet,
.seat.seat-ghost .seat-action,
.seat.seat-ghost .blind-chip,
.seat.seat-ghost .dealer-chip,
.seat.seat-ghost .seat-flag {
  display: none !important;
}
/* Suppress the OUT pseudo-element specifically: .seat-ghost wins over
   .seat-out (we made them mutually exclusive in the class string, but
   this is defence-in-depth in case something else adds .seat-out). */
.seat.seat-ghost::after {
  content: none !important;
}


/* ── Copy invite-link button in #game-info-modal ── */
.gim-share-section {
  text-align: center;
}
.gim-copy-link-btn {
  width: 100%;
  padding: 10px 14px;
  background: linear-gradient(180deg, rgba(var(--gold-rgb), 0.2), rgba(var(--gold-rgb), 0.1));
  border: 1px solid var(--border-hi);
  border-radius: 5px;
  color: var(--gold-hi);
  font-family: var(--ff-display);
  font-size: var(--fs-base);
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.05s;
}
.gim-copy-link-btn:hover {
  background: linear-gradient(180deg, rgba(var(--gold-rgb), 0.35), rgba(var(--gold-rgb), 0.18));
  border-color: var(--gold);
}
.gim-copy-link-btn:active { transform: scale(0.98); }
.gim-share-hint {
  margin-top: 6px;
  font-size: var(--fs-xs);
  color: var(--gold-dim);
  font-style: italic;
  letter-spacing: 0.03em;
}


/* ── Poker-hands help button (inline SVG, replaces the 🂡 glyph) ── */
.btn-hands { color: var(--gold); }
.btn-hands:hover { color: var(--gold-hi); border-color: var(--border-hi); }
.btn-hands svg { display: block; }


/* ── 4-aces SVG inside the end-game overlay (replaces 🎲) ──
   .eg-trophy already provides the bob animation + gold drop-shadow;
   we just need the inline SVG to center and sit inline-block so the
   text-align:center on the parent applies. */
.eg-trophy .eg-aces {
  display: inline-block;
  vertical-align: middle;
}


/* ── Player bar when locally eliminated (.pb-out) ──
   Mirror the .seat-out look on the seats: grey out the avatar/name/stack,
   show clearly that the player has busted out of the tournament. The
   "OUT" prefix is added inline by JS in the money line. */
.player-bar.pb-out .pb-avatar,
.player-bar.pb-out .pb-name,
.player-bar.pb-out .pb-action {
  opacity: 0.45;
  filter: grayscale(0.55);
}
.player-bar.pb-out .pb-cards .pk { opacity: 0.5; }
/* Tag OUT net + suit le theme (meme logique que .seat-out::after). */
.pb-out-tag {
  display: inline-block;
  font-weight: 800;
  letter-spacing: 0.1em;
  color: #fff;
  background: var(--red);
  padding: 0 6px;
  border-radius: 4px;
  border: 1px solid rgba(255,255,255,0.22);
  box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}

/* ═══════════════════════════════════════════════════════════════════
   LOBBY REDESIGN — création (repliable) au-dessus des tables,
   cartes de tables détaillées, et mise en page responsive
   (téléphone : 1 colonne · tablette : tables en grille 2 col ·
    ordinateur : création en colonne latérale + tables en grille).
   ═══════════════════════════════════════════════════════════════════ */
.lobby-body { display: block; overflow-y: auto; padding: 16px 14px 32px; }
.lobby-grid { display: grid; gap: 18px; grid-template-columns: 1fr; max-width: none; align-items: start; }

/* Bouton repliable "Créer une table" */
.create-toggle {
  width: 100%; display: flex; align-items: center; gap: var(--sp-5);
  background: rgba(var(--gold-rgb), 0.07); border: 1px solid var(--border); border-radius: var(--r-bar);
  color: var(--gold); cursor: pointer; padding: 13px 16px;
  font-family: var(--ff-display); font-size: var(--fs-lg); font-weight: 600; transition: background 0.2s;
}
.create-toggle:hover { background: rgba(var(--gold-rgb), 0.13); }
.create-toggle .ct-ico { font-size: 1.25rem; }
.create-toggle .ct-arrow { margin-left: auto; width: 0; height: 0; border-style: solid; border-width: 6px 0 6px 9px; border-color: transparent transparent transparent var(--gold-dim); transition: transform 0.25s; }
.create-toggle.open { border-radius: 10px 10px 0 0; }
.create-toggle.open .ct-arrow { transform: rotate(90deg); }

#create-form {
  /* Échelle typographique du formulaire — harmonisée (police Inter/QML) */
  /* Aliasés sur la couche générique (:root) — valeurs strictement identiques. */
  --cf-fs-xs: var(--fs-xs); --cf-fs-body: var(--fs-base); --cf-fs-value: var(--fs-md); --cf-fs-ico: var(--fs-xl); --cf-fs-ico-sm: var(--fs-lg);
  padding: 14px; background: rgba(8,18,10,0.6); border: 1px solid var(--border); border-top: none; border-radius: 0 0 10px 10px; }
/* PokerTH clair : le fond du formulaire est code en dur (vert sombre translucide)
   et ne suit pas la palette -> en clair il vire au gris et masque le texte
   var(--text) (noms de presets non actifs, libelle mot de passe). Fond clair force. */
:root[data-theme="pokerth-light"] #create-form { background: rgba(255,255,255,0.62); }

/* En page pleine « Creer une table » (#s-create), le formulaire n'est plus
   colle sous le bouton toggle accordeon de la colonne lobby : on referme donc
   le cadre en haut (contour complet + coins arrondis), sinon le bord superieur
   apparait coupe juste sous le header. Deux ids => bat la regle de base. */
#s-create #create-form { border-top: 1px solid var(--border); border-radius: var(--r-bar); }

/* ── PokerTH clair : corrections de contraste (clair uniquement) ──
   Plusieurs elements gardent des couleurs CLAIRES codees en dur (pensees pour le
   fond sombre) -> illisibles sur fond clair. On corrige ici, sans toucher au JS. */
/* Assistance (force de la main) : le texte est colore inline par le JS selon la force
   (teintes claires). On garde un fond SOMBRE pour cette pastille en clair -> le code
   couleur (gris faible -> vert/rouge fort) reste lisible tel quel. */
/* Ecran d'attente : wp-ready-label/btn (pas de couleur de base) corriges ici ;
   ld-quit/ld-create/wp-ok/wp-hint-admin/wp-need pilotes par variables de theme. */
:root[data-theme="pokerth-light"] .wp-ready-label,
:root[data-theme="pokerth-light"] .wp-ready-btn { color: var(--green); }
/* Siege actif (a parler) : nom/stack attenues a 0.35 (concu pour texte clair sur fond
   sombre). En clair le texte est SOMBRE -> 0.35 = invisible. On remonte l'opacite en
   clair uniquement : leger estompage conserve, mais lisible. */
:root[data-theme="pokerth-light"] .seat.active .seat-name,
:root[data-theme="pokerth-light"] .seat.active .seat-money { opacity: 0.7; }

/* Préréglages */
.cf-presets-label { font-size: var(--cf-fs-xs); letter-spacing: 0.18em; text-transform: uppercase; color: var(--gold-dim); font-weight: 600; margin: 0 2px 8px; }
/* Rangée label + bouton Développer/Réduire du bloc « Style de partie ». */
.cf-style-label-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.cf-style-toggle {
  display: inline-flex; align-items: center; gap: 5px;
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--gold-dim);
  font-family: inherit; font-size: var(--fs-2xs); font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  padding: 3px 8px; cursor: pointer;
}
.cf-style-toggle:hover { border-color: var(--gold-dim); color: var(--gold); }
.cf-style-toggle .cfs-arr { font-size: var(--cf-fs-xs); line-height: 1; }
.cf-presets { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: 16px; }
.cf-preset {
  flex: 1 1 104px; border: 1.5px solid var(--border); border-radius: var(--r-md); background: var(--inset);
  color: var(--text); cursor: pointer; padding: 11px 6px; text-align: center;
  transition: border-color 0.2s, background 0.2s; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: 3px;
}
.cf-preset:hover { border-color: var(--gold-dim); }
.cf-preset.active { border-color: var(--sel,var(--gold)); background: rgba(var(--sel-rgb,var(--gold-rgb)), 0.14); color: var(--cream); box-shadow: 0 0 12px rgba(var(--sel-rgb,var(--gold-rgb)), 0.18); }
.cf-preset .cfp-ico { font-size: var(--cf-fs-ico); line-height: 1.1; }
.cf-preset .cfp-n { font-family: var(--ff-display); font-size: var(--cf-fs-body); font-weight: 600; }
.cf-preset .cfp-d { font-size: var(--cf-fs-xs); color: var(--gold-dim); }

/* ── Rangée Style de partie : grille fixe 3 colonnes (2 rangées logiques :
   rythmes libres / formats pokerth.net), cartes allégées — esprit QML :
   plat, radius 9, bordures discrètes. ⭐ Perso devient une barre fine
   horizontale sous la grille (action de chargement, pas un style). ── */
.cf-presets-style { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-bottom: 8px; }
.cf-presets-style .cf-preset { padding: 8px 4px; gap: var(--sp-0); }
.cf-presets-style .cf-preset .cfp-ico { font-size: var(--cf-fs-ico); }
.cf-presets-style .cf-preset .cfp-n { font-size: var(--cf-fs-body); }
.cf-presets-style .cf-preset .cfp-d { font-size: var(--cf-fs-xs); }
.cf-preset.cfp-bar { flex-direction: row; align-items: center; justify-content: center; width: 100%; padding: 8px 10px; gap: var(--sp-3); margin-bottom: 16px; }
.cf-preset.cfp-bar .cfp-ico { font-size: var(--cf-fs-ico-sm); line-height: 1; }
.cf-preset.cfp-bar .cfp-n { font-size: var(--cf-fs-body); }
.cf-preset.cfp-bar .cfp-d { font-size: var(--cf-fs-xs); }
.cf-preset.cfp-bar .cfp-d::before { content: '— '; }

/* Curseurs (sliders) Joueurs max / Stack */
.cf-sliders { display: grid; gap: 14px; margin-top: 14px; }
.cf-slider label { display: flex; justify-content: space-between; align-items: baseline; font-size: var(--cf-fs-xs); color: var(--gold-dim); letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 8px; }
.cf-range-val { font-family: var(--ff-mono); font-size: var(--cf-fs-value); font-weight: 700; color: var(--cream); letter-spacing: 0; }
.cf-num-wrap { display: inline-flex; align-items: center; gap: 5px; }
.cf-range-num {
  width: 6.5em; background: var(--field-bg); border: 1px solid var(--border); border-radius: 4px;
  color: var(--cream); font-family: var(--ff-mono); font-size: var(--fs-md); font-weight: 700;
  padding: 3px 7px; text-align: right; outline: none; letter-spacing: 0; text-transform: none;
  -moz-appearance: textfield;
}
.cf-range-num::-webkit-outer-spin-button, .cf-range-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.cf-range-num:focus { border-color: var(--gold); }
.cf-slider label .cf-range-num { width: 4em; }
.cf-slider label .cf-num-wrap .cf-range-num { width: 6.5em; }
.cf-money { color: var(--gold); font-size: var(--cf-fs-value); font-weight: 700; }
.cf-unit { color: var(--gold-dim); font-size: var(--cf-fs-body); font-weight: 600; }
.cf-unit-ico { font-size: var(--cf-fs-ico-sm); }
#cf-players.cf-range-num { width: 4.2em; }
.cf-range { -webkit-appearance: none; appearance: none; width: 100%; height: 6px; border-radius: var(--r-xs); background: var(--inset); outline: none; cursor: pointer; }
.cf-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 20px; height: 20px; border-radius: 50%; background: var(--gold); border: 2px solid #1a1408; cursor: pointer; box-shadow: 0 0 7px rgba(var(--gold-rgb), 0.5); transition: transform 0.1s; }
.cf-range::-webkit-slider-thumb:active { transform: scale(1.12); }
.cf-range::-moz-range-thumb { width: 20px; height: 20px; border-radius: 50%; background: var(--gold); border: 2px solid #1a1408; cursor: pointer; box-shadow: 0 0 7px rgba(var(--gold-rgb), 0.5); }
.cf-range::-moz-range-track { height: 6px; border-radius: var(--r-xs); background: var(--inset); }
.cf-range:hover::-webkit-slider-thumb { background: var(--gold-hi); }
.cf-range:hover::-moz-range-thumb { background: var(--gold-hi); }

/* Liste des tables : grille + cartes détaillées */
/* Entête « Tables disponibles » : mêmes attributs que les autres entêtes de
   panneau (.g-chat-panel-header) — fond chrome, bordure basse, padding et
   typo identiques. */
.games-col .pane-header {
  padding: 4px 10px;
  background: var(--chrome-tint);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-2xs);
  letter-spacing: 0.15em;
}
.games-scroll { display: grid; gap: var(--sp-5); grid-template-columns: 1fr; overflow: visible; }
/* Barre de filtre des tables (design A : puces arrondies) */
.filter-bar { display: flex; gap: var(--sp-2); flex-wrap: wrap; padding: 0 2px 12px; }
.filter-chip {
  font-family: var(--ff-mono); font-size: var(--fs-xs); font-weight: 500; letter-spacing: 0.02em;
  background: var(--inset); border: 1px solid var(--border); border-radius: 20px;
  color: var(--text); padding: 6px 13px; cursor: pointer; transition: all 0.15s; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 5px;
}
.filter-chip:hover { border-color: var(--gold-dim); color: var(--cream); }
.filter-chip.active { background: rgba(var(--sel-rgb,var(--gold-rgb)), 0.14); border-color: var(--sel,var(--gold)); color: var(--sel,var(--gold)); font-weight: 700; }
.filter-chip .cnt { font-size: var(--fs-2xs); opacity: 0.65; }
/* Compact icon, hidden on desktop (the i18n label already carries the emoji). */
.filter-chip .fc-ic { display: none; }
/* ── Mobile only: V4 compact — one row, equal chips, icons replace labels ── */
@media (max-width: 600px) {
  .filter-bar { flex-wrap: nowrap; gap: var(--sp-2); }
  .filter-chip { flex: 1; min-width: 0; justify-content: center; padding: 7px 6px; gap: var(--sp-1); border-radius: 16px; }
  .filter-chip .fc-ic { display: inline; }      /* show the icon… */
  .filter-chip .fc-ic + .fc-label { display: none; } /* …and hide the word (but not on 'All', which has no icon) */
}
.games-scroll .empty { grid-column: 1 / -1; padding: 32px 10px; }
.game-row.gcard {
  border: 1px solid var(--border); border-radius: 11px; background: var(--inset);
  align-items: stretch; flex-direction: column; gap: 9px; padding: 13px 14px;
}
.game-row.gcard:hover { border-color: var(--gold-dim); background: var(--inset-hi); }
.gcard-main { min-width: 0; }
.game-row.gcard .game-name { white-space: normal; display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; font-size: var(--fs-md); }
.game-row.gcard .game-meta { flex-wrap: wrap; gap: 4px 10px; font-size: var(--fs-xs); }
.game-badge { font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.05em; padding: 2px 7px; border-radius: 20px; text-transform: uppercase; white-space: nowrap; }
.game-badge.live   { color: var(--red,#e05050); background: rgba(var(--red-rgb), 0.14); border: 1px solid rgba(var(--red-rgb), 0.4); }
.game-badge.wait   { color: var(--green); background: rgba(var(--green-rgb), 0.15); border: 1px solid rgba(var(--green-rgb), 0.4); }
.game-badge.closed { color: #999; background: var(--inset); border: 1px solid var(--border); }
/* Palettes PokerTH officielles : etat en texte simple colore, dans la ligne meta
   (parite QML — pas de pilule) : running #FF6D00/#BF360C · open #4CAF50/#2E7D32. */
:root[data-theme^="pokerth"] .game-badge {
  background:none; border:none; padding:0; border-radius:0;
  font-size:inherit; text-transform:none; letter-spacing:0; font-weight:600;
}
:root[data-theme="pokerth"] .game-badge.live { color:#FF6D00; }
:root[data-theme="pokerth"] .game-badge.wait { color:#4CAF50; }
:root[data-theme="pokerth"] .game-badge.closed { color:#EF5350; }
:root[data-theme="pokerth-light"] .game-badge.live { color:#BF360C; }
:root[data-theme="pokerth-light"] .game-badge.wait { color:#2E7D32; }
:root[data-theme="pokerth-light"] .game-badge.closed { color:#C62828; }
.game-seats { display: flex; gap: 3px; margin-top: 8px; flex-wrap: wrap; }
.seat-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--inset); border: 1px solid var(--border); }
.seat-dot.on { background: var(--gold); border-color: var(--gold); box-shadow: 0 0 5px rgba(var(--gold-rgb), 0.5); }
.gcard-btns { display: flex; gap: var(--sp-3); align-items: center; }
.gcard-caret { flex:0 0 auto; background:none; border:none; cursor:pointer; color:var(--gold-dim);
  font-size:var(--fs-md); line-height:1; padding:4px 6px; border-radius:var(--r-sm); transition:color .12s, background .12s; }
.gcard-caret:hover { color:var(--gold); background:var(--inset-hi); }
/* Flèche « voir les joueurs » sur les lignes de partie : redondante avec le
   panneau INFOS DE PARTIE dans le lobby normal → masquée. Réaffichée uniquement
   quand je suis connecté à une table en attente du démarrage (#s-lobby.lobby-waiting),
   où le panneau Infos n'est plus au premier plan et où j'ai besoin de voir qui a rejoint. */
#s-lobby:not(.lobby-waiting) .gcard-caret { display: none; }
#s-lobby:not(.lobby-waiting) .game-players { display: none; }
/* Boutons façon maquette v3 : Rejoindre en vert (extensible) + œil compact */
.game-row.gcard .btn-join {
  flex: 1;
  border: 1.5px solid var(--green);
  background: rgba(var(--green-rgb), 0.12);
  color: var(--green);
  border-radius: 8px;
  padding: var(--sp-4);
  font-family: var(--ff-display);
  font-weight: 600;
  font-size: var(--fs-base);
}
.game-row.gcard .btn-join:hover { background: rgba(var(--green-rgb), 0.28); }

/* Liste dépliable des joueurs d'une table (clic sur la carte) */
.game-players {
  margin: -7px 0 10px;
  padding: 9px 12px 11px;
  background: var(--inset);
  border: 1px solid var(--border);
  border-top: none;
  border-radius: 0 0 12px 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 8px;
  animation: gpReveal 0.18s ease;
}
.game-players[hidden] { display: none; }
.gp-player {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  max-width: 100%;
  padding: 4px 9px;
  background: var(--inset-hi);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  line-height: 1.2;
}
.gp-player .gp-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gp-player.gp-pending { opacity: 0.55; }
/* Badge « Admin » de table (parite QML GameAdminBadge, commit 12f5eaf) :
   pastille verte (Theme.colorGameAdmin = colorStatusOpen) + ligne teintee
   (Theme.colorGameAdminRow). --green/--green-rgb suivent deja le mode
   clair/sombre des themes. */
.gp-admin { display:inline-block; flex:0 0 auto; margin-left:2px; padding:1px 6px;
  border-radius:8px; font-size:10px; font-weight:700; line-height:14px; white-space:nowrap;
  color:var(--green); background:rgba(var(--green-rgb), .22); border:1px solid rgba(var(--green-rgb), .7); }
.gp-player.gp-adminrow { background:rgba(var(--green-rgb), .14); border-color:rgba(var(--green-rgb), .45); }
.lgi-prow.lgi-adminrow { background:rgba(var(--green-rgb), .14); border-color:rgba(var(--green-rgb), .45); }
.cc-flag.gp-flag { width: 18px; height: auto; border-radius: 2px; flex: 0 0 auto; }
.gp-av {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--inset);
  font-size: var(--fs-lg);
  line-height: 1;
}
.gp-av.letter { font-size: var(--fs-xs); font-weight: 700; color: var(--gold); }
.gp-av.has-pth-avatar { background: rgba(0,0,0,0.2); padding: 0; overflow: visible; }
.gp-empty { opacity: 0.6; font-size: var(--fs-sm); padding: 2px 0; }
@keyframes gpReveal { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.game-row.gcard .btn-watch {
  flex: 0 0 auto;
  border: 1px solid var(--border);
  background: var(--inset);
  color: var(--text);
  border-radius: 8px;
  padding: 10px 14px;
  font-size: var(--fs-lg);
  line-height: 1;
  cursor: pointer;
}
.game-row.gcard .btn-watch:hover { border-color: var(--gold-dim); color: var(--gold); background: var(--inset-hi); }

/* Mobile (cards stacked): make the Rejoindre / Regarder buttons shorter. */
@media (max-width: 599.98px) {
  .game-row.gcard .btn-join  { padding: 5px 10px; font-size: var(--fs-sm); }
  .game-row.gcard .btn-watch { padding: 5px 12px; font-size: var(--fs-md); }
}

/* ── Téléphone (base) : boutons SOUS la carte, texte lisible ──
   ── Tablette (≥640) : boutons sur le FLANC DROIT, texte un cran plus grand ──
   ── Ordinateur (≥1024) : texte plus grand encore ── */
@media (min-width: 600px) {
  .game-row.gcard { flex-direction: row; align-items: center; gap: 14px; }
  .gcard-main { flex: 1 1 auto; }
  .gcard-btns { flex: 0 0 auto; align-self: center; }
  .game-row.gcard .btn-join { flex: 0 0 auto; font-size: var(--fs-md); }
  .game-row.gcard .game-name { font-size: var(--fs-lg); }
  .game-row.gcard .game-meta { font-size: var(--fs-sm); }
  .create-toggle { font-size: 1.15rem; }
}
@media (min-width: 900px) {
  .game-row.gcard .game-name { font-size: 1.16rem; }
  .game-row.gcard .game-meta { font-size: var(--fs-base); }
  .game-badge { font-size: var(--fs-xs); }
  .game-row.gcard .btn-join { font-size: var(--fs-md); padding: 11px 20px; }
  .game-row.gcard .btn-watch { font-size: 1.2rem; padding: 11px 16px; }
  .create-toggle { font-size: var(--fs-xl); padding: 16px 18px; }
  .seat-dot { width: 10px; height: 10px; }
  #create-form .cf label { font-size: var(--fs-2xs); }
  #create-form .cf input, #create-form .cf select { font-size: var(--fs-md); padding: 7px 9px; }
}

/* ── PWA edge-to-edge: respect notch / status-bar in fullscreen mode ──
   display_override lists "fullscreen" first, so an installed PWA on Android
   may draw under the system status bar. These insets keep the game & lobby
   headers clear of the notch and landscape camera cut-outs. Appended last so
   the longhand padding-* wins over the mobile shorthand `padding:` rules. */
@media (display-mode: fullscreen), (display-mode: standalone) {
  /* standalone ajoute : iOS PWA (apple-mobile-web-app-status-bar-style
     black-translucent) dessine SOUS la barre d'etat -> memes insets.
     Android standalone sans notch : env() = 0, no-op. */
  #s-game .header,
  #s-lobby .header,
  #s-connect .connect-header,
  /* Full-page screens/overlays that carry their own top bar. Without these
     the bar is drawn *under* the iOS status bar and its title/icons collide
     with the clock, signal and battery (reported narmod 2026-08-01, iPhone
     and iPad, installed PWA). */
  #s-create .cp-header,
  #privacy-page .cp-header {
    /* !important : le palier paysage telephone (max-height:500px) pose
       `.header{padding:2px 6px !important}` — un shorthand qui ecrasait ces
       longhands et supprimait les insets la ou l'encoche est sur le cote.
       Specificite id > classe, donc ces trois lignes gagnent. */
    padding-top: max(4px, env(safe-area-inset-top)) !important;
    padding-left: max(8px, env(safe-area-inset-left)) !important;
    padding-right: max(8px, env(safe-area-inset-right)) !important;
  }
}

/* ── Reset button in the create-table form (secondary, paired with Create) ── */
.btn-cf-reset {
  background: transparent;
  color: var(--text-hi);
  border: 1px solid var(--gold-dim);
  border-radius: 2px;
  font-family: var(--ff-display);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  padding: 10px 14px;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  transition: border-color 0.15s, color 0.15s, transform 0.1s;
}
.btn-cf-reset:hover  { border-color: var(--border-hi); color: var(--gold); }
.btn-cf-reset:active { transform: scale(0.98); }

/* ── Barre d'actions du formulaire « Créer une table » ──
   Même forme que les boutons de la barre du bas du lobby (.lobby-footbar .lfb-*) :
   coins arrondis 8 px, ff-display 600. Trois actions alignées :
   Réinitialiser (neutre) · Annuler (retour lobby, rouge doux) · Créer (or, primaire). */
.cf-actions {
  display: flex;
  align-items: stretch;
  gap: var(--sp-4);
  margin-top: 4px;
}
.cf-actions > button {
  flex: 1 1 0; min-width: 0; text-align: center;
  border: 1px solid transparent; border-radius: 8px; padding: 9px 14px;
  cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-family: var(--ff-display); font-weight: 600; font-size: var(--fs-md); letter-spacing: 0.02em;
  transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.1s, box-shadow 0.15s;
}
.cf-actions > button:active { transform: scale(0.98); }
/* Réinitialiser — neutre (bordure or atténuée) ; ne s'étire pas (icône seule sur mobile) */
.cf-actions .btn-cf-reset { flex: 0 0 auto; background: transparent; color: var(--text-hi); border-color: var(--gold-dim); }
.cf-actions .btn-cf-reset:hover { border-color: var(--border-hi); color: var(--gold); }
/* Annuler — retour au lobby sans créer ; neutre (palette QML), comme les autres boutons secondaires. */
.cf-actions .btn-cf-cancel { background: transparent; color: var(--text-hi); border-color: var(--gold-dim); }
.cf-actions .btn-cf-cancel:hover { background: var(--inset); border-color: var(--border-hi); }
/* Créer — couleur identique au bouton Créer du lobby (.lfb-create : or/neutre teinté), un peu plus large */
.cf-actions .cf-create-btn { flex-grow: 1.6; background: #2b3444; color: #ffffff; border-color: transparent; }
.cf-actions .cf-create-btn:hover { background: #384252; }
/* Mobile (<600px, canon QML) : la barre passe sur deux lignes. Reset (icône) + Annuler
   restent en haut ; « Créer » — l'action primaire — descend pleine largeur sur
   sa propre ligne, donc jamais tronquée quelle que soit la langue (fr « ＋ Créer
   cette table », de…), et devient une plus grande cible tactile. */
@media (max-width: 599.98px) {
  .cf-actions { flex-wrap: wrap; }
  .cf-actions .cf-create-btn { flex: 1 0 100%; }
}

/* ── Reusable toast (showToast in pokerth.js) ──
   Styled to match the header ••• overflow menu: dark panel, gold-hi hairline
   border, soft drop shadow, Inconsolata. */
.app-toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translate(-50%, 20px);
  z-index: 950;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  max-width: min(90vw, 360px);
  padding: 8px 16px;
  background: var(--modal-bg);
  border: 1px solid var(--border-hi);
  border-radius: var(--r-sm);
  box-shadow: 0 12px 32px rgba(0,0,0,0.7);
  color: var(--text-hi);
  font-family: var(--ff-mono);
  font-size: var(--fs-sm);
  letter-spacing: 0.02em;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.app-toast.show { opacity: 1; transform: translate(-50%, 0); }
.app-toast-tick { color: #3fb950; font-weight: 700; }

/* Reset button: full "↺ Réinitialiser" label on tablet/desktop, icon-only on
   phones (<600px, the QML-canon phone/tablet boundary) to leave room for Create.
   The label stays in the DOM (visually hidden) so it remains translated and
   reachable by screen readers; the standalone ↺ is aria-hidden. */
.btn-cf-reset .cf-reset-ico { display: none; }
@media (max-width: 599.98px) {
  .btn-cf-reset { padding: 10px 13px; }
  .btn-cf-reset .cf-reset-ico { display: inline; font-size: var(--cf-fs-ico-sm); line-height: 1; }
  .btn-cf-reset .cf-reset-lbl {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

/* Error variant of the reusable toast (showToast(msg,{tone:'error'})) —
   orange hairline + icon, e.g. the "game name too long" guard. */
.app-toast--error { border-color: var(--orange); }
.app-toast--error .app-toast-tick { color: var(--orange); }

/* ─── Mobile : confort tactile (v0.2.329) ─── */

/* iOS : pas de menu long-press (save image / loupe / sélection) sur les cartes
   de jeu et les boutons. Le chat et les champs restent sélectionnables. */
.pk-flip, .pk-fold, button, .cf-preset {
  -webkit-touch-callout: none;
}

/* Respecte « réduire les animations » du système (accessibilité + téléphones
   faibles). Les anims en forwards/both atteignent leur état final instantanément
   (cartes distribuées/retournées, jetons posés) ; le timer SVG, piloté en JS,
   n'est pas affecté. Durée ~0 (pas 0) pour que animationend/transitionend
   continuent de se déclencher. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ─── theme-color du navigateur, suivant le thème actif (lue par JS) ─── */
:root { --theme-color: #1d222b; color-scheme: dark; }                             /* thèmes sombres */
:root[data-theme="pokerth-light"] { --theme-color: #eef1f6; color-scheme: light; } /* thème clair    */

/* ─── Téléphones faibles / Linux + accessibilité : couper le backdrop-filter ───
   Le flou de fond (backdrop-filter) est coûteux en GPU. Ici ses seuls usages
   sont des voiles sombres de modale (rgba noir 0.65–0.85), purement décoratifs :
   aucun texte n'en dépend, donc les retirer ne nuit pas à la lisibilité. On les
   coupe quand l'utilisateur demande moins de transparence OU moins d'animations
   (signaux fiables, supportés y compris Firefox/WebKit des téléphones Linux). */
@media (prefers-reduced-transparency: reduce), (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
}


/* ===== Sieges : base neutre =============================================
   DOM neutre (display:contents) = repli « classique » quand aucun pack
   structurel ne s'applique (pack importé avec qmlStruct:false). */
.seat-plate, .seat-info, .seat-foot { display: contents; }

/* (packs web Classic/Chip/Plate/Card/Compact/Bar retirés en 0.3.698 —
   page blanche : les prochains sièges = packs importés style.css + traits.) */


/* ===== Sieges : STRUCTURE COMMUNE QML (html[data-seat-struct="qml"]) ======
   Depuis 0.3.695, TOUS les packs (integres + galerie) partagent cette
   structure — placement, gabarit de boite, self-box — sauf opt-out via le
   trait qmlStruct:false. Chaque pack n'apporte plus que son habillage
   (couleurs, avatar, cadre). Historique :
   Replique du client officiel : boite par joueur, GRILLE 2 colonnes :
   [avatar | 2 cartes] en LIGNE 1, nom + tapis (span 2 col) en LIGNE 2.
   max-width:none !important bat les caps .seat {max-width !important} des
   media-queries (sinon les cartes retombent sous l'avatar -> vertical).
   COULEURS : tokens de palette. Boite = var(--panel) et NON var(--field-bg) :
   --panel est dans PALETTE_TOKENS donc defini par TOUTES les palettes (y
   compris les paquets importes) ; --field-bg n'y est pas et retombait sur le
   vert de :root ("style green casino"). Bordure var(--border), nom var(--text-hi),
   tapis var(--gold)
   herites de la BASE (plus aucune surcharge en dur). La boite suit donc le
   theme PokerTH (claire en PokerTH Light, sombre ailleurs). Cartes injectees
   dans .seat-plate par renderSeats si pack actif : dos adversaires, faces moi. */
html[data-seat-struct="qml"] .seat { max-width: none !important; }
/* GamePlayerBox.qml : dégradé lighter(panel,~18%)→panel (QML : #434c5e→#1d222b),
   radius 6, bordure blanche 6 %, ombre 0 3px (opacité .42), opacity .9.
   color-mix garde la fidélité QML en PokerTH Dark ET suit les autres thèmes. */
html[data-seat-struct="qml"] .seat-plate {
  display: grid; grid-template-columns: auto auto;
  align-items: center; justify-content: center;
  column-gap: 4px; row-gap: 3px; position: relative;
  padding: var(--sp-1); border-radius: var(--r-sm);   /* 4px symétrique : budget hauteur QML */
  /* Degrade EXACT du QML (GamePlayerBox : col600 #394150 eclairci x1.18
     -> col700 #1d222b), FIXE — la plaque ne suit pas le --panel du theme
     de table (le client officiel garde sa plaque ardoise sur tous les
     feutres). */
  background: linear-gradient(180deg, #434c5f, #1d222b);
  border: 1px solid rgba(255, 255, 255, 0.06);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.42);
  opacity: 0.9;
  transition: transform 0.18s ease-out;
}
/* §4.2 QML (2.1.3+) : largeur de boîte adverse FIXE (opp ≈121), INDÉPENDANTE
   du contenu — « le texte n'élargit jamais le layout ». Avant : grille auto →
   « $6 000 » vs « $29 575 » (ou un nom long) élargissait la plate → le
   corridor libre entre plates latérales variait → --comm-scale, la rangée
   community ET la barre d'action (264×cs) « respiraient » avec les tapis
   (rapport narmod 03/09, choix B = parité complète plutôt que normalisation
   de mesure). border-box : 121 = boîte EXTÉRIEURE comme en QML ; le seat
   inline porte translate+boxScale par-dessus. Écarts acceptés : spectateur
   ≈115 en QML → 121 conservé partout ; self en largeur de contenu (valeur
   QML non documentée) ; packs image (data-seat-img) exclus — cadre 9-slice
   à padding libre, largeur pilotée par le pack. */
html[data-seat-struct="qml"]:not([data-seat-img]) .seat:not(.me) .seat-plate {
  width: 121px; box-sizing: border-box;
}
/* Le texte se range DANS la largeur imposée (élision, jamais d'élargissement,
   parité infoBar QML) : le nom cède la place, le tapis ne se coupe jamais. */
html[data-seat-struct="qml"] .seat:not(.me) .seat-name { min-width: 0; }
html[data-seat-struct="qml"] .seat:not(.me) .seat-money { flex: 0 0 auto; white-space: nowrap; }
/* PlayerBoxAccent : un tablestyle.xml peut TEINTER la boîte joueur (p. ex. le
   bleu des consoles du style Star Trek). Parité GamePlayerBox : l'accent
   TEINTE le dégradé et la bordure, il ne les remplace pas — la boîte reste
   sombre et le texte lisible dessus. Forces reprises telles quelles du QML
   (Qt.tint = composition alpha, donc équivalent à color-mix) :
     haut 0.34 · bas 0.22 · bordure alpha 0.60
   theme.mjs lit déjà la balise et expose --box-accent + data-box-accent ; il
   ne restait qu'à les consommer ici. Sans accent, rien ne change : la règle
   ci-dessus reste seule à s'appliquer (et les navigateurs sans color-mix
   gardent eux aussi la plaque neutre plutôt qu'une couleur cassée). */
html[data-seat-struct="qml"][data-box-accent] .seat-plate {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--box-accent) 34%, #434c5f),
    color-mix(in srgb, var(--box-accent) 22%, #1d222b));
  border-color: color-mix(in srgb, var(--box-accent) 60%, transparent);
}
/* AvatarCardRow QML : avatar CARRÉ = hauteur de rangée (box 84 → rangée 40 en
   wide ; box 71 → rangée 43 en narrow/portrait), radius 2, bord 1px col200,
   fond col700 opacité 0.9. Cartes : cardH == hauteur d'avatar, ratio 120:168
   (wide 29×40, narrow 31×43 ≈ largeur totale QML ~121). !important : bat les
   tailles d'avatar des media queries. Tokens surchargeables par thème. */
html[data-seat-struct="qml"] .seat-avatar {
  grid-column: 1; grid-row: 1;
  width: 40px !important; height: 40px !important;
  border-radius: 2px;
  background: var(--seat-avatar-bg, rgba(29, 34, 43, 0.9));
  border: 1px solid var(--seat-avatar-border, rgba(205, 211, 224, 0.9));
}
html[data-seat-struct="qml"] .seat-holecards .pk { width: 29px; height: 40px; font-size: var(--fs-2xs); }
html[data-seat-struct="qml"] .seat-narrow .seat-avatar { width: 43px !important; height: 43px !important; }
html[data-seat-struct="qml"] .seat-narrow .seat-holecards .pk { width: 31px; height: 43px; }
/* QML : pas de bord vert (self) ni or (au tour) sur l'avatar — le tour est
   signalé par le PlayerTurnGlow de la boîte. Halo/anneau neutralisés. */
html[data-seat-struct="qml"] .seat.me .seat-avatar,
html[data-seat-struct="qml"] .seat.active .seat-avatar {
  border-color: var(--seat-avatar-border, rgba(205, 211, 224, 0.9));
  box-shadow: none;
  animation: none !important;
}
html[data-seat-struct="qml"] .seat-holecards { grid-column: 2; grid-row: 1; display: flex; gap: var(--sp-1); flex: none; }
/* PlayerActionBadge QML : centré sur la rangée de hole-cards (x/y = cardsCenter),
   hauteur 18, radius 9, padding H 7, texte #eaf1ff 12px bold, pop à l'apparition
   (0.6→1.12→1.0). Le badge est injecté DANS .seat-holecards par renderSeats. */
html[data-seat-struct="qml"] .seat-holecards { position: relative; }
html[data-seat-struct="qml"] .seat-holecards .seat-action-badge {
  position: absolute; top: 50%; left: 50%;
  height: 18px; box-sizing: border-box;
  display: inline-flex; align-items: center;
  padding: 0 7px; border-radius: var(--r-md);
  font-size: 12px; font-weight: 700; letter-spacing: 0;
  color: #eaf1ff;
  box-shadow: none;
  z-index: 18;
  transform: translate(-50%, -50%);
  animation: seatBadgePop 0.23s ease-out;
}
@keyframes seatBadgePop {
  0%   { transform: translate(-50%, -50%) scale(0.6); }
  48%  { transform: translate(-50%, -50%) scale(1.12); }
  100% { transform: translate(-50%, -50%) scale(1); }
}
/* Wide (par défaut) : nom à GAUCHE ligne 1, tapis accent à DROITE ligne 2
   (infoBar 2 lignes du QML : nom 15px DemiBold #eff1f5 ls .3 / $stack bold). */
html[data-seat-struct="qml"] .seat-info {
  grid-column: 1 / -1; grid-row: 2;
  display: flex; flex-direction: column; gap: 0;
  align-items: stretch; min-width: 0;
}
html[data-seat-struct="qml"] .seat-name {
  max-width: 106px; text-align: left;
  font-size: 15px !important;               /* QML wide : 15px DemiBold */
  font-weight: 600; letter-spacing: 0.3px;
  line-height: 16px;   /* serré : hauteur de boîte QML (wide 84) */
  color: #eff1f5; /* col100 QML */
}
html[data-seat-struct="qml"] .seat-money { text-align: right; font-size: 15px !important; font-weight: 700; line-height: 16px; color: #E3C800; font-family: var(--ff-display); } /* accent or QML, 15px bold */
/* Wide (infoBar QML) : ligne 2 = drapeau 22×15 en bas-GAUCHE + stack à droite. */
html[data-seat-struct="qml"] .seat-info-row2 {
  display: flex; align-items: flex-end; gap: var(--sp-2); min-width: 0;
}
html[data-seat-struct="qml"] .seat-info-row2 .seat-money { margin-left: auto; }
html[data-seat-struct="qml"] .seat-info-row2 .seat-flag {
  position: static; filter: none; animation: none; line-height: 0; pointer-events: none;
}
html[data-seat-struct="qml"] .seat-info-row2 .seat-flag .cc-flag { width: 22px !important; height: 15px !important; }
/* Non-wide (hauteur scalée < 76 ou portrait) : « nom · tapis » sur UNE ligne. */
html[data-seat-struct="qml"] .seat-narrow .seat-info { flex-direction: row; align-items: baseline; justify-content: space-between; gap: var(--sp-2); }
html[data-seat-struct="qml"] .seat-narrow .seat-name { max-width: 62px; font-size: 12px !important; line-height: 13px; }
html[data-seat-struct="qml"] .seat-narrow .seat-money { font-size: 12px !important; line-height: 13px; }
/* Desktop : texte des sieges un peu plus discret (les boites sont deja scalees par boxScale). */
@media (min-width: 900px) and (hover: hover) and (pointer: fine) {
  /* Nom/tapis : tailles QML fixes (15/12 px) définies dans la base — plus
     d'override desktop, le boxScale de l'ellipse fait la mise à l'échelle. */
  html[data-seat-struct="qml"] .seat-action-label { font-size: var(--fs-2xs) !important; }
}
/* Joueur au tour : boîte « soulevée » scale 1.04 (playerBox.scale QML). Le
   cadre or est porté par ::after (PlayerTurnGlow) — la bordure blanche 6 % de
   la boîte reste visible dessous, comme dans le client officiel. */
html[data-seat-struct="qml"] .seat.active .seat-plate {
  transform: scale(1.04);
  opacity: 1;
}
/* PlayerTurnGlow QML, pour TOUTES les boîtes au tour (self inclus — le QML
   partage le même composant) : cadre or #FFD54A 1 px à −2 px du bord
   (anchors.margins -2, radius 6) + halo doux, pulsation 0.65↔1.0 en 1.5 s.
   iOS-safe : box-shadow STATIQUE sur le pseudo-élément, seule l'OPACITÉ est
   animée (mémo thermique). Tokens surchargeables par thème :
   --turn-glow (cadre) et --turn-glow-halo (halo). */
html[data-seat-struct="qml"] .seat.active .seat-plate::after {
  /* FORMULE de surbrillance adaptative : le cadre suit la FORME de la plate
     du pack actif (border-radius: inherit) et chaque pack peut ajuster
     l'écart (--turn-glow-inset), le rayon (--turn-glow-radius) et le plan
     (--turn-glow-z : passer sous un avatar débordant, qui porte alors son
     propre anneau). Défauts = comportement QML historique. */
  content: ""; position: absolute;
  inset: var(--turn-glow-inset, -2px);
  border-radius: var(--turn-glow-radius, inherit);
  border: 1px solid var(--turn-glow, #FFD54A);
  box-shadow: 0 0 14px 2px var(--turn-glow-halo, rgba(255, 213, 74, 0.55));
  pointer-events: none; z-index: var(--turn-glow-z, 10); will-change: opacity;
  animation: turnGlowPulse 1.5s ease-in-out infinite;
}
@keyframes turnGlowPulse { 0%, 100% { opacity: 0.65; } 50% { opacity: 1; } }
/* PlayerWinnerOverlay QML : cadre or #FFD700 3px + halo autour de la boîte du
   gagnant (ne couvre pas les cartes), badge « WINNER » pilule (h 16, radius 8,
   fond #0d3d0d, bord/texte or 9px bold) à 6px AU-DESSUS de la boîte —
   au-dessous (.winner-below) pour la boîte la plus haute (TC portrait).
   Défini APRÈS le turn glow : à showdown, la règle winner l'emporte sur
   ::after. Tokens thème : --winner-frame / --winner-halo / --winner-badge-bg. */
html[data-seat-struct="qml"] .seat.winner .seat-plate { opacity: 1; }
html[data-seat-struct="qml"] .seat.winner .seat-plate::after {
  content: ""; position: absolute;
  inset: var(--winner-frame-inset, var(--turn-glow-inset, -2px));
  border-radius: var(--winner-frame-radius, inherit); /* forme du pack */
  border: 3px solid var(--winner-frame, #FFD700);
  box-shadow: 0 0 16px 2px var(--winner-halo, rgba(255, 215, 0, 0.6));
  pointer-events: none; z-index: var(--winner-frame-z, 19);
  animation: none; opacity: 1;
}
html[data-seat-struct="qml"] .seat-winner-badge {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(100% + 6px);
  height: 16px; box-sizing: border-box;
  display: inline-flex; align-items: center;
  padding: 0 6px; border-radius: 8px;
  background: var(--winner-badge-bg, #0d3d0d);
  border: 1px solid var(--winner-frame, #FFD700);
  color: var(--winner-frame, #FFD700);
  font-family: var(--ff-display); font-size: 9px; font-weight: 700;
  letter-spacing: 0.04em; white-space: nowrap;
  z-index: 30; pointer-events: none;
}
html[data-seat-struct="qml"] .seat.winner-below .seat-winner-badge { bottom: auto; top: calc(100% + 6px); }
/* QML : le badge d'action est masqué pendant l'état gagnant (visible: !isWinner). */
html[data-seat-struct="qml"] .seat.winner .seat-action-badge,
html[data-seat-struct="qml"] .seat.winner .seat-action-label { display: none; }
/* PlayerTimeoutBar QML (bloc F) : 44×9, track #0e1a30 bordé blanc 55 %,
   remplissage #4070D0 (self #6E9CEC), radius pilule, centrée sur les cartes à
   l'emplacement du badge d'action. S'AJOUTE au cadre rectangulaire décomptant
   (conservé). Tokens thème : --timeout-track / --timeout-fill(-self). */
html[data-seat-struct="qml"] .seat-timeout-bar {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 44px; height: 9px; box-sizing: border-box;
  border-radius: 5px;
  background: var(--timeout-track, #0e1a30);
  border: 1px solid rgba(255, 255, 255, 0.55);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
  overflow: hidden;
  z-index: 18; pointer-events: none;
}
html[data-seat-struct="qml"] .seat-timeout-bar .stb-fill {
  height: 100%;
  border-radius: 4px;
  background: var(--timeout-fill, #4070D0);
  transition: width 0.9s linear;
}
html[data-seat-struct="qml"] .seat-timeout-bar.me .stb-fill { background: var(--timeout-fill-self, #6E9CEC); }
/* ── Bandeau self QML (GamePlayerSelfBox.topStrip) ─────────────────────────
   Strip 18 px à 6 px AU-DESSUS de la self-box : badge d'action aligné à
   DROITE (hPadding 16 → padding 0 8px), barre de timeout 56×7 centrée
   horizontalement, mise (BetChip) à gauche de l'indicateur visible —
   badge : bord à bord + 8 px ; barre : 50 % + 28 + 8 px du bord droit. */
html[data-seat-struct="qml"] .seat.me .seat-self-strip {
  position: absolute; left: 0; right: 0;
  bottom: calc(100% + 6px);
  height: 18px;
  display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-3);
  pointer-events: none; z-index: 26;
}
html[data-seat-struct="qml"] .seat.me .seat-self-strip .seat-action-badge {
  position: static; top: auto; left: auto; transform: none;
  height: 18px; box-sizing: border-box;
  display: inline-flex; align-items: center;
  padding: 0 8px; border-radius: var(--r-md);
  font-size: 12px; font-weight: 700; letter-spacing: 0;
  color: #eaf1ff; box-shadow: none;
  animation: stripPop 0.23s ease-out;
}
@keyframes stripPop { 0% { scale: 0.6; } 48% { scale: 1.12; } 100% { scale: 1; } }
/* Barre self : 56×7 (QML), centrée horizontalement dans le strip. */
html[data-seat-struct="qml"] .seat.me .seat-self-strip .seat-timeout-bar {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 56px; height: 7px; border-radius: 4px;
}
/* Mise self : visible dans le strip (annule le display:none du siège self),
   en flux, sans positionnement betside. Quand seule la barre est visible,
   elle se cale à gauche de celle-ci (betRightMargin QML). */
html[data-seat-struct="qml"] .seat.me .seat-self-strip .seat-bet {
  display: inline-flex; position: static; transform: none; margin: 0;
}
html[data-seat-struct="qml"] .seat.me .seat-self-strip.has-tb .seat-bet {
  margin-right: calc(50% + 36px);
}
/* ── Bandeau « cartes a decouvert » (.seat-open-strip) ─────────────────────
   Des que les hole-cards d'un siege sont face visible (showdown, spectateur,
   entrainement offline), le badge d'action et la barre de decompte quittent
   la rangee de cartes pour ce bandeau, 4 px AU-DESSUS de la box — ou 4 px en
   DESSOUS (.below) pour les sieges hauts, comme le badge winner. Aucune
   surcouche ne recouvre donc jamais une carte lisible.
   Regles posees APRES le bloc timeout-bar : specificite egale, on gagne. */
.seat-open-strip {
  position: absolute; left: 0; right: 0;
  bottom: calc(100% + 4px);
  height: 16px;
  display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
  pointer-events: none; z-index: 26;
}
.seat-open-strip.below { bottom: auto; top: calc(100% + 4px); }
.seat-open-strip .seat-action-badge {
  position: static; top: auto; left: auto; transform: none;
  height: 16px; box-sizing: border-box;
  display: inline-flex; align-items: center;
  padding: 0 7px; border-radius: var(--r-md);
  font-size: 11px; font-weight: 700; letter-spacing: 0;
  color: #eaf1ff; box-shadow: none;
  animation: stripPop 0.23s ease-out;
}
.seat-open-strip .seat-timeout-bar {
  position: static; top: auto; left: auto; transform: none;
  width: 44px; height: 7px; border-radius: 4px;
}
/* Puck self : HORS du siège — ancre QML exacte du GamePlayerSelfBox
   (anchors.left = bord droit de la box + 6, aligné au haut). !important
   requis : les règles betside-* arrivent PLUS BAS dans le fichier à
   spécificité égale et remettaient le puck SUR les cartes du self. */
html[data-seat-struct="qml"] .seat.me .seat-pucks {
  left: calc(100% + 6px) !important; right: auto !important;
  top: 0 !important; bottom: auto !important; transform: none !important;
}
/* Self au tour : cadre du PlayerTurnGlow un peu plus épais (borderWidth 2 QML). */
html[data-seat-struct="qml"] .seat.me.active .seat-plate::after { border-width: 2px; }
/* Self au tour : levée QML (GamePlayerSelfBox `scale: isAtTurn ? 1.03 : 1.0`,
   180 ms OutQuad). Appliquée à la PLAQUE (le transform inline du .seat porte
   translate+boxScale et ne doit pas être écrasé) ; les pucks, hors plaque,
   restent en place — écart accepté vs le QML qui lève toute la box. */
html[data-seat-struct="qml"] .seat.me .seat-plate { transition: transform 0.18s ease-out; transform-origin: center; }
html[data-seat-struct="qml"] .seat.me.active .seat-plate { transform: scale(1.03); }
/* Couché : 0.72 ; éliminé : la base gère déjà l'estompe (transition QML 250ms). */
html[data-seat-struct="qml"] .seat { transition: opacity 0.25s ease-out; }
html[data-seat-struct="qml"] .seat.folded { opacity: 0.72; }
/* Self couché : parité totale avec le client officiel (narmod 2026-07-28,
   révise la décision du 26/07). Source upstream qt6-qml :
     GamePlayerBox     (adversaires) : folded → 0.72
     GamePlayerSelfBox (self)        : folded → 0.78   ← volontairement moins
                                       estompée que les autres, pas identique
   Les deux boîtes animent l'opacité sur 250 ms (Easing.OutQuad). */
html[data-seat-struct="qml"] .seat.me.folded { opacity: 0.78; }
/* Cartes d'un joueur couché. AvatarCardRow.qml (composant PARTAGÉ par les
   deux boîtes) : `opacity: root.folded ? 0.3 : 1.0`, animation 250 ms.
   Cette valeur est RELATIVE à la boîte, pas absolue : Qt Quick multiplie les
   opacités comme CSS et la rangée est imbriquée dans la boîte. Le résultat
   officiel est donc 0.72 × 0.3 = 0.216 pour un adversaire et 0.78 × 0.3 =
   0.234 pour soi — et non 0.30, comme je l'avais d'abord calculé.
   Une seule règle pour tout le monde : la self est incluse (parité totale
   demandée le 28/07), c'est déjà ce que fait le QML puisque le composant est
   partagé et reçoit `folded: root.folded` des deux côtés. */
html[data-seat-struct="qml"] .seat.folded .seat-holecards {
  opacity: 0.3;
  transition: opacity 0.25s ease-out;
}
/* Mise (pack PokerTH) : icône jeton à gauche du montant, comme le BetChip du
   client officiel (chipStack.svg). On garde la pastille de base (fond sombre,
   montant crème) et on préfixe juste le jeton PokerTH. */
/* BetChip QML : icône chipStack.svg 20×20 + « $X » 14px bold #f0f0f0,
   espacement 2, SANS pastille de fond (le composant officiel n'en a pas).
   Pop 0.5→1.15→1.0 via la propriété `scale` (indépendante du transform de
   positionnement betside-*). Icône surchargeable par thème (--bet-chip-img). */
html[data-seat-struct="qml"] .seat-bet {
  display: inline-flex; align-items: center; gap: var(--sp-0);
  padding: 0; background: none; border: none;
  /* BetChip.qml : textColor #eff1f5 (et non #f0f0f0 — écart relevé à l'audit
     du siège contre l'AppImage 2.1.4, imperceptible mais autant être exact). */
  color: #eff1f5; font-family: var(--ff-display);
  font-size: 14px !important; font-weight: 700;
  animation: betChipPop 0.24s ease-out;
}
html[data-seat-struct="qml"] .seat-bet::before {
  content: ""; width: 20px; height: 20px; flex: none;
  background: var(--bet-chip-img, url('/pucks/chipStack.svg')) center / contain no-repeat;
}
@keyframes betChipPop { 0% { scale: 0.5; } 46% { scale: 1.15; } 100% { scale: 1; } }
/* Mise HORS de la boîte (packs PokerTH) : la pastille jeton+montant est posée
   sur le côté de la box, côté calculé en JS (classe betside-* sur .seat).
   .seat-bet devient absolu (ancré à .seat = la box), sorti du flux colonne. */
html[data-seat-struct="qml"] .seat[class*="betside-"] .seat-bet {
  position: absolute; z-index: 6; margin: 0;
}
html[data-seat-struct="qml"] .seat.betside-r .seat-bet { left: calc(100% + var(--seat-bet-gap-r, 8px)); top: 50%; transform: translateY(-50%); }
html[data-seat-struct="qml"] .seat.betside-l .seat-bet { right: calc(100% + var(--seat-bet-gap-l, 8px)); top: 50%; transform: translateY(-50%); }
html[data-seat-struct="qml"] .seat.betside-t .seat-bet { bottom: calc(100% + var(--seat-bet-gap-t, 7px)); left: 50%; transform: translateX(-50%); }
html[data-seat-struct="qml"] .seat.betside-b .seat-bet { top: calc(100% + var(--seat-bet-gap-b, 7px)); left: 50%; transform: translateX(-50%); }
/* Self : sa mise est dans la barre d'action -> pas de jeton de siège (officiel). */
html[data-seat-struct="qml"] .seat.me .seat-bet { display: none; }

/* ── Style de mise « inset » (SeatStyle/PlayerBetStrip QML, upstream 414a89c) :
   la mise est posée dans un SOCLE en bas de la boîte, À L'INTÉRIEUR du cadre.
   html[data-bet="inset|classic"] est posé par _applyBetStyle (pokerth.js)
   selon pth_bet_style ; vide = défaut plateforme (desktop inset, mobile
   classic — Qt.platform.os côté QML, pointer:coarse côté web). La PLACE du
   socle est réservée en PERMANENCE sous la boîte (padding-bottom sur .seat,
   rendu quand le socle s'ouvre) : comme la réservation tableZone.betStripH du
   QML, l'ouverture ne déplace ni les voisins ni l'autofit — la hauteur totale
   du siège reste constante, seul le corps visible grandit. Dépli 140 ms
   ease-out (Behavior on height, Easing.OutQuad). Le puck D/SB/BB reste HORS
   de la boîte dans les DEUX styles (la classe betside-* reste posée). */
html[data-seat-struct="qml"][data-bet="inset"] .seat {
  padding-bottom: 20px;
  transition: padding-bottom 0.14s ease-out;
}
html[data-seat-struct="qml"][data-bet="inset"] .seat.socle-open { padding-bottom: 0; }
/* Socle (PlayerBetStrip) : pleine largeur de la plaque (grille 2 colonnes),
   rejoint le cadre à 1 px près (padding plate 3px 5px → marges -4/-2), fond
   rgba(0,0,0,0.26), trait de séparation 1 px blanc 10 %, coins bas arrondis
   sous le rayon de la plaque. Hauteur QML betStripHeight = 20 (le trait est
   DANS ces 20 px : box-sizing border-box). */
html[data-seat-struct="qml"] .seat-bet-socle {
  grid-column: 1 / -1;
  box-sizing: border-box;
  height: 20px; overflow: hidden;
  margin: 0 -4px -2px;
  background: rgba(0, 0, 0, 0.26);
  border-top: 1px solid rgba(255, 255, 255, 0.10);
  border-bottom-left-radius: calc(var(--r-sm, 4px) - 1px);
  border-bottom-right-radius: calc(var(--r-sm, 4px) - 1px);
  display: flex; align-items: center; justify-content: center;
  animation: socleOpen 0.14s ease-out;
}
@keyframes socleOpen { from { height: 0; margin-bottom: 18px; } to { height: 20px; margin-bottom: -2px; } }
/* Anti-replay : la mise change sans être apparue -> pas de ré-animation du
   dépli (le pop du BetChip garde sa propre garde .no-pop). */
html[data-seat-struct="qml"] .seat-bet-socle.no-open { animation: none; }
/* BetChip du socle : icône 15, texte 13 (BetChip.iconSize/fontSize dans
   PlayerBetStrip), position STATIQUE (annule le positionnement betside
   absolu, la classe betside-* restant posée pour les pucks) et visible aussi
   pour la self-box (annule .seat.me .seat-bet { display: none }). */
html[data-seat-struct="qml"] .seat .seat-bet-socle .seat-bet,
html[data-seat-struct="qml"] .seat.me .seat-bet-socle .seat-bet {
  position: static; transform: none; margin: 0;
  display: inline-flex;
  font-size: 13px !important;
}
html[data-seat-struct="qml"] .seat .seat-bet-socle .seat-bet::before { width: 15px; height: 15px; }

/* Pucks D/SB/BB (packs PokerTH) : sortis de l'avatar (JS), posés sur le CÔTÉ de
   la boîte (même betSide que le jeton), un peu plus bas pour ne pas chevaucher la
   mise. On remet à plat le positionnement coin-avatar des chips (qui utilise
   !important dans les media-queries) et on les met en ligne. */
html[data-seat-struct="qml"] .seat-pucks {
  position: absolute; z-index: 6; display: flex; gap: var(--sp-0); align-items: center;
  pointer-events: none;
}
html[data-seat-struct="qml"] .seat-pucks .dealer-chip,
html[data-seat-struct="qml"] .seat-pucks .blind-chip {
  position: static !important; top: auto !important; right: auto !important;
  bottom: auto !important; left: auto !important; margin: 0 !important;
  width: 32px !important; height: 32px !important;   /* BlindButtonImage QML 32×32 */
}
/* Côtés (betSide left/right) : puck SOUS la mise. Formule universelle :
   ancré sur le CENTRE DE LA MISE (50%) + dégagement (--seat-bet-clearance,
   36px = demi-mise + demi-puck + marge) -> aucun chevauchement quelle que
   soit la hauteur de la boîte (gabarit QML 84px, pilule 44px, etc.).
   Surcharge possible par pack : --seat-pucks-top. */
html[data-seat-struct="qml"] .seat.betside-r .seat-pucks { left: calc(100% + var(--seat-bet-gap-r, 8px)); top: var(--seat-pucks-top, calc(50% + var(--seat-bet-clearance, 36px))); transform: translateY(-50%); }
html[data-seat-struct="qml"] .seat.betside-l .seat-pucks { right: calc(100% + var(--seat-bet-gap-l, 8px)); top: var(--seat-pucks-top, calc(50% + var(--seat-bet-clearance, 36px))); transform: translateY(-50%); }
/* Haut/bas : puck aligné à DROITE, 6px du bord de la boîte (QML), même bande
   verticale que la mise (centre puck 32 ≈ centre mise 20 posée à 7px). */
html[data-seat-struct="qml"] .seat.betside-t .seat-pucks { bottom: calc(100% + 1px); right: 6px; left: auto; }
html[data-seat-struct="qml"] .seat.betside-b .seat-pucks { top: calc(100% + 1px); right: 6px; left: auto; }
/* betSplit (QML 2.1.8, upstream 9f402258) : mise à DROITE de la boîte (+8),
   pucks à GAUCHE (−8), centrés verticalement — box haut-centre en paysage et
   siège bas-centre du spectateur (classe betside-split posée par le JS). */
html[data-seat-struct="qml"] .seat.betside-split .seat-bet { left: calc(100% + var(--seat-bet-gap-r, 8px)); right: auto; top: 50%; bottom: auto; transform: translateY(-50%); }
html[data-seat-struct="qml"] .seat.betside-split .seat-pucks { right: calc(100% + var(--seat-bet-gap-l, 8px)); left: auto; top: 50%; bottom: auto; transform: translateY(-50%); }
/* Socle inset × betside-split : la règle split ci-dessus gagnait la cascade
   (spécificité égale, ordre postérieur) sur la remise à plat du BetChip du
   socle (plus haut dans le fichier) et posait son translateY(-50%) sur le
   chip DU SOCLE — left/top ignorés (position:static) mais le TRANSFORM
   s'applique → le chip remontait d'une demi-hauteur dans le ruban
   overflow:hidden de 20 px : mise du siège HAUT-CENTRE (et du spectateur
   bas-centre) « mangée » (rapport narmod 03/09). Le QML n'a pas ce cas :
   PlayerBetStrip (inset) ignore betSide, betSplit ne concerne que le jeton
   classic. On re-neutralise APRÈS la règle split, pour tous les axes. */
html[data-seat-struct="qml"] .seat.betside-split .seat-bet-socle .seat-bet {
  position: static; left: auto; right: auto; top: auto; bottom: auto;
  transform: none; margin: 0;
}
/* Sitz-Stil inset (QML : betInset → puck vertical mittig) : la mise vit dans
   le socle, le slot latéral est libre → le puck se centre verticalement à
   côté de la boîte. Classic garde le slot bas (dégagement sous la mise). */
html[data-bet="inset"][data-seat-struct="qml"] .seat.betside-r .seat-pucks,
html[data-bet="inset"][data-seat-struct="qml"] .seat.betside-l .seat-pucks { top: var(--seat-pucks-top, 50%); }
html[data-seat-struct="qml"] .seat-timer-badge { display: none; }
/* Countdown RECTANGULAIRE (style pokerth) : cadre depletif autour de la boite,
   remplace l'anneau rond .seat-timer. Couleurs officielles 2.1.1. */
html[data-seat-struct="qml"] .seat-avatar .seat-timer { display: none; }
html[data-seat-struct="qml"] .seat-timer-rect {
  position: absolute; inset: -3px;
  width: calc(100% + 6px); height: calc(100% + 6px);
  pointer-events: none; overflow: visible; z-index: 3;
}
html[data-seat-struct="qml"] .seat-timer-rect .rt-bg  { fill: none; stroke: #0e1a30; stroke-width: 3; vector-effect: non-scaling-stroke; }
html[data-seat-struct="qml"] .seat-timer-rect .rt-arc { fill: none; stroke-width: 3; stroke-linecap: round; vector-effect: non-scaling-stroke; transition: stroke-dashoffset 0.9s linear; }


/* ===== Sieges : packs IMAGE importes (galerie, html[data-seat-img]) =======
   Un pack image = une plaque 9-slice (border-image ; le mot-cle `fill` fait que
   l'image peint AUSSI le fond de la boite) posee sur .seat-plate. Variables
   injectees par theme.mjs (_injectSeatPkg) ; restituees zero-flash par le
   snippet <head>. Meme boite que le pack « Plaque » integre, mais cadre
   graphique au lieu de background/bordure CSS. id non liste = repli Classique.
   Variante « moi » optionnelle (self.*) -> cadre aussi la barre du heros. */
html[data-seat-img] .seat { max-width: none; }
html[data-seat-img] .seat-plate {
  /* structure = grille QML (data-seat-struct) ; ici seulement le CADRE 9-slice */
  position: relative;
  padding: var(--seat-pkg-pad, 6px 12px);
  border-style: solid; border-color: transparent;
  border-width: var(--seat-pkg-width, 15px);
  border-image-source: var(--seat-pkg-img);
  border-image-slice: var(--seat-pkg-slice, 34%) fill;
  border-image-width: var(--seat-pkg-width, 15px);
  border-image-repeat: stretch;
}
html[data-seat-img] .seat-name { text-align: left; max-width: 88px; }
html[data-seat-img] .seat-foot {
  display: flex; align-items: center; justify-content: center; gap: var(--sp-2); flex-wrap: wrap; margin-top: 3px;
}
html[data-seat-img] .seat.active .seat-plate {
  box-shadow: 0 0 0 1px var(--gold), 0 0 16px rgba(var(--gold-rgb), 0.45);
}
html[data-seat-img] .seat-timer-badge { display: none; }
/* Variante « moi » : la barre du heros recoit le meme cadre (self.* fourni). */
html[data-seat-img-self] .player-bar {
  border-style: solid; border-color: transparent;
  border-width: var(--seat-pkg-self-width, 15px);
  border-image-source: var(--seat-pkg-self-img);
  border-image-slice: var(--seat-pkg-self-slice, 34%) fill;
  border-image-width: var(--seat-pkg-self-width, 15px);
  border-image-repeat: stretch;
}
@media (max-width: 600px) {
  html[data-seat-img] .seat-name { max-width: 46px; font-size: var(--fs-2xs); }
  html[data-seat-img] .seat-money { font-size: var(--fs-2xs); }
  html[data-seat-img] .seat-foot { gap: var(--sp-1); margin-top: 2px; }
}


/* ════════════════════════════════════════════════════════════════════════
   FENETRES FLOTTANTES — chat / journal / reactions deplacables & redimension-
   nables. La classe .floating-win est posee par le JS UNIQUEMENT au-dessus du
   seuil (>= 900 px ; chat = souris/pointer:fine en plus). En dessous, aucun de
   ces styles ne s'applique : les panneaux restent en bandeau pleine largeur.
   Position/taille pilotees en inline par le JS (left/top/width/height).
   ════════════════════════════════════════════════════════════════════════ */
#g-chat-panel.floating-win,
#lobby-chat-panel.floating-win,
#g-log-panel.floating-win,
#g-reaction-panel.floating-win,
#hands-card-inner.floating-win {
  border: 1px solid var(--gold-dim);
  border-radius: var(--r-bar);
  box-shadow: 0 14px 36px rgba(0,0,0,0.62);
  overflow: hidden;            /* coins arrondis nets + borne les poignees */
}
/* Fenêtre Combinaisons alignée sur les autres fenêtres flottantes */
#hands-card-inner.floating-win {
  position: fixed !important;   /* indispensable pour _placeWin/drag (l'overlay ne le centre plus) */
  padding: 0; border-top-width: 1px; max-width: none; max-height: none;
  display: flex; flex-direction: column; background: var(--chatlog-bg, var(--modal-bg));
  z-index: 201;
}
/* Aucun palier de taille ici : l'echelle du contenu est assuree UNIQUEMENT par
   le zoom generique des fenetres flottantes (.win-zoom / --wz), continu et
   recalcule a chaque mouvement du redimensionnement — comme le chat, le journal
   et les reactions. Empiler des paliers discrets par-dessus ce zoom produisait
   une « marche d'escalier » : les seuils se comparaient a offsetWidth (largeur
   EXTERIEURE) alors que le contenu est rendu en coordonnees locales divisees
   par z, si bien qu'un redimensionnement diagonal laissait la place locale
   quasi constante (~380 px) tout en faisant sauter la typo trois fois. */
#hands-card-inner.floating-win .hands-scroll {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
  -webkit-overflow-scrolling: touch; padding: 10px 12px;   /* = .chat-msgs */
}
/* Chat du lobby flottant : la zone messages remplit la fenetre (annule le
   max-height:120px inline du mode accroche). */
#lobby-chat-panel.floating-win .chat-msgs { max-height: none !important; flex: 1 1 auto; min-height: 0; }
/* La barre de titre devient la poignee de deplacement. */
.floating-win .g-chat-panel-header,
.floating-win .react-panel-title { cursor: move; touch-action: none; -webkit-user-select: none; user-select: none; }
.floating-win .g-chat-panel-header.win-dragging,
.floating-win .react-panel-title.win-dragging { cursor: grabbing; }
/* Le resize vertical historique (bandeau) ne sert plus en mode flottant. */
.floating-win .chat-resize-handle { display: none !important; }
/* Reactions : la grille remplit la fenetre, defile, et s'adapte a SA largeur
   (et non plus a celle de l'ecran) via auto-fit. */
#g-reaction-panel.floating-win { display: flex; flex-direction: column; }
#g-reaction-panel.floating-win .react-grid {
  grid-template-columns: repeat(auto-fit, minmax(44px, 1fr));
  max-width: none; margin: 0;
  flex: 1 1 auto; min-height: 0; overflow: auto;
  /* Téléphone (<600px) : le média impose max-height:88px (~2 lignes) à .react-grid.
     En fenêtre flottante on l'annule pour que la grille remplisse la fenêtre et
     défile normalement (comme desktop/tablette où ce plafond n'existe pas). */
  max-height: none;
}
/* Taille des émojis (et du compteur) pilotée par la case calculée en JS
   (_fitReactGrid, variable --react-cell) : les glyphes grossissent/rétrécissent
   avec la fenêtre. Repli 44px si la variable n'est pas encore posée. */
#g-reaction-panel.floating-win .react-grid .react-btn {
  font-size: calc(var(--react-cell, 44px) * 0.55);
}
#g-reaction-panel.floating-win .react-grid .react-btn .rc {
  font-size: calc(var(--react-cell, 44px) * 0.17);
}
/* 8 poignees de redimensionnement (bords + coins), injectees en JS.
   Transparentes : seul le curseur signale la zone. Placees sur les bords
   interieurs car le panneau est en overflow:hidden. */
.win-rsz { position: absolute; z-index: 6; touch-action: none; }
.win-rsz-n  { top: 0; left: 12px; right: 12px; height: 6px;  cursor: ns-resize; }
.win-rsz-s  { bottom: 0; left: 12px; right: 12px; height: 6px;  cursor: ns-resize; }
.win-rsz-e  { right: 0; top: 12px; bottom: 12px; width: 6px;  cursor: ew-resize; }
.win-rsz-w  { left: 0; top: 12px; bottom: 12px; width: 6px;  cursor: ew-resize; }
.win-rsz-ne { top: 0; right: 0; width: 13px; height: 13px; cursor: nesw-resize; }
.win-rsz-nw { top: 0; left: 0; width: 13px; height: 13px; cursor: nwse-resize; }
.win-rsz-se { bottom: 0; right: 0; width: 18px; height: 18px; cursor: nwse-resize; }
/* Grip visible du coin bas-droit (demande narmod 2026-07-19) : hachures
   diagonales classiques dans un triangle, or discret, plus frappe au survol.
   La zone SE passe de 13 a 18 px (plus facile a attraper). Les autres bords
   restent des zones invisibles (comportement OS standard). */
.win-rsz-se::after { content: ''; position: absolute; right: 3px; bottom: 3px; width: 13px; height: 13px;
  background: repeating-linear-gradient(-45deg, transparent 0 3px, var(--gold-dim, #b09a00) 3px 5px);
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
  opacity: .8; pointer-events: none; transition: opacity .12s; } /* .55->.8 + 11->13 px : plus lisible sur fond clair (demande narmod 2026-07-19) */
.win-rsz-se:hover::after, .win-rsz-se:active::after { opacity: 1; }
.win-rsz-sw { bottom: 0; left: 0; width: 13px; height: 13px; cursor: nwse-resize; }
.win-resizing, .win-resizing * { -webkit-user-select: none; user-select: none; }


/* ════════════════════════════════════════════════════════════════════════
   Ouverture EN BANDEAU + detachement au 1er drag/resize : geree cote JS
   (_attachFloatControls / _ensureFloating). Les styles .floating-win et
   .win-rsz sont definis plus haut (v0.2.385) et s'appliquent au detachement.
   ════════════════════════════════════════════════════════════════════════ */

/* Bouton "reset des fenetres" du header : visible seulement tablette + desktop.
   Selecteur sur-specifie (#s-game .header .btn-sm.win-reset-only) pour battre
   `.header .btn-sm` (base) et `#s-game .header .btn-sm` (@media max-740) qui
   forcaient sinon display:inline-flex partout, mobile compris. */
#s-game .header .btn-sm.win-reset-only { display: none; }
@media (min-width: 900px) and (min-height: 600px) { #s-game .header .btn-sm.win-reset-only { display: inline-flex; } }

/* Bouton reset des fenetres dans le LOBBY : desktop SOURIS uniquement
   (exclu tablette/mobile via hover + pointer:fine), comme le gate du chat lobby. */
#s-lobby .header .btn-sm.win-reset-lobby { display: none; }
@media (min-width: 900px) and (hover: hover) and (pointer: fine) { #s-lobby .header .btn-sm.win-reset-lobby { display: inline-flex; } }






/* ════════════════════════════════════════════════════════════════════════
   Raccourcis clavier discrets dans les boutons d'action — DESKTOP UNIQUEMENT
   (souris : hover + pointeur fin) ; masques sur tablette/telephone tactiles.
   Les touches F / C / R / A (+ Espace pour Call/Check) sont DEJA actives ;
   ceci ne fait que les afficher. Pave encadre en haut a droite, dessine en
   currentColor (s'adapte a la couleur de chaque bouton) et attenue.
   ════════════════════════════════════════════════════════════════════════ */
.act-key {
  display: none;
  position: absolute; top: 3px; right: 4px;
  font-family: var(--ff-mono); font-size: var(--fs-2xs); line-height: 1;
  font-weight: 600; letter-spacing: 0;
  padding: 1px 4px; border: 1px solid currentColor; border-radius: var(--r-xs);
  opacity: 0.4; pointer-events: none;
}
/* Revele : (a) desktop a la souris (hover + pointeur fin), tout de suite */
@media (hover: hover) and (pointer: fine) {
  .btn-action, .btn-pct { position: relative; }
  .act-key { display: inline-block; }
}
/* (b) ou des qu'un clavier physique est detecte (classe posee par le JS) */
body.has-keyboard .btn-action, body.has-keyboard .btn-pct { position: relative; }
body.has-keyboard .act-key { display: inline-block; }
/* Le bouton Tapis est etroit : le badge de touche « A » chevauchait le texte
   (« Tapís »). Le QML n'affiche aucun badge sur les boutons -> on le retire ici. */
.btn-allin .act-key,
.btn-pct .act-key { display: none !important; } /* pas de badge de touche sur Tapis ni bets rapides (parite QML) */

/* iOS Safari : empeche le zoom auto au focus d'un champ (declenche quand
   font-size < 16px). Force a 16px sur tactile uniquement ; le pinch-zoom
   reste actif (viewport intact). .raise-input exclu pour preserver la
   table compacte (slider + boutons 1/3.1/2.Pot disponibles). */
@media (pointer: coarse) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="color"]):not(.raise-input),
  textarea,
  select { font-size: 16px !important; }
}


/* ── Zoom de la table (boutons +/- en haut a droite) ──────────────────────
   Visible uniquement tablette/desktop. Reduit/re-agrandit #g-table-zone
   (feutre + cartes communes + adversaires) ; max = taille actuelle (1.0). */
.zoom-ctrl {
  position: absolute; top: 8px; right: 8px; z-index: 30;
  display: flex; flex-direction: column; gap: var(--sp-2); /* visible sur tous les appareils */
}
@media (min-width: 900px) and (min-height: 600px) {
  .zoom-ctrl { display: flex; }
}
.zoom-btn {
  width: 30px; height: 30px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border-radius: 8px; cursor: pointer;
  background: rgba(20, 28, 24, 0.72); color: #e8e8e8;
  border: 1px solid rgba(255, 255, 255, 0.18);
  font-size: 1.2rem; font-weight: 600; line-height: 1;
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  transition: background 0.15s, opacity 0.15s;
}
.zoom-btn:hover { background: rgba(40, 52, 46, 0.9); }
.zoom-btn:active { transform: scale(0.94); }
.zoom-btn:disabled { opacity: 0.35; cursor: default; }
/* Boutons ronds posés sur le tapis (chat+emoji haut-gauche, journal haut-droite) */
.g-felt-corner { position:absolute; z-index:30; display:flex; gap:var(--sp-2); }
.g-felt-tl { top:8px; left:8px; }
.g-felt-tr { top:8px; right:8px; }
.g-felt-btn { position:relative; width:34px; height:34px; padding:0; border-radius:50%;
  display:flex; align-items:center; justify-content:center; cursor:pointer;
  background:rgba(20,28,24,0.72); color:#e8e8e8; border:1px solid rgba(255,255,255,0.18);
  font-size:var(--fs-lg); line-height:1; -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px);
  transition:background .15s, transform .1s; }
.g-felt-btn:hover { background:rgba(40,52,46,0.9); }
.g-felt-btn:active { transform:scale(0.94); }
.g-felt-btn.active { background:var(--accent,#E3C800); color:#1a1205; border-color:var(--accent,#E3C800); }
/* Bouton emoji grisé quand les réactions sont désactivées (mute) */
.g-felt-btn.muted { opacity:0.4; filter:grayscale(1); }
/* Le zoom passe SOUS le journal (haut-droite) pour ne pas le chevaucher */
.zoom-ctrl { top:50px; }
/* Zoom de table RETIRÉ (demande narmod) : rendu incohérent selon
   navigateur/OS — le placement redevient strictement QML partout.
   🔍/+/↺/− masqués ; ✎ (édition custom, régi par ses propres règles #id)
   et le zoom de cartes restent visibles en permanence. */
/* 🔍 = LOUPE QML (zoomLayer ×2, pan, follow) — visibilité STRICTE 2.1.3 :
   mobile (pointer:coarse) + compact + option tableZoomEnabled, gérée par
   _loupeBtnSync (classe .loupe-avail). Pastille ronde 36 px en bas-droite,
   or quand active (GamePage 2.1.3:2283-2325). +/↺/− restent retirés. */
#g-zoom-in, #g-zoom-reset, #g-zoom-out { display:none !important; }
#g-zoom-toggle { display: none !important; }
#g-zoom-toggle.loupe-avail {
  display: flex !important;
  position: fixed; right: 8px; bottom: 8px; z-index: 200;
  width: 36px; height: 36px; border-radius: 18px;
  background: rgba(0, 0, 0, 0.5); color: #fff;
  border: none; font-size: var(--fs-lg);
}
#g-zoom-toggle.loupe-avail.active {
  background: var(--accent, #E3C800); color: #101010;
}
/* Couche zoomable (parité zoomLayer QML) : mêmes règles de centrage que
   .table-zone ; le transform est posé par _loupeApply (transition 220ms
   OutCubic hors drag). */
#g-zoom-layer { position:absolute; inset:0; display:flex; align-items:center;
  justify-content:center; overflow:visible; will-change:transform; }
.zoom-ctrl .zoom-btn:not(.zoom-toggle) { display:flex; }
.zoom-btn.active { background: var(--accent, #E3C800); color: #1a1205; border-color: var(--accent, #E3C800); }

/* ═══ État « fenêtre ouverte » — convention unique de toute l'app ═══════
   Tout bouton qui a ouvert une fenêtre, un panneau ou un menu reste en or
   tant que celle-ci est visible (registre + observers : _WIN_BTN dans
   pokerth.js). Trois rendus :
     · .win-open           frameless (en-têtes, ☰, ⛶, ⚙/⋮) → icône en or,
                           aucun cadre ajouté (demande narmod : lisible sur
                           les systèmes où l'icône neutre se voit mal)
     · .g-felt-btn/.zoom-btn.win-open  boutons ronds du feutre → aplat or,
                           même rendu que .active (💬 📋 😊 harmonisés)
   Toujours actif : l'option des Options avancées a été retirée le 30/07/2026
   (comportement permanent). La règle body.adv-no-winopen est conservée, plus
   aucune classe ne la déclenche. */
.win-open { color: var(--sel, var(--gold, #E3C800)) !important; }
.g-felt-btn.win-open,
.zoom-btn.win-open {
  background: var(--sel, var(--gold, #E3C800)) !important;
  border-color: var(--sel, var(--gold, #E3C800)) !important;
  color: #1a1205 !important;
}
body.adv-no-winopen .win-open {
  color: inherit !important; background: none !important;
  border-color: transparent !important; box-shadow: none !important;
}

/* -- Bouton "editer le placement" (mode custom) + edition glisser-deposer -- */
#g-seat-edit { display: none; }
html[data-seat-layout="custom"] #g-seat-edit { display: flex; }
#g-seat-edit.active { background: #E3C800; color: #1d222b; border-color: #E3C800; }
html[data-seat-edit="1"] { -webkit-user-select: none; user-select: none; }
html[data-seat-edit="1"] #g-seats .seat[data-pid] { cursor: grab; touch-action: none; outline: 2px dashed rgba(227, 200, 0, 0.75); outline-offset: 2px; border-radius: var(--r-bar); }
html[data-seat-edit="1"] #g-seats .seat.seat-ghost { outline: none; cursor: default; }
#g-seats .seat.seat-dragging { cursor: grabbing; z-index: 50; }
.seat-edit-banner {
  position: fixed; left: 50%; bottom: 16px; transform: translateX(-50%);
  z-index: 60; display: flex; align-items: center; gap: var(--sp-4);
  padding: 8px 12px; border-radius: var(--r-lg); max-width: calc(100vw - 24px);
  background: rgba(20, 28, 24, 0.92); color: #eff1f5;
  border: 1px solid rgba(227, 200, 0, 0.55);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  font-size: var(--fs-md); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}
.seat-edit-banner .seb-hint { opacity: 0.92; }
.seat-edit-banner button {
  border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 8px;
  padding: 5px 10px; cursor: pointer; font-weight: 600; font-size: var(--fs-base);
  background: rgba(255, 255, 255, 0.08); color: #eff1f5;
}
.seat-edit-banner .seb-done { background: #E3C800; color: #1d222b; border-color: #E3C800; }


/* ── Selecteur d'emoji du chat (desktop / souris) ───────────────────
   Bouton dans la barre de saisie + grille repliable au-dessus. Un clic insere
   l'emoji dans #g-chat-in (envoye tel quel : texte unicode). Mobile -> masque
   (le clavier natif propose deja les emojis). */
.chat-emoji-toggle {
  display: none;            /* desktop (souris) uniquement -- voir media query */
  align-items: center; justify-content: center;
  background: var(--inset); color: var(--cream);
  border: 1px solid var(--border); border-radius: 2px;
  padding: 0 9px; font-size: var(--fs-md); line-height: 1; cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
@media (hover: hover) and (pointer: fine) {
  .chat-emoji-toggle { display: flex; }
}
.chat-emoji-toggle:hover { background: rgba(var(--gold-rgb), 0.15); border-color: var(--gold-dim); }
.chat-emoji-toggle.active { background: rgba(var(--sel-rgb,var(--gold-rgb)), 0.22); border-color: var(--gold-dim); }
.chat-emoji-panel {
  grid-template-columns: repeat(auto-fill, minmax(32px, 1fr));
  gap: var(--sp-0);
  max-height: 80px;
  overflow-y: auto;
  padding: 6px 8px;
  border-top: 1px solid var(--border);
  background: var(--panel);
  flex-shrink: 0;
}
.chat-emoji-btn {
  aspect-ratio: 1; width: 100%;
  background: none; border: none; border-radius: 5px;
  font-size: 1.25rem; line-height: 1; cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
  transition: transform 0.1s ease, background 0.1s;
}
.chat-emoji-btn:hover { background: rgba(var(--gold-rgb), 0.15); transform: scale(1.18); }
.chat-emoji-btn:active { transform: scale(0.9); }

/* ════════════════════════════════════════════════════════════════════════
   PANNEAU MUSIQUE — fenetre flottante globale (accueil + lobby + jeu).
   Deplacable + redimensionnable >= 900px via le systeme de fenetres existant
   (_attachFloatControls + .floating-win). Sous le seuil : feuille fixe en bas
   d'ecran, non deplacable. z-index 900 : au-dessus du jeu, sous les modales
   (9999). Bloc purement additif — aucune regle partagee n'est modifiee.
   ════════════════════════════════════════════════════════════════════════ */
#music-panel {
  position: fixed;
  z-index: 100;
  top: 58px;
  right: 14px;
  width: min(340px, 92vw);
  max-height: min(72dvh, 560px);
  display: none;                 /* bascule en flex par le JS */
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--gold-dim);
  border-radius: var(--r-lg);
  box-shadow: 0 14px 36px rgba(0,0,0,0.62);
  overflow: hidden;              /* coins nets + borne les poignees de resize */
}
.music-panel-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 9px 12px;
  font-size: var(--fs-2xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold-dim);
  background: var(--chrome-tint);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  -webkit-user-select: none;
  user-select: none;
}
.music-panel-title > span { display: inline-flex; align-items: center; gap: var(--sp-2); }
.music-panel-close {
  background: none; border: none; color: var(--text);
  cursor: pointer; font-size: var(--fs-md); line-height: 1; padding: 0 2px;
}
.music-body {
  padding: 12px 14px 14px;
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.music-foot { flex: 0 0 auto; border-top: 1px solid var(--gold-dim); padding: 11px 14px; }
.music-foot-row { display: flex; align-items: center; gap: var(--sp-4); cursor: pointer; color: var(--text); font-size: var(--fs-md); user-select: none; }
.music-foot-lbl { display: inline-flex; align-items: center; }
.music-foot-row input[type=checkbox] { width: 18px; height: 18px; accent-color: var(--gold); cursor: pointer; flex: 0 0 auto; }
.music-empty { color: var(--gold-dim); font-size: var(--fs-base); text-align: center; padding: 18px 4px; }
.music-now { display: flex; flex-direction: column; gap: var(--sp-0); }
.music-now-label { font-size: var(--fs-3xs); letter-spacing: 0.14em; text-transform: uppercase; color: var(--gold-dim); }
.music-now-title { font-size: var(--fs-md); color: var(--text); font-weight: 600; }
.music-transport { display: flex; flex-direction: column; align-items: center; gap: var(--sp-4); }
.music-trow { display: flex; align-items: center; justify-content: center; gap: var(--sp-4); }
.music-tbtn {
  flex: 0 0 auto;
  width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 1;
  background: var(--field-bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 50%;
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease, transform 90ms ease;
}
.music-tbtn:active:not([disabled]) { transform: scale(0.94); }
.music-tbtn svg { width: 19px; height: 19px; display: block; pointer-events: none; }
.music-tbtn:hover:not([disabled]) { border-color: var(--gold-dim); color: var(--gold); }
.music-tbtn[disabled] { opacity: 0.4; cursor: default; }
.music-tbtn-main { width: 52px; height: 52px; color: var(--gold); border-color: var(--gold-dim); background: rgba(var(--gold-rgb),0.10); }
.music-tbtn-main svg { width: 25px; height: 25px; }
.music-tbtn.is-active { color: var(--sel,var(--gold)); border-color: var(--sel,var(--gold)); background: rgba(var(--sel-rgb,var(--gold-rgb)),0.16); }
.music-div { width: 1px; align-self: stretch; background: var(--border); margin: 4px 2px; }
.music-vol { display: flex; align-items: center; gap: var(--sp-3); }
.music-vol-ic { flex: 0 0 auto; color: var(--gold-dim); display: inline-flex; }
.music-vol-ic svg { width: 18px; height: 18px; display: block; }
.music-vol-range { flex: 1 1 auto; min-width: 0; accent-color: var(--gold); }
.music-vol-val { flex: 0 0 auto; font-size: var(--fs-xs); color: var(--gold-dim); min-width: 34px; text-align: right; }
.music-seek-row { display: flex; align-items: center; gap: var(--sp-3); }
.music-seek { flex: 1 1 auto; min-width: 0; accent-color: var(--gold); cursor: pointer; }
.music-seek[disabled] { opacity: 0.45; cursor: default; }
.music-time { flex: 0 0 auto; font-size: var(--fs-xs); color: var(--gold-dim); min-width: 36px; font-variant-numeric: tabular-nums; }
.music-cur { text-align: right; }
.music-dur { text-align: left; }
/* ── Section « Sons du jeu » en haut du lecteur (visuellement distincte) ── */
.music-gamesnd { border: 1px solid var(--border-hi); background: rgba(var(--gold-rgb),0.06); border-radius: var(--r-bar); padding: 8px 10px; display: flex; flex-direction: column; gap: var(--sp-2); }
.music-gamesnd-hd { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-3xs); letter-spacing: 0.12em; text-transform: uppercase; color: var(--gold-dim); }
.music-gamesnd-ic { font-size: var(--fs-base); }
.music-gamesnd-row { display: flex; align-items: center; gap: var(--sp-3); }
.music-gs-mute { flex: 0 0 auto; width: 32px; height: 32px; font-size: var(--fs-md); }
.music-gs-range { flex: 1 1 auto; min-width: 0; accent-color: var(--gold); cursor: pointer; }
.music-gs-val { flex: 0 0 auto; font-size: var(--fs-xs); color: var(--gold-dim); min-width: 34px; text-align: right; font-variant-numeric: tabular-nums; }
.music-gamesnd-row.is-off .music-gs-range { opacity: 0.4; }
.music-gamesnd-row.is-off .music-gs-val { opacity: 0.5; }
/* ── Box jumelle « Lecteur » qui regroupe les commandes musique ── */
.music-player-box { border: 1px solid var(--border-hi); background: rgba(var(--gold-rgb),0.06); border-radius: var(--r-bar); padding: 8px 10px 10px; display: flex; flex-direction: column; gap: var(--sp-4); }
.music-player-hd { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-3xs); letter-spacing: 0.12em; text-transform: uppercase; color: var(--gold-dim); }
.music-player-ic { font-size: var(--fs-base); }

/* ══ Lecteur « esprit Winamp » — LCD, VU, marquee, playlist (palette de thème) ══ */
.music-lcd {
  border: 1px solid var(--border-hi);
  background: var(--field-bg);
  border-radius: 8px;
  padding: 7px 10px 8px;
  box-shadow: inset 0 1px 3px rgba(0,0,0,0.22);
  display: flex; flex-direction: column; gap: var(--sp-1);
}
.music-lcd-top { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); }
.music-lcd .music-cur {
  font-size: 1.15rem; font-weight: 700; color: var(--gold);
  cursor: pointer; text-align: left; min-width: 0; line-height: 1;
  font-variant-numeric: tabular-nums; letter-spacing: 0.02em;
}
.music-lcd .music-cur:hover { color: var(--sel, var(--gold)); }
.music-vu { display: inline-flex; align-items: flex-end; gap: var(--sp-0); height: 16px; flex: 0 0 auto; }
.music-vu-bar {
  width: 3px; height: 3px; border-radius: 1px;
  background: var(--gold);
  transform-origin: bottom;
  transition: height 90ms linear;
}
/* Pouces haut/bas, en bas du LCD : discrets au repos, teintés une fois votés.
   Le compteur reste caché tant que l'admin n'a pas révélé les totaux. */
.music-vote { display: flex; align-items: center; justify-content: flex-end; gap: var(--sp-2); margin-top: 2px; }
.music-vote[hidden] { display: none; }
.music-vbtn {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 3px 8px; line-height: 1;
  background: transparent; color: var(--gold-dim);
  border: 1px solid var(--border); border-radius: 999px;
  cursor: pointer;
  transition: border-color 120ms ease, background 120ms ease, color 120ms ease, transform 90ms ease;
}
.music-vbtn svg { width: 15px; height: 15px; display: block; pointer-events: none; }
.music-vbtn:hover { border-color: var(--gold-dim); color: var(--gold); }
.music-vbtn:active { transform: scale(0.94); }
.music-vbtn.is-active { color: var(--sel,var(--gold)); border-color: var(--sel,var(--gold)); background: rgba(var(--sel-rgb,var(--gold-rgb)),0.16); }
.music-vote-n { font-size: var(--fs-2xs); font-weight: 700; font-variant-numeric: tabular-nums; }
.music-vote-n[hidden] { display: none; }
.music-marquee { overflow: hidden; white-space: nowrap; }
.music-marquee-txt {
  display: inline-block; font-size: var(--fs-md); color: var(--text); font-weight: 600;
  will-change: transform;
}
.music-marquee.is-scroll .music-marquee-txt { animation: music-mq 9s ease-in-out infinite; }
@keyframes music-mq {
  0%, 12%   { transform: translateX(0); }
  50%, 62%  { transform: translateX(var(--mq-shift, 0)); }
  100%      { transform: translateX(0); }
}
/* Playlist dépliable */
.music-pl-head { display: flex; align-items: stretch; gap: var(--sp-1); }
.music-pl-head .music-pl-toggle { width: auto; flex: 0 0 auto; }
.music-tab {
  flex: 1 1 0; display: flex; align-items: center; justify-content: center; gap: var(--sp-2);
  background: var(--field-bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 7px 8px; font-size: var(--fs-sm); cursor: pointer;
}
.music-tab:hover { border-color: var(--gold-dim); }
.music-tab.is-active { border-color: var(--gold-dim); color: var(--gold); background: rgba(var(--gold-rgb), 0.08); }
.music-pl-empty { padding: 8px; text-align: center; color: var(--gold-dim); font-size: var(--fs-sm); list-style: none; }
.music-pl-toggle {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%;
  background: var(--field-bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 7px 10px; font-size: var(--fs-base); cursor: pointer; text-align: left;
}
.music-pl-toggle:hover { border-color: var(--gold-dim); }
.music-pl-caret { flex: 0 0 auto; color: var(--gold-dim); font-size: var(--fs-xs); }
.music-pl-toggle > span[data-i18n] { flex: 1 1 auto; }
.music-pl-count {
  flex: 0 0 auto; font-size: var(--fs-xs); color: var(--gold-dim);
  border: 1px solid var(--border); border-radius: var(--r-pill); padding: 1px 7px;
  font-variant-numeric: tabular-nums;
}
.music-pl {
  list-style: none; margin: 0; padding: var(--sp-1);
  border: 1px solid var(--border); border-radius: var(--r-md);
  max-height: 168px; overflow-y: auto;
  display: flex; flex-direction: column; gap: var(--sp-0);
}
.music-pl-item {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 6px 8px; border-radius: 7px; cursor: pointer;
  color: var(--text); font-size: var(--fs-base);
}
.music-pl-item:hover { background: rgba(var(--gold-rgb), 0.10); }
.music-pl-item.is-cur {
  background: rgba(var(--sel-rgb, var(--gold-rgb)), 0.16);
  color: var(--sel, var(--gold));
}
.music-pl-num { flex: 0 0 auto; width: 1.4em; text-align: right; color: var(--gold-dim); font-variant-numeric: tabular-nums; font-size: var(--fs-sm); }
.music-pl-ttl { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.music-pl-eq { flex: 0 0 auto; color: var(--sel, var(--gold)); font-size: var(--fs-sm); }
.music-pl-move { flex: 0 0 auto; display: inline-flex; flex-direction: column; margin-left: 2px; }
.music-pl-mv { background: none; border: none; color: var(--gold-dim); cursor: pointer; padding: 0; display: inline-flex; line-height: 0.5; }
.music-pl-mv:hover:not([disabled]) { color: var(--gold); }
.music-pl-mv[disabled] { opacity: 0.25; cursor: default; }
.music-pl-mv svg { width: 15px; height: 15px; display: block; }
.music-credit { font-size: var(--fs-xs); color: var(--gold-dim); text-align: center; }
.music-credit a { color: var(--gold-dim); }
/* Radios (flux live) : pastille LIVE dans la playlist + libellé LIVE à la place de la durée */
.music-pl-live {
  flex: 0 0 auto; margin-left: 6px; padding: 0 4px;
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: 0.06em;
  color: #ff5252; border: 1px solid rgba(255, 82, 82, 0.55); border-radius: 3px;
}
.music-dur.music-live { color: #ff5252; font-weight: 700; letter-spacing: 0.04em; }
.music-dur.music-live::before { content: '\25CF '; animation: pth-live-blink 1.6s ease-in-out infinite; display: inline-block; }
@keyframes pth-live-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* Balance G/D (lot 2) */
.music-bal { display: flex; align-items: center; gap: var(--sp-3); }
.music-bal-end { flex: 0 0 auto; font-size: var(--fs-xs); color: var(--gold-dim); width: 12px; text-align: center; font-weight: 700; }
.music-bal-range { flex: 1 1 auto; min-width: 0; accent-color: var(--gold); cursor: pointer; }
/* Mode compact « windowshade » */
.music-shade-row { display: none; }
#music-panel.music-shade .music-player-box { display: none; }
#music-panel.music-shade .music-shade-row { display: flex; align-items: center; gap: var(--sp-3); padding: 2px 0; }
.music-shade-row .music-tbtn { width: 34px; height: 34px; }
.music-shade-row .music-tbtn svg { width: 17px; height: 17px; }
.music-shade-row .music-tbtn-main { width: 40px; height: 40px; }
.music-shade-row .music-tbtn-main svg { width: 21px; height: 21px; }
.music-shade-row .music-marquee { flex: 1 1 auto; min-width: 0; }
.music-shade-btn { margin-left: auto; background: none; border: none; color: var(--text); cursor: pointer; padding: 0 2px; display: inline-flex; align-items: center; line-height: 1; }
.music-shade-btn:hover { color: var(--gold); }
.music-shade-btn svg { width: 16px; height: 16px; display: block; }

/* ── Popover de réglage du son (bouton son du header) ── */
.sound-pop {
  position: fixed; z-index: 1200;
  display: flex; flex-direction: column; align-items: stretch; gap: var(--sp-3);
  width: 210px; box-sizing: border-box;
  padding: 8px 10px;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border-hi); border-radius: var(--r-bar);
  box-shadow: 0 8px 26px rgba(0,0,0,0.45);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
.sound-pop-mute {
  flex: 0 0 auto; width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-md); line-height: 1;
  background: var(--field-bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px; cursor: pointer;
}
.sound-pop-mute:hover { border-color: var(--gold-dim); }
.sound-pop-range { flex: 1 1 auto; min-width: 0; accent-color: var(--gold); cursor: pointer; }
.sound-pop-val { flex: 0 0 auto; font-size: var(--fs-xs); color: var(--gold-dim); min-width: 34px; text-align: right; font-variant-numeric: tabular-nums; }
.sp-row { display: flex; align-items: center; gap: var(--sp-3); }
.sound-pop-ico { flex: 0 0 auto; width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-md); opacity: 0.9; }
.sound-pop.is-off .sound-pop-range:not(.sp-music-range) { opacity: 0.4; }
.sound-pop.is-off .sound-pop-val:not(.sp-music-val) { opacity: 0.5; }
.sp-haptic-lbl { flex: 1 1 auto; min-width: 0; font-size: var(--fs-sm); color: var(--text); }
.sp-haptic-cb { flex: 0 0 auto; width: 18px; height: 18px; margin: 0; accent-color: var(--gold); cursor: pointer; }
.music-list { display: flex; flex-direction: column; gap: var(--sp-1); }
.music-row {
  display: flex; align-items: center; gap: var(--sp-4);
  width: 100%; text-align: left;
  padding: 8px 10px;
  background: var(--field-bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-md);
  cursor: pointer;
}
.music-row:hover { border-color: var(--gold-dim); }
.music-row.is-current { border-color: var(--gold-dim); background: rgba(var(--gold-rgb),0.08); }
.music-row-ic { flex: 0 0 auto; width: 20px; text-align: center; color: var(--gold); font-size: var(--fs-md); }
.music-row-meta { display: flex; flex-direction: column; min-width: 0; }
.music-row-title { font-size: var(--fs-md); color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.music-row-artist { font-size: var(--fs-xs); color: var(--gold-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.music-credit { font-size: var(--fs-2xs); line-height: 1.4; color: var(--gold-dim); opacity: 0.85; }
.music-credit a { color: var(--gold-dim); text-decoration: underline; }
.music-sel-label { display: block; font-size: var(--fs-3xs); letter-spacing: 0.14em; text-transform: uppercase; color: var(--gold-dim); margin-bottom: -4px; }
.music-sel-wrap { width: 100%; }
.music-sel-wrap select { width: 100%; }
/* Barre de titre = poignee de deplacement en mode flottant (makeWinDraggable
   pose deja cursor/touch-action inline ; on garde le curseur de prehension). */
.floating-win .music-panel-title { cursor: move; touch-action: none; }
.floating-win .music-panel-title.win-dragging { cursor: grabbing; }
/* En mode flottant, la fenetre pilote sa hauteur (resize) : on libere le plafond. */
#music-panel.floating-win { max-height: none; width: min(340px, 92vw) !important; height: auto !important; }
/* Telephone / petit ecran : feuille fixe en bas, non deplacable. Le JS
   (_winGate=false) n'ajoute jamais .floating-win ici et nettoie les inline. */
@media (max-width: 899px), (max-height: 599px) {
  #music-panel {
    top: auto; bottom: 8px; left: 8px; right: 8px;
    width: auto;
    max-height: min(70dvh, 460px);
    border-radius: 14px;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   Ranking modal (connect screen) — PokerTH / BBC / WEC.
   Entry: the 🏆 button in #s-connect .corner-links. Content via the
   same-origin /api/ranking relay (see proxy.js). Self-contained so it
   is easy to restyle or move later. All colours via palette tokens.
   ═══════════════════════════════════════════════════════════════════ */
#ranking-modal, #tableranking-modal { position: fixed; inset: 0; z-index: 1200; align-items: center; justify-content: center; pointer-events: none; }
.trk-sub { color: var(--text); opacity: 0.75; font-size: var(--fs-sm); margin-bottom: 10px; }
/* Parité avec les options avancées (#adv-modal) : aucun voile sombre — le jeu /
   le lobby restent visibles derrière et cliquables ; seule la carte capte les
   clics. La carte (fond --modal-bg opaque + ombre portée) reste parfaitement
   lisible sans backdrop. Fermeture par ✕ ou Échap (plus par clic extérieur,
   comme les options avancées). Voir #adv-modal .km-backdrop{display:none}. */
.rk-backdrop { display: none; }
#ranking-modal .rk-card, #tableranking-modal .rk-card { pointer-events: auto; }
#rk-awpop { pointer-events: auto; }
.rk-card {
  position: relative;
  width: min(440px, calc(100vw - 32px));
  max-height: min(80vh, 620px);
  display: flex;
  flex-direction: column;
  background: var(--modal-bg);
  border: 1px solid var(--border-hi);
  border-radius: var(--r-sm);
  box-shadow: 0 16px 50px rgba(0,0,0,0.8);
  padding: 16px 18px 14px;
}
.rk-close { position: absolute; top: 8px; right: 10px; background: none; border: none; color: var(--text); font-size: var(--fs-lg); line-height: 1; cursor: pointer; transition: color 0.15s; }
.rk-close:hover { color: var(--gold); }
.rk-title { font-family: var(--ff-mono); font-size: var(--fs-md); font-weight: 600; letter-spacing: 0.04em; color: var(--text-hi); margin-bottom: 12px; }
.rk-tabs { display: flex; gap: var(--sp-2); margin-bottom: 12px; }
.rk-tab {
  flex: 1; padding: 7px 6px; cursor: pointer;
  background: var(--inset); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-xs);
  font-family: var(--ff-mono); font-size: var(--fs-sm); letter-spacing: 0.03em;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.rk-tab:hover { border-color: var(--border-hi); color: var(--text-hi); }
.rk-tab.active { background: rgba(var(--sel-rgb,var(--gold-rgb)), 0.18); border-color: var(--sel,var(--gold)); color: var(--sel-hi,var(--gold-hi)); }
.rk-body { flex: 1; min-height: 120px; overflow-y: auto; }
.rk-msg { text-align: center; color: var(--text); opacity: 0.85; padding: 30px 8px; font-size: var(--fs-base); }
.rk-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); font-family: var(--ff-mono); }
.rk-table th { position: sticky; top: 0; background: var(--field-bg); text-align: left; color: var(--gold-dim); font-weight: 600; padding: 5px 8px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.rk-table th.rk-th-sort { cursor: pointer; user-select: none; }
.rk-table th.rk-th-sort:hover { color: var(--gold); }
.rk-table td { padding: 4px 8px; border-bottom: 1px solid rgba(var(--gold-rgb), 0.08); color: var(--text-hi); }
.rk-table tbody tr:hover td { background: rgba(var(--gold-rgb), 0.06); }

/* Ranking search box (name filter) — see #rk-search in the ranking modal.
   16px font-size avoids the iOS focus-zoom on the connect screen. */
.rk-search-wrap { margin-bottom: 10px; }
.rk-search {
  width: 100%; box-sizing: border-box; padding: 8px 10px;
  background: var(--field-bg); color: var(--text-hi);
  border: 1px solid var(--border); border-radius: var(--r-xs);
  font-family: var(--ff-mono); font-size: 16px; outline: none;
  transition: border-color 0.15s;
}
.rk-search::placeholder { color: var(--text); opacity: 0.6; }
.rk-search:focus { border-color: var(--gold); }

/* Ranking season + All-Time controls — see #rk-controls in the ranking modal.
   16px <select> font-size avoids the iOS focus-zoom on the connect screen. */
.rk-controls { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 10px; font-family: var(--ff-mono); font-size: var(--fs-base); color: var(--text); }
.rk-season-lbl { display: inline-flex; align-items: center; gap: var(--sp-2); }
.rk-season { font-family: var(--ff-mono); font-size: 16px; padding: 5px 8px; background: var(--field-bg); color: var(--text-hi); border: 1px solid var(--border); border-radius: var(--r-xs); outline: none; }
.rk-season:focus { border-color: var(--gold); }
.rk-season:disabled { opacity: 0.45; }
.rk-alltime-lbl { display: inline-flex; align-items: center; gap: var(--sp-2); cursor: pointer; }
.rk-alltime-lbl input { accent-color: var(--gold); width: 15px; height: 15px; cursor: pointer; }

/* ── Ranking card as a floating window (desktop/tablet, >=900x600) ────────
   Reuses the shared floating-win drag/resize system (makeWin* in pokerth.js).
   The card is position:relative inside the modal, so pin it fixed + drop the
   dark backdrop + let clicks through behind it. Mobile keeps the modal.
   Applies to BOTH the lobby ranking (#ranking-modal) and the in-game table
   ranking (#tableranking-modal) so they behave identically — draggable,
   resizable, no dimming overlay, integrated into the app. */
#ranking-modal.rk-floating,
#tableranking-modal.rk-floating { pointer-events: none; }
#ranking-modal.rk-floating .rk-backdrop,
#tableranking-modal.rk-floating .rk-backdrop { display: none; }
#ranking-modal.rk-floating .rk-card,
#tableranking-modal.rk-floating .rk-card { pointer-events: auto; }
#ranking-modal .rk-card.floating-win,
#tableranking-modal .rk-card.floating-win {
  position: fixed; margin: 0; max-height: none;
  border: 1px solid var(--gold-dim); border-radius: var(--r-bar);
  box-shadow: 0 14px 36px rgba(0,0,0,0.62); overflow: hidden;
}
#ranking-modal .rk-card.floating-win .rk-title,
#tableranking-modal .rk-card.floating-win .rk-title { cursor: move; touch-action: none; -webkit-user-select: none; user-select: none; }
#ranking-modal .rk-card.floating-win .rk-title.win-dragging,
#tableranking-modal .rk-card.floating-win .rk-title.win-dragging { cursor: grabbing; }

/* ═══════════════════════════════════════════════════════════════════
   Forum news (extension web) — fenetre generique #forum-modal sur le
   meme modele que le classement (rk-card, palette, flottante desktop) +
   badge de non-lus sur le bouton d'en-tete du lobby. Entierement
   ADDITIF ; tout passe par les tokens de palette. Logique :
   modules/ui/forumnews.mjs, relais : proxy.js /api/forumfeed. */
#forum-modal { position: fixed; inset: 0; z-index: 1200; align-items: center; justify-content: center; pointer-events: none; }
#forum-modal .rk-card { pointer-events: auto; }
#forum-modal.rk-floating { pointer-events: none; }
#forum-modal.rk-floating .rk-backdrop { display: none; }
#forum-modal.rk-floating .rk-card { pointer-events: auto; }
#forum-modal .rk-card.floating-win {
  position: fixed; margin: 0; max-height: none;
  border: 1px solid var(--gold-dim); border-radius: var(--r-bar);
  box-shadow: 0 14px 36px rgba(0,0,0,0.62); overflow: hidden;
}
#forum-modal .rk-card.floating-win .rk-title { cursor: move; touch-action: none; -webkit-user-select: none; user-select: none; }
#forum-modal .rk-card.floating-win .rk-title.win-dragging { cursor: grabbing; }
/* Lignes de la liste : pastille forum + titre tronque + auteur/date. */
.fn-row { display: flex; gap: var(--sp-3); align-items: flex-start; padding: 7px var(--sp-3); border-bottom: 1px solid var(--border); cursor: pointer; transition: background 0.12s; }
.fn-row:hover, .fn-row:focus-visible { background: var(--inset); outline: none; }
.fn-forum { flex-shrink: 0; margin-top: 1px; font-family: var(--ff-mono); font-size: var(--fs-3xs); letter-spacing: 0.08em; text-transform: uppercase; padding: 2px 7px; border: 1px solid rgba(var(--fn-c, var(--gold-rgb)), 0.40); border-radius: var(--r-pill); color: rgb(var(--fn-c, var(--gold-rgb))); background: rgba(var(--fn-c, var(--gold-rgb)), 0.12); max-width: 74px; white-space: normal; text-align: center; line-height: 1.25; overflow-wrap: break-word; }
/* Code couleur des forums — teintes mi-luminance lisibles sur les themes
   sombres ET clairs. Attribution stable cote JS (fnForumClass) : BBC amber,
   WEC teal, Bugs rouge, General bleu, Feature Requests violet ; les autres
   forums recoivent une teinte deterministe (hash) parmi les huit. */
.fn-c0 { --fn-c: 201,143,31; }
.fn-c1 { --fn-c: 42,157,143; }
.fn-c2 { --fn-c: 208,80,80; }
.fn-c3 { --fn-c: 77,143,208; }
.fn-c4 { --fn-c: 157,111,208; }
.fn-c5 { --fn-c: 93,164,93; }
.fn-c6 { --fn-c: 200,106,154; }
.fn-c7 { --fn-c: 138,151,168; }
.fn-main { flex: 1; min-width: 0; }
.fn-t { font-size: var(--fs-sm); color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fn-meta { font-size: var(--fs-2xs); color: var(--text); opacity: 0.6; margin-top: 1px; }
.fn-row.fn-unread .fn-t { color: var(--text-hi); font-weight: 600; }
/* Icone d'ouverture externe directe (a gauche de la pastille) : discrete au
   repos, or au survol. */
.fn-golink { flex-shrink: 0; display: flex; align-items: center; margin-top: 3px; padding: 2px; color: var(--text); opacity: 0.5; }
.fn-golink:hover, .fn-golink:focus-visible { color: var(--gold); opacity: 1; outline: none; }
/* ── Vue post (parite QML ForumPostPage) : post entier dans la fenetre ── */
.fn-post-head { display: flex; gap: var(--sp-3); align-items: flex-start; padding: 2px 2px 8px; border-bottom: 1px solid var(--border); }
.fn-pt { font-size: var(--fs-base); font-weight: 600; color: var(--text-hi); overflow-wrap: break-word; }
.fnp-globe { flex-shrink: 0; }
.fnp-globe.tr-active { color: var(--gold); }
.fnp-body { padding: 10px 2px; font-size: var(--fs-sm); line-height: var(--lh-base); color: var(--text); overflow-wrap: break-word; }
.fnp-body img { max-width: 100%; height: auto; display: block; margin: 6px 0; }
.fnp-body img.smilies { display: inline; margin: 0 1px; vertical-align: -3px; }
.fnp-body a { color: var(--gold); }
.fnp-body hr { border: none; border-top: 1px solid var(--border); margin: 8px 0; }
.fnp-body dl.file dt { margin: 6px 0; }
.fnp-body.fnp-translated { font-style: italic; white-space: pre-wrap; }
.fn-foot .fnp-error { flex: 1; align-self: center; text-align: right; font-size: var(--fs-2xs); color: var(--red); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rk-tab.fn-btn-off, .rk-tab:disabled { opacity: 0.5; cursor: default; }
.fn-dot { flex-shrink: 0; box-sizing: border-box; width: 8px; height: 8px; border-radius: 50%; background: var(--gold); margin-top: 6px; box-shadow: 0 0 4px rgba(var(--gold-rgb), 0.55); }
/* Lu : anneau vide (l'inverse de la pastille pleine « non lu »). */
.fn-dot.fn-dot-read { background: transparent; border: 1.5px solid var(--gold-dim); box-shadow: none; opacity: 0.55; }
/* Pied : « tout marquer lu » + « ouvrir le forum » (look .rk-tab). */
.fn-foot { display: flex; gap: var(--sp-2); margin-top: 12px; }
/* Badge de non-lus sur le bouton d'en-tete (cap « 9+ » cote JS). */
/* Meme convention que .chat-badge (compteur non-lus du chat) : rouge +
   blanc, la seule combinaison lisible dans TOUS les themes (--gold est
   redefini en quasi-noir par le theme clair, ce qui rendait le nombre
   invisible). */
[id^="forum-btn-"] { position: relative; }
.forum-unread { position: absolute; top: -5px; right: -5px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px; background: var(--red); color: #fff; border: 1.5px solid var(--panel); font-family: var(--ff-mono); font-size: var(--fs-2xs); font-weight: 700; line-height: 13px; text-align: center; box-shadow: 0 1px 4px rgba(0,0,0,0.5); pointer-events: none; z-index: 6; }
/* Option avancee forum_news (ON par defaut) : coupe bouton ET fenetre. */
body.adv-no-forumnews [id^="forum-btn-"], body.adv-no-forumnews #forum-modal { display: none !important; }

/* ── Messages prives (parite QML « Messages prives ») ────────────────────
   Reutilise la carcasse .rk-card / .rk-body / .rk-tab des autres fenetres.
   Specifique ici : la bande des partenaires, la corbeille de l'en-tete et
   le pied de saisie avec son compteur d'octets. */
#pm-btn-lobby { position: relative; }
.pm-unread { position: absolute; top: -5px; right: -5px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px; background: var(--red); color: #fff; border: 1.5px solid var(--panel); font-family: var(--ff-mono); font-size: var(--fs-2xs); font-weight: 700; line-height: 13px; text-align: center; box-shadow: 0 1px 4px rgba(0,0,0,0.5); pointer-events: none; z-index: 6; }
/* Meme carcasse de fenetre que le classement et le forum : conteneur fixe
   plein ecran qui centre la carte, et mode fenetre flottante (deplacable,
   redimensionnable) au-dessus du seuil _winGate. Sans la regle de conteneur
   la carte retombait dans le flux du document -- posee en bas a gauche, et
   la hauteur ajoutee au body faisait apparaitre une barre de defilement qui
   revelait #gchat-fab, un bouton sans regle CSS vivant sous la ligne de
   flottaison. */
/* Mode fenetre flottante des deux cartes converties (popup joueur, infos de
   partie) : deplacables par leur en-tete, redimensionnables, position
   memorisee -- exactement comme les autres fenetres du lobby. */
/* !important obligatoire ici : le media desktop (min-width:900px) impose
   `.pim-card { max-width: 400px !important }` et `.gim-card { width: ... 
   !important }`, qui gagnent sur n'importe quelle specificite. Sans cette
   neutralisation la fenetre refusait de s'elargir au-dela de 400 px, quel
   que soit le maxW passe a _enableFloating. */
#player-info-modal .pim-card.floating-win,
#game-info-modal .gim-card.floating-win {
  position: fixed; margin: 0;
  max-width: none !important; min-width: 0 !important;
  max-height: none !important;
  border: 1px solid var(--gold-dim); border-radius: var(--r-bar);
  box-shadow: 0 14px 36px rgba(0,0,0,0.62);
}
/* La largeur en mode fenetre vient de l'attribut style (posé par
   _enableFloating / le redimensionnement), pas de la feuille. */
#game-info-modal .gim-card.floating-win { width: auto !important; }
/* Surtout PAS align-items:stretch ici : la carte est une colonne flex centree
   (align-items:center), et tous les blocs larges — boutons, stats, infos,
   coupes — portent deja width:100%. Passer en stretch ne les elargissait donc
   pas d'un pixel, mais decalait l'avatar (112 px fixes) contre le bord
   gauche. */
#player-info-modal .pim-card.floating-win { align-items: center; }
/* Largeur FIGEE pour la carte joueur (PIM_WIN_W, player-popup.mjs) : seules
   les poignees haut/bas gardent une course. Les poignees purement
   horizontales et les quatre coins sont retires — un coin qui ne fait plus
   que la hauteur ment sur ce qu'il propose. */
#player-info-modal .pim-card.floating-win .win-rsz-e,
#player-info-modal .pim-card.floating-win .win-rsz-w,
#player-info-modal .pim-card.floating-win .win-rsz-ne,
#player-info-modal .pim-card.floating-win .win-rsz-nw,
#player-info-modal .pim-card.floating-win .win-rsz-se,
#player-info-modal .pim-card.floating-win .win-rsz-sw { display: none !important; }
/* Une fenetre large ne doit pas etirer les COMMANDES : un bouton « Ignorer »
   de 1 300 px de long est absurde. On plafonne la colonne d'actions et on la
   centre ; seuls les blocs de donnees (tableau des places, camembert,
   histogramme) profitent vraiment de la largeur. Le !important est requis :
   ces boutons portent un style inline width:100% pose par le module. */
.pim-card.floating-win .pim-cups-btn,
.pim-card.floating-win .pim-ignore-btn,
.pim-card.floating-win .pim-report-avatar-btn,
.pim-card.floating-win .pim-kickban-btn,
.pim-card.floating-win .pim-profile-link,
.pim-card.floating-win .rk-cups-tabs,
.pim-card.floating-win .stats-tabs {
  max-width: 380px !important;
  margin-left: auto !important; margin-right: auto !important;
}
/* Barre de prehension de la carte joueur : rien en mode centre (la carte ne
   se deplace pas), bandeau discret avec trois points en mode fenetre. Elle
   couvre toute la largeur du haut, la croix passant par-dessus. */
.pim-grip { display: none; }
.pim-card.floating-win .pim-grip {
  display: block; position: absolute; top: 0; left: 0; right: 0; height: 26px;
  border-bottom: 1px solid var(--border);
  border-radius: var(--r-bar) var(--r-bar) 0 0;
  background: var(--inset);
}
.pim-card.floating-win .pim-grip::after {
  content: "\00b7\00b7\00b7"; display: block; text-align: center;
  color: var(--text-dim); font-size: var(--fs-md); line-height: 20px; letter-spacing: 3px;
}
/* La carte demarre sous le bandeau une fois en fenetre. */
.pim-card.floating-win { padding-top: 34px; }
.pim-card.floating-win .pim-grip,
.gim-card.floating-win .gim-header { cursor: move; touch-action: none; -webkit-user-select: none; user-select: none; }
.pim-card.floating-win .pim-grip.win-dragging,
.gim-card.floating-win .gim-header.win-dragging { cursor: grabbing; }
/* ── Fenetre « Profil joueur » (parite QML PokerthPlayerPage) ────────────
   Carcasse generique identique aux autres fenetres ; le contenu reutilise
   les rendus rk* de la carte profil du classement. */
/* Nom du joueur a cote du titre de la fenetre de statistiques. */
.pp-who { margin-left: 8px; color: var(--gold-hi); font-weight: 700; }
/* Carte des stats de session dans la fenetre de statistiques : encadree pour
   la separer nettement du bloc coupes, qui vient du serveur alors que
   celles-ci sont des mesures locales de ma partie en cours. */
.pp-sec { border: 1px solid var(--border); border-radius: var(--r-xs); background: var(--inset); padding: 10px 12px; margin-bottom: 14px; }
.pp-sec .rk-cups-h { margin-top: 0; }
#pp-modal { position: fixed; inset: 0; z-index: 1200; align-items: center; justify-content: center; pointer-events: none; }
/* Largeur suivant l'ecran : le contenu porte un tableau de dix colonnes, un
   camembert et un histogramme de dix barres. A 480px fixes, tout se tassait
   sur un grand ecran alors que la place ne manquait pas. On prend une part de
   la largeur disponible, bornee -- plancher pour rester lisible sur un
   telephone, plafond pour ne pas etaler le texte sur un 4K. */
#pp-modal .rk-card { pointer-events: auto; width: clamp(300px, 68vw, 780px); max-width: calc(100vw - 32px); height: min(88vh, 700px); }
#pp-modal.rk-floating { pointer-events: none; }
#pp-modal.rk-floating .rk-backdrop { display: none; }
#pp-modal.rk-floating .rk-card { pointer-events: auto; }
#pp-modal .rk-card.floating-win {
  position: fixed; margin: 0; max-height: none;
  border: 1px solid var(--gold-dim); border-radius: var(--r-bar);
  box-shadow: 0 14px 36px rgba(0,0,0,0.62); overflow: hidden;
}
#pp-modal .rk-card.floating-win .rk-title { cursor: move; touch-action: none; -webkit-user-select: none; user-select: none; }
#pp-modal .rk-card.floating-win .rk-title.win-dragging { cursor: grabbing; }
/* 5 dernieres places : la 1re ressort en accent (parite colorAccent). */
.rk-last5 { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.rk-last5-l { font-size: var(--fs-2xs); color: var(--text-dim); }
.rk-l5 { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 12px; background: var(--inset); color: var(--text-hi); font-family: var(--ff-mono); font-size: var(--fs-2xs); font-weight: 700; }
/* Vert de la 1re place (RK_PLACE_COLORS[0]), pas var(--gold) : le theme clair
   redefinit --gold en quasi-noir (#1d222b), ce qui donnait du texte noir sur
   fond noir -- exactement le piege deja documente pour .forum-unread. Le vert
   est en dur, comme la legende et le camembert qui l'emploient deja. */
.rk-l5.win { background: #56e289; color: #101010; }
/* Parties recentes : liste courte qui defile seule. */
.rk-games { max-height: 190px; overflow-y: auto; border: 1px solid var(--border); border-radius: var(--r-xs); background: var(--field-bg); }
.rk-game { display: flex; align-items: center; gap: 8px; padding: 5px 8px; border-bottom: 1px solid var(--border); font-size: var(--fs-2xs); }
.rk-game:last-child { border-bottom: none; }
.rk-game-p { flex: 0 0 34px; font-family: var(--ff-mono); color: var(--text-dim); }
.rk-game-p.win { color: var(--gold); font-weight: 700; }
.rk-game-n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-hi); }
.rk-game-d { flex: 0 0 auto; font-family: var(--ff-mono); color: var(--text-dim); }
/* Saisons : une ligne par saison, depliable a la demande. */
.rk-seasons { display: flex; flex-direction: column; gap: 5px; }
.rk-season-h { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; background: var(--inset); border: 1px solid var(--border); border-radius: var(--r-xs); color: var(--text); font: inherit; font-size: var(--fs-sm); padding: 7px 10px; cursor: pointer; }
.rk-season-h:hover { border-color: var(--border-hi); }
.rk-season-t { flex: 1; font-weight: 700; color: var(--text-hi); }
.rk-season-r { font-family: var(--ff-mono); font-size: var(--fs-2xs); color: var(--text-dim); }
.rk-season-r b { color: var(--gold); }
.rk-season-c { color: var(--text-dim); transition: transform 0.15s; }
.rk-season.open .rk-season-c { transform: rotate(90deg); }
.rk-season-b { padding: 6px 2px 2px; }
#pm-modal { position: fixed; inset: 0; z-index: 1200; align-items: center; justify-content: center; pointer-events: none; }
#pm-modal .rk-card { pointer-events: auto; width: min(600px, calc(100vw - 32px)); height: min(85vh, 520px); padding: 14px 16px 12px; }
#pm-modal.rk-floating { pointer-events: none; }
#pm-modal.rk-floating .rk-backdrop { display: none; }
#pm-modal.rk-floating .rk-card { pointer-events: auto; }
#pm-modal .rk-card.floating-win {
  position: fixed; margin: 0; max-height: none;
  border: 1px solid var(--gold-dim); border-radius: var(--r-bar);
  box-shadow: 0 14px 36px rgba(0,0,0,0.62); overflow: hidden;
}
#pm-modal .rk-card.floating-win .rk-title { cursor: move; touch-action: none; -webkit-user-select: none; user-select: none; }
#pm-modal .rk-card.floating-win .rk-title.win-dragging { cursor: grabbing; }
/* Corbeille : a gauche de la croix, rouge (parite QML). */
.pm-del { position: absolute; top: 8px; right: 36px; background: none; border: none; color: var(--red); cursor: pointer; padding: 2px; line-height: 0; transition: opacity 0.15s; }
.pm-del:hover { opacity: 0.75; }
.pm-del:disabled { opacity: 0.3; cursor: default; }
/* Corps : colonne des partenaires a gauche, conversation a droite. */
.pm-main { flex: 1; min-height: 0; display: flex; gap: 10px; }
.pm-partners { flex: 0 0 160px; min-width: 0; overflow-y: auto; overflow-x: hidden; background: var(--panel-row, var(--inset)); border: 1px solid var(--border); border-radius: var(--r-xs); padding: 4px; display: flex; flex-direction: column; gap: 2px; }
.pm-partner { display: flex; align-items: center; gap: 5px; width: 100%; text-align: left; background: none; border: none; border-radius: 4px; color: var(--text); font: inherit; padding: 5px 7px; cursor: pointer; transition: background 0.12s; }
.pm-partner:hover, .pm-partner.active { background: var(--inset-hi, rgba(var(--gold-rgb),0.12)); }
.pm-pmain { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.pm-pname { font-size: var(--fs-sm); color: var(--text-hi); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pm-pname.unread { font-weight: 700; }
.pm-plast { font-size: var(--fs-2xs); color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pm-partner.off .pm-pname { color: var(--text-dim); font-style: italic; }
.pm-pbadge { flex: 0 0 auto; min-width: 14px; height: 14px; padding: 0 4px; border-radius: 7px; background: var(--red); color: #fff; font-family: var(--ff-mono); font-size: var(--fs-2xs); font-weight: 700; line-height: 14px; text-align: center; }
.pm-nopart { align-self: center; font-size: var(--fs-sm); color: var(--text-dim); }
/* Conversation. */
.pm-conv { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.pm-head { display: flex; align-items: center; gap: 7px; padding-bottom: 6px; }
.pm-select { max-width: 170px; background: var(--field-bg); border: 1px solid var(--border); border-radius: var(--r-xs); color: var(--text); font: inherit; font-size: var(--fs-2xs); padding: 2px 5px; }
.pm-cur { font-size: var(--fs-sm); font-weight: 700; color: var(--text-hi); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pm-offlbl { font-size: var(--fs-2xs); color: var(--text-dim); flex: 0 0 auto; }
.pm-msgs { flex: 1; min-height: 0; overflow-y: auto; background: var(--field-bg); border: 1px solid var(--border); border-radius: var(--r-xs); padding: 8px; display: flex; flex-direction: column; gap: 6px; }
.pm-hint { color: var(--text-dim); font-size: var(--fs-sm); }
/* Bulles : les miennes a droite et teintees, celles d'en face a gauche. */
.pm-line { display: flex; }
.pm-line.mine { justify-content: flex-end; }
.pm-bub { display: flex; flex-direction: column; max-width: 85%; padding: 6px 9px; border-radius: 8px; border: 1px solid var(--border); background: var(--panel-row, var(--inset)); }
.pm-line.mine .pm-bub { background: rgba(var(--gold-rgb), 0.16); border-color: var(--gold-dim); }
.pm-text { font-size: var(--fs-sm); color: var(--text-hi); overflow-wrap: break-word; white-space: pre-wrap; }
.pm-ts { align-self: flex-end; font-family: var(--ff-mono); font-size: var(--fs-3xs, var(--fs-2xs)); color: var(--text-dim); margin-top: 2px; }
.pm-line.mine .pm-ts { color: var(--gold-dim); }
/* Avis « invite » / « a la table » : remplace la saisie quand rien ne peut
   partir (parite PrivateMessageDialog.qml). */
.pm-note { margin-top: 8px; font-size: var(--fs-2xs); color: var(--text-dim); }
.pm-input:disabled { opacity: 0.5; cursor: not-allowed; }
.pm-foot { display: flex; align-items: center; gap: var(--sp-2); margin-top: 10px; }
.pm-input { flex: 1; min-width: 0; background: var(--field-bg); border: 1px solid var(--border); border-radius: var(--r-xs); color: var(--text); font: inherit; font-size: var(--fs-sm); padding: 6px 9px; }
.pm-input:focus { outline: none; border-color: var(--sel,var(--gold)); }
.pm-count { flex: 0 0 auto; font-family: var(--ff-mono); font-size: var(--fs-2xs); color: var(--text-dim); }
/* Bouton d'envoi du PM : icône avion façon chat lobby (parité QML be3c43c) —
   carré à hauteur du champ, sans chrome ; mêmes verts par thème que
   #lobby-chat-panel .chat-send. Désactivé = atténué, comme l'opacité QML. */
.pm-send {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0;
  background: none; border: none; border-radius: var(--r-sm);
  color: var(--green); cursor: pointer;
}
.pm-send:hover:not(:disabled) { background: var(--inset-hi); }
.pm-send:disabled { opacity: .5; cursor: default; }
:root[data-theme="pokerth"] .pm-send { color: #4ade80; }
:root[data-theme="pokerth-light"] .pm-send { color: #16a34a; }
/* Enveloppe absente (moi, invite, joueur a une table en cours) : une cale de
   meme largeur garde les deux autres pastilles alignees d'une ligne a l'autre. */
.pl-act-gap { display: inline-block; width: 20px; }
/* Marqueur « (pm) » sur la ligne du chat lobby : le client QML ecrit
   « Nom(pm): texte », on l'obtient sans toucher au nom passe a addChat
   (sinon liste d'ignores et surlignage du pseudo cassent). */
.msg.pm .who::after { content: "(pm)"; font-size: var(--fs-2xs); opacity: 0.75; }
.msg.pm .txt { font-style: italic; opacity: 0.9; }

/* ── Player profile (clicking a name in the ranking) ─────────────────────── */
.rk-name { background: none; border: none; padding: 0; margin: 0; font: inherit; letter-spacing: inherit; color: var(--gold-hi); cursor: pointer; }
.rk-name:hover { color: var(--gold); }
#rk-profile { flex: 1; min-height: 120px; overflow-y: auto; }
.rk-back { background: var(--inset); color: var(--text); border: 1px solid var(--border); border-radius: var(--r-xs); width: 32px; height: 28px; line-height: 1; font-size: var(--fs-lg); cursor: pointer; margin-bottom: 12px; transition: border-color 0.15s, color 0.15s; }
.rk-back:hover { border-color: var(--gold); color: var(--gold-hi); }
.rk-prof-head { margin-bottom: 14px; }
.rk-prof-name { font-family: var(--ff-mono); font-size: 1.15rem; font-weight: 600; letter-spacing: 0.02em; color: var(--gold-hi); word-break: break-word; }
.rk-prof-since { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--text); opacity: 0.8; margin-top: 2px; }
.rk-sec { margin-bottom: 14px; }
.rk-sec-h { font-family: var(--ff-mono); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--gold-dim); margin-bottom: 6px; }
.rk-awards { display: flex; flex-wrap: nowrap; gap: var(--sp-3); overflow-x: auto; overflow-y: hidden; padding-bottom: 6px; -webkit-overflow-scrolling: touch; scrollbar-width: thin; scrollbar-color: var(--gold-dim) transparent; }
.rk-awards::-webkit-scrollbar { height: 6px; }
.rk-awards::-webkit-scrollbar-thumb { background: var(--gold-dim); border-radius: var(--r-xs); }
.rk-awards::-webkit-scrollbar-track { background: transparent; }
.rk-award { flex: 0 0 auto; width: 54px; height: 54px; object-fit: contain; border-radius: 4px; background: rgba(var(--gold-rgb), 0.05); cursor: pointer; }
#rk-awpop { display: none; position: fixed; inset: 0; z-index: 10000; background: rgba(0, 0, 0, 0.85); align-items: center; justify-content: center; padding: 24px; cursor: pointer; pointer-events: auto; }
#rk-awpop img { max-width: 90vw; max-height: 90vh; object-fit: contain; border-radius: var(--r-sm); box-shadow: 0 8px 40px rgba(0, 0, 0, 0.6); }
.rk-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(64px, 1fr)); gap: 6px; }
.rk-tile { background: var(--field-bg); border: 1px solid var(--border); border-radius: 4px; padding: 8px 4px; text-align: center; }
.rk-tile-v { font-family: var(--ff-mono); font-size: var(--fs-md); font-weight: 600; color: var(--text-hi); }
.rk-tile-l { font-family: var(--ff-mono); font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text); opacity: 0.75; margin-top: 3px; }
/* Cups summary inside the player-info popup (#pim-cups) — reuses the ranking
   profile renderer (rkProfileBody); adds a top heading + one tab per cup. */
.pim-cups { width: 100%; margin-top: 6px; }
.pim-cups-btn { width: 100%; margin-top: 10px; padding: 9px 0; border: 1px solid var(--gold-dim); border-radius: 8px; background: transparent; color: var(--cream); font-family: var(--ff-mono); font-size: var(--fs-base); font-weight: 600; letter-spacing: 0.03em; cursor: pointer; transition: border-color 0.15s, color 0.15s, background 0.15s; }
.pim-cups-btn:hover, .pim-cups-btn:focus-visible { border-color: var(--gold); color: var(--gold-hi); background: rgba(var(--gold-rgb), 0.06); outline: none; }
.rk-cups-h { font-family: var(--ff-mono); font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: 0.06em; color: var(--gold-hi); margin: 4px 0 8px; }
.rk-cups-msg, .rk-cups-empty { font-family: var(--ff-mono); font-size: var(--fs-xs); color: var(--text); opacity: 0.7; margin: 6px 0; }
.rk-cups-tabs { display: flex; gap: var(--sp-1); margin: 2px 0 10px; flex-wrap: wrap; }
.rk-cups-tab { flex: 1 1 auto; min-width: 64px; padding: 6px 8px; font-family: var(--ff-mono); font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.03em; color: var(--text); background: var(--field-bg); border: 1px solid var(--border); border-radius: 4px; cursor: pointer; transition: color 0.15s, border-color 0.15s, background 0.15s; }
.rk-cups-tab:hover { border-color: var(--gold); color: var(--gold-hi); }
.rk-cups-tab.active { color: var(--sel-hi,var(--gold-hi)); border-color: var(--sel,var(--gold)); background: rgba(var(--sel-rgb,var(--gold-rgb)), 0.08); }

/* ── Repartition des places (camembert) — SeasonStatsSection + PlacementResult ── */
.rk-place-legend { display:flex; flex-wrap:wrap; gap:var(--sp-4); margin:8px 0; }
.rk-lg { display:inline-flex; align-items:center; gap:5px; font-family:var(--ff-mono); font-size:var(--fs-xs); color:var(--text); }
.rk-lg i { width:11px; height:11px; border-radius:2px; display:inline-block; flex:0 0 auto; }
.rk-ps-tw, .rk-pr-tw { overflow-x:auto; -webkit-overflow-scrolling:touch; }
.rk-ps-table, .rk-pr-table { border-collapse:collapse; width:100%; font-family:var(--ff-mono); }
.rk-ps-table { min-width:340px; }
.rk-pr-table { min-width:280px; }
.rk-ps-table th, .rk-ps-table td, .rk-pr-table th, .rk-pr-table td { padding:4px 3px; text-align:center; font-size:var(--fs-xs); border-bottom:1px solid var(--border); }
.rk-ps-table th, .rk-pr-table th { color:var(--text-hi); font-weight:700; }
.rk-ps-table td, .rk-pr-table td { color:var(--text); }
.rk-ps-table .rk-ps-lbl { text-align:left; color:var(--text); opacity:0.8; white-space:nowrap; }
.rk-ps-pie { display:flex; justify-content:center; margin:10px 0; }
.rk-ps-pie canvas { width:200px; height:200px; max-width:62vw; max-height:62vw; }
.rk-ps-bars { display:flex; align-items:flex-end; gap:3px; height:150px; margin-top:6px; padding-top:14px; }
.rk-ps-bar { flex:1 1 0; display:flex; flex-direction:column; align-items:center; justify-content:flex-end; height:100%; min-width:0; }
.rk-ps-bar i { display:block; width:62%; border-radius:2px 2px 0 0; }
.rk-ps-bar b { font-family:var(--ff-mono); font-size:var(--fs-3xs); color:var(--text); opacity:0.85; line-height:1; margin-bottom:2px; min-height:0.58rem; }
.rk-ps-bar u { font-family:var(--ff-mono); font-size:var(--fs-3xs); color:var(--text); opacity:0.7; text-decoration:none; margin-top:3px; }
.rk-pr { display:flex; gap:14px; flex-wrap:wrap; align-items:flex-start; }
.rk-pr-chart { cursor:pointer; flex:0 0 auto; }
.rk-pr-bars { display:flex; align-items:flex-end; gap:var(--sp-0); width:200px; height:100px; }
.rk-pr-chart.show-pie .rk-pr-bars { display:none; }
.rk-pr-bars i { flex:1 1 0; display:block; border-radius:2px 2px 0 0; }
.rk-pr-pie { display:none; }
.rk-pr-chart.show-pie .rk-pr-pie { display:flex; justify-content:center; }
.rk-pr-pie canvas { width:140px; height:140px; }
.rk-pr-tw { flex:1 1 200px; }
.rk-step-tabs { display:flex; gap:var(--sp-1); flex-wrap:wrap; margin:2px 0 8px; }
.rk-step-tab { padding:4px 10px; font-family:var(--ff-mono); font-size:var(--fs-xs); color:var(--text); background:var(--field-bg); border:1px solid var(--border); border-radius:4px; cursor:pointer; }
.rk-step-tab.active { color:var(--sel-hi,var(--gold-hi)); border-color:var(--sel,var(--gold)); background:rgba(var(--sel-rgb,var(--gold-rgb)),0.08); }

.rk-pager { display: flex; align-items: center; justify-content: center; gap: var(--sp-5); padding: 8px 0 2px; }
.rk-page-btn { background: var(--inset); color: var(--text); border: 1px solid var(--border); border-radius: 4px; width: 32px; height: 28px; line-height: 1; font-size: var(--fs-lg); cursor: pointer; transition: border-color 0.15s, color 0.15s; }
.rk-page-btn:hover:not(:disabled) { border-color: var(--gold); color: var(--gold-hi); }
.rk-page-btn:disabled { opacity: 0.4; cursor: default; }
.rk-page-lbl { font-family: var(--ff-mono); font-size: var(--fs-sm); color: var(--text); min-width: 56px; text-align: center; }
/* Encart « Calcul du classement » (parité QML RankingPage, commit ea33d7a) :
   boîte discrète sous la liste PokerTH — fond inset + bord, titre gras,
   lignes en ton atténué, valeurs en mono. */
.rk-calc { margin-top: 8px; padding: 8px 10px; background: var(--inset); border: 1px solid var(--border); border-radius: 4px; }
.rk-calc-title { font-size: var(--fs-xs); font-weight: 700; color: var(--text); margin-bottom: 3px; }
.rk-calc-row { font-size: var(--fs-xs); color: var(--chatlog-muted, var(--text-dim, #9aa3b5)); font-family: var(--ff-mono); overflow-wrap: anywhere; }
.rk-calc-row span { font-family: inherit; }

/* ═══════════════════════════════════════════════════════════════════
   Menu « liens PokerTH » (écran connect) — regroupe PokerTH.net /
   Discord / Shoutbox / Week-End Cup dans un seul <details> pour
   désencombrer le header (surtout sur téléphone). Reprend la chrome du
   menu ••• (#g-overflow-menu). Le 🏆 Ranking reste un bouton séparé.
   ═══════════════════════════════════════════════════════════════════ */
#s-connect .connect-header .cl-links { position: relative; }
#s-connect .connect-header .cl-links > summary {
  list-style: none;
  cursor: pointer;
}
#s-connect .connect-header .cl-links > summary::-webkit-details-marker { display: none; }
#s-connect .connect-header .cl-links > summary::marker { content: ""; }
#s-connect .connect-header .cl-links > summary img { display: block; width: 18px; height: 18px; }

#s-connect .connect-header .cl-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 200;
  width: clamp(232px, 46vw, 300px);
  display: flex;
  flex-direction: column;
  gap: clamp(4px, 1vw, 7px);
  padding: clamp(6px, 1.4vw, 9px) clamp(5px, 1.1vw, 8px);
  box-sizing: border-box;
  background: var(--modal-bg);
  border: 1px solid var(--border-hi);
  border-radius: var(--r-bar);
  box-shadow: 0 12px 32px rgba(0,0,0,0.7);
}
/* Lignes du menu : on neutralise la « chip » héritée des .btn-sm du
   cluster (fond/bord/ombre/hauteur fixe) pour des lignes plates. */
#s-connect .connect-header .cl-menu .btn-sm {
  width: 100%;
  height: auto;
  min-width: 0;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: clamp(9px, 1.8vw, 12px);
  text-align: left;
  padding: clamp(7px, 1.4vw, 10px) clamp(12px, 2.2vw, 16px);
  border-radius: 4px;
  font-size: clamp(0.8rem, 1.9vw, 0.95rem);
  white-space: nowrap;
  text-decoration: none;
  background: transparent;
  border: 1px solid transparent;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
#s-connect .connect-header .cl-menu .btn-sm:hover {
  background: var(--panel-hi);
  border-color: var(--border-hi);
}
#s-connect .connect-header .cl-menu .cl-ico {
  flex: none;
  width: clamp(18px, 3.6vw, 24px);
  height: clamp(18px, 3.6vw, 24px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
#s-connect .connect-header .cl-menu .cl-ico img,
#s-connect .connect-header .cl-menu .cl-ico svg { display: block; width: clamp(18px, 3.6vw, 24px); height: clamp(18px, 3.6vw, 24px); }
#s-connect .connect-header .cl-menu .cl-ico-emo {
  flex: none;
  width: clamp(18px, 3.6vw, 24px);
  text-align: center;
  font-size: clamp(0.95rem, 2.2vw, 1.2rem);
  line-height: 1;
}

/* ── Advanced options modal (QML-parity settings) ──────────────────────────
   Entrée via la roue crantée (jeu + lobby). Chrome réutilisé de .km-* ; corps
   .adv-* propre. Bascules présentation appliquées par applyAdvOpts() via des
   classes sur <body>. */
#adv-modal{position:fixed;inset:0;z-index:1200;display:flex;align-items:center;justify-content:center;pointer-events:none}
#adv-modal .km-backdrop{display:none}
#adv-modal .km-card{pointer-events:auto}
.adv-card{max-width:420px;width:calc(100% - 32px)}
.adv-body{max-height:62vh;overflow:auto;margin-top:8px;padding-right:2px}
.adv-sec{font-size:var(--fs-2xs);letter-spacing:0.12em;text-transform:uppercase;color:var(--gold-dim);margin:13px 0 4px;font-weight:700}
.adv-sec:first-child{margin-top:4px}
/* Sections repliables de l'onglet Interface (demande narmod 2026-07-19) :
   <details class="adv-fold"> natif, plie par defaut, chevron anime. */
details.adv-fold>summary.adv-sec{cursor:pointer;list-style:none;display:flex;align-items:center;gap:var(--sp-3);user-select:none;-webkit-tap-highlight-color:transparent;padding:5px 0;font-size:var(--fs-xs);letter-spacing:0.07em} /* narmod 2026-07-19 : en-tete de section discret (eyebrow), plus petit que les options .adv-row 0.82rem */
details.adv-fold>summary.adv-sec::-webkit-details-marker{display:none}
summary.adv-sec .adv-sec-ic{flex:0 0 auto;width:14px;height:14px;display:inline-flex;align-items:center;justify-content:center;opacity:.9}
summary.adv-sec .adv-sec-ic svg{width:14px;height:14px;display:block} /* 14 px : accorde a l'en-tete de section reduit */
details.adv-fold>summary.adv-sec::before{content:'\25B8';font-size:0.95em;color:var(--gold-dim);transition:transform .15s ease;flex:0 0 auto}
details.adv-fold[open]>summary.adv-sec::before{transform:rotate(90deg)}
/* Lisibilité (demande narmod 2026-07-19) : les options d'une section sont
   INDENTÉES sous leur catégorie (alignées sous l'icône), et une section
   ouverte est soulignée d'un fin séparateur sous son en-tête. */
details.adv-fold>*:not(summary){margin-left:28px}
details.adv-fold[open]>summary.adv-sec{border-bottom:1px solid var(--border);padding-bottom:7px;margin-bottom:6px}
details.adv-fold:first-child>summary.adv-sec{margin-top:4px}
.adv-row{display:flex;align-items:center;gap:9px;padding:6px 2px;font-size:var(--fs-base);color:var(--text);cursor:pointer}
.adv-row input[type=checkbox]{accent-color:var(--gold);flex-shrink:0;width:16px;height:16px}
.adv-row > span{flex:1}
.adv-row .adv-numin{flex:0 0 auto;width:92px;text-align:right;background:transparent;color:var(--text);border:1px solid var(--border);border-radius:8px;padding:5px 8px;font:inherit;font-size:var(--fs-base)}
.adv-row .adv-textin{flex:0 0 auto;width:150px;background:transparent;color:var(--text);border:1px solid var(--border);border-radius:8px;padding:5px 8px;font:inherit;font-size:var(--fs-base)}
.adv-row em{font-size:var(--fs-3xs);font-style:normal;letter-spacing:0.08em;text-transform:uppercase;color:var(--gold-dim);border:1px solid var(--border);border-radius:var(--r-bar);padding:1px 7px;opacity:0.85}
.adv-webtag{font-size:var(--fs-3xs);font-style:normal;letter-spacing:0.08em;text-transform:uppercase;color:var(--green);border:1px solid rgba(var(--green-rgb),0.45);border-radius:var(--r-bar);padding:1px 7px;opacity:0.9;flex-shrink:0}
.adv-betatag{font-size:var(--fs-3xs);font-style:normal;letter-spacing:0.08em;text-transform:uppercase;color:var(--gold);border:1px solid rgba(var(--gold-rgb),0.5);border-radius:var(--r-bar);padding:1px 7px;opacity:0.92;flex-shrink:0;margin-right:5px}
.adv-row.adv-soon{opacity:0.5;cursor:default}
body.adv-no-cardanim .seat-holecards *,body.adv-no-cardanim .hand-cards *,body.adv-no-cardanim #g-myseat-cards *{transition:none !important;animation:none !important}
body.adv-no-blinds .blind-chip,body.adv-no-blinds .dealer-chip{display:none !important}
body.adv-no-blindsbadge .blinds-next{display:none !important}
body.adv-no-handsbtn #hands-toggle-btn{display:none !important}
body.adv-no-community #ranking-btn-connect,body.adv-no-community #ranking-btn-lobby,body.adv-no-community #ranking-btn-game{display:none !important}
body.adv-no-tablezoom #g-zoom-ctrl{display:none !important}
body.adv-no-lobbychat #lobby-chat-btn{display:none !important}
body.adv-no-lobbychat #lobby-chat-panel,
body.adv-no-lobbychat #s-lobby .lobby-grid > #lobby-chat-panel{display:none !important}
#g-next-hand-btn{position:fixed;left:50%;bottom:150px;transform:translateX(-50%);z-index:950;padding:10px 22px;font-size:var(--fs-md);font-weight:700;letter-spacing:0.04em;color:#fff;background:linear-gradient(#50b840,#1e6614);border:1px solid #7ad06a;border-radius:var(--r-md);cursor:pointer;box-shadow:0 4px 14px rgba(0,0,0,0.45)}
#g-next-hand-btn:active{transform:translateX(-50%) scale(0.96)}
.adv-ign-desc{font-size:var(--fs-sm);color:var(--text);opacity:0.7;margin:2px 2px 8px}
.adv-ign-row{display:flex;align-items:center;gap:9px;padding:5px 2px;font-size:var(--fs-base);color:var(--text);border-bottom:1px solid rgba(var(--gold-rgb),0.08)}
.adv-ign-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.adv-ign-rm{font-size:var(--fs-xs);letter-spacing:0.05em;color:var(--text);background:none;border:1px solid var(--border);border-radius:var(--r-bar);padding:2px 10px;cursor:pointer}
.adv-ign-rm:hover{color:var(--gold);border-color:var(--gold-dim)}
.adv-ign-empty{font-size:var(--fs-sm);color:var(--text);opacity:0.55;padding:4px 2px}
@media (min-width:900px){body.adv-no-lobbychat #s-lobby .lobby-grid{grid-template-rows:minmax(0,1fr) 0 0 auto}body.adv-no-lobbychat #s-lobby .lob-h-v{display:none}}
@media (max-width:899.98px){body.adv-no-lobbychat #s-lobby .lobby-grid > .games-col{flex:1 1 auto !important}}

/* Estompage des cartes des perdants au showdown (fadeOutLosingCards). Le marquage
   .loser-fade est pose sur le siege par renderSeatsImmediate quand le pid est dans
   _sdLosers (rempli a EndOfHandShow). Seules les cartes sont attenuees. */
.seat.loser-fade .pk{opacity:.4;filter:grayscale(.55);transition:opacity .45s ease,filter .45s ease}
/* Drapeau pays sur l'avatar (showCountryFlagOnAvatar) — visibilite togglable. */
body.adv-no-flag .seat-flag{display:none !important}
/* Titre « PokerTH » de l'écran de connexion (.cl-app-title) : CACHÉ par
   défaut (demande narmod 19/07). Rendu visible uniquement quand l'option
   « Afficher le titre PokerTH » est activée (body.adv-show-title). */
.cl-app-title{display:none}
body.adv-show-title .cl-app-title{display:block}
/* Player-bar masquée : tes cartes/infos s'affichent dans ta self-box (siège).
   La barre d'action descend tout en bas (pin forcé en JS -> visible en permanence). */
body.adv-hide-pbar .player-bar{display:none !important}
/* Reserve de repli pour les sieges (le JS affine a la hauteur exacte de la barre
   d'action des le prochain rendu). Garde la self-box au-dessus de la barre epinglee. */
body.adv-hide-pbar .game-area{padding-bottom:clamp(210px,34dvh,290px)}
body.adv-hide-pbar .my-zone{bottom:0 !important;padding-bottom:max(8px,env(safe-area-inset-bottom))}
/* ── Mobile PORTRAIT + table plein écran + dock masqué : barre d'action COLLÉE
   au bord bas. Le panneau des boutons devient une bande pleine largeur dont le
   fond descend jusqu'au bord (coins bas droits) ; la marge du home-indicator
   (safe-area) passe DANS le panneau -> plus de bande "bois" (fond de table)
   visible sous les boutons. Scopé phone portrait uniquement : tablette/desktop
   gardent la carte flottante centrée, le paysage garde son flux. */
@media (orientation: portrait) and (max-width: 899px) {
  html[data-table-fs="1"] body.adv-hide-pbar .my-zone {
    padding-left: 0 !important; padding-right: 0 !important; padding-bottom: 0 !important;
  }
  html[data-table-fs="1"] body.adv-hide-pbar .my-zone .action-wrap { max-width: none; width: 100%; }
  html[data-table-fs="1"] body.adv-hide-pbar .my-zone .action-grid {
    max-width: none; width: 100%; margin: 0;
    border-radius: 12px 12px 0 0;   /* coins hauts arrondis, bas droits */
    border-bottom: 0;
    padding-bottom: calc(8px + env(safe-area-inset-bottom));
  }
}
/* Idem pour TOUS LES AUTRES styles de table. .my-zone reserve deja la marge du
   home-indicator dans SON propre padding-bottom, avec le fond --panel dedans :
   sous le cadre or de .action-grid il restait donc une bande grise pleine
   largeur jusqu'au bord (constate narmod 2026-08-01, iPhone, feutre vert).
   Meme remede que le cas plein ecran ci-dessus : la reserve passe DANS
   .action-grid, dont le cadre vient alors epouser le bord bas. Le padding
   lateral, retire de .my-zone, est repris ici pour ne pas coller les boutons
   aux bords. Scope identique (telephone portrait, dock masque). */
@media (orientation: portrait) and (max-width: 899px) {
  html:not([data-table-fs="1"]) body.adv-hide-pbar .my-zone {
    padding-left: 0 !important; padding-right: 0 !important; padding-bottom: 0 !important;
  }
  html:not([data-table-fs="1"]) body.adv-hide-pbar .my-zone .action-wrap { max-width: none; width: 100%; }
  html:not([data-table-fs="1"]) body.adv-hide-pbar .my-zone .action-grid {
    max-width: none; width: 100%; margin: 0;
    padding: 0 12px calc(8px + env(safe-area-inset-bottom));
    border-radius: 12px 12px 0 0;   /* coins hauts arrondis, bas droits */
  }
}
/* Mode masqué (Mode PokerTH) : MA self-box (siège) affiche mes cartes ~taille tapis.
   COHERENCE PAR STYLE :
   - Style "pokerth" : on GARDE la grille native -> self-box IDENTIQUE aux adversaires,
     on agrandit SEULEMENT mes cartes (aucune surcharge de layout ci-dessous).
   - Autres styles : ils ne placent pas mes cartes nativement -> on force un layout flex
     (avatar|cartes, nom dessous) + max-width:none (sinon empilement vertical). */

/* Taille des cartes self RATTACHEE aux cartes communes via --comm-scale
   (slot QML 46x64 x echelle par appareil) : chaque palier est une FRACTION
   <= 1 de la taille riviere -> le zoom ne peut JAMAIS la depasser, sur tous
   les appareils. Niveau 0 (base QML) = 85% de la riviere. */
.seat.me .seat-holecards.shc-big .pk {
  width: calc(46px * var(--comm-scale, 1) * 0.85) !important;
  height: calc(64px * var(--comm-scale, 1) * 0.85) !important;
  font-size: calc(1.02rem * var(--comm-scale, 1) * 0.85) !important;
  border-radius: calc(5px * var(--comm-scale, 1)) !important;
}
/* Paliers du zoom de mes cartes (cycle 0-3 du bouton #g-cardzoom),
   STRICTEMENT CROISSANTS depuis la base et PLAFONNES a la riviere :
   0 = 85% · 1 = 90% · 2 = 95% · 3 = 100% (= taille exacte des cartes
   communes). Fractions de --comm-scale -> adapte a chaque appareil. */
.seat.me .seat-holecards.shc-l1 .pk { width: calc(46px * var(--comm-scale, 1) * 0.90) !important; height: calc(64px * var(--comm-scale, 1) * 0.90) !important; font-size: calc(1.02rem * var(--comm-scale, 1) * 0.90) !important; border-radius: calc(5px * var(--comm-scale, 1)) !important; }
.seat.me .seat-holecards.shc-l2 .pk { width: calc(46px * var(--comm-scale, 1) * 0.95) !important; height: calc(64px * var(--comm-scale, 1) * 0.95) !important; font-size: calc(1.02rem * var(--comm-scale, 1) * 0.95) !important; border-radius: calc(5px * var(--comm-scale, 1)) !important; }
.seat.me .seat-holecards.shc-l3 .pk { width: calc(46px * var(--comm-scale, 1)) !important; height: calc(64px * var(--comm-scale, 1)) !important; font-size: calc(1.02rem * var(--comm-scale, 1)) !important; border-radius: calc(5px * var(--comm-scale, 1)) !important; }
/* Paysage mobile (landscapeCompact) : cartes self a la taille EXACTE de la
   riviere des le niveau 0 (le transform du siege les reduit deja ~20%) —
   demande narmod « grossir les cartes de la selfbox ». Tout le cycle est mis
   au plafond pour rester monotone (plateau, jamais de retrecissement). */
@media (orientation: landscape) and (max-height: 600px) {
  /* Division par --sscale (échelle transform du siège, posée par renderSeats) :
     sans elle, la taille rivière était RE-réduite par le scale du siège
     (~x0.8) → cartes minuscules. Taille écran = taille rivière EXACTE. */
  .seat.me .seat-holecards.shc-big .pk,
  .seat.me .seat-holecards.shc-l1 .pk,
  .seat.me .seat-holecards.shc-l2 .pk,
  .seat.me .seat-holecards.shc-l3 .pk {
    width: calc(46px * var(--comm-scale, 1) / var(--sscale, 1)) !important;
    height: calc(64px * var(--comm-scale, 1) / var(--sscale, 1)) !important;
    font-size: calc(1.02rem * var(--comm-scale, 1) / var(--sscale, 1)) !important;
  }
}
/* ── Packs pokerth : self-box à la STRUCTURE QML exacte (GamePlayerSelfBox).
   Dans le QML, les cartes du self dérivent de la hauteur de la BOX
   (selfBaseHeight 96/94/82 → cardsH = H−12−barre → cartes ≈ 37×52, 36×50 en
   landscapeCompact ; avatar = min(cardsH, 60/54)). Côté web elles étaient
   couplées à la taille des cartes COMMUNES (--comm-scale), qui grossit avec
   la fenêtre — cause de la self énorme qui chevauchait ses voisins sur
   desktop. Ici : tailles fixes QML (échelle du siège appliquée par le
   transform, comme les adversaires) ; tous les paliers du bouton de zoom de
   cartes sont au même point (plateau) — en fidélité QML la taille des cartes
   self est fixe. Les packs non-pokerth gardent le couplage rivière. */
html[data-seat-struct="qml"] .seat.me .seat-holecards.shc-big .pk,
html[data-seat-struct="qml"] .seat.me .seat-holecards.shc-l1 .pk,
html[data-seat-struct="qml"] .seat.me .seat-holecards.shc-l2 .pk,
html[data-seat-struct="qml"] .seat.me .seat-holecards.shc-l3 .pk {
  width: 37px !important; height: 52px !important;
  font-size: var(--fs-sm) !important; border-radius: 3px !important;
}
html[data-seat-struct="qml"] .seat.me .seat-avatar {
  width: 52px !important; height: 52px !important;
}
@media (orientation: landscape) and (max-height: 600px) {
  html[data-seat-struct="qml"] .seat.me .seat-holecards.shc-big .pk,
  html[data-seat-struct="qml"] .seat.me .seat-holecards.shc-l1 .pk,
  html[data-seat-struct="qml"] .seat.me .seat-holecards.shc-l2 .pk,
  html[data-seat-struct="qml"] .seat.me .seat-holecards.shc-l3 .pk {
    width: 36px !important; height: 50px !important; font-size: var(--fs-sm) !important;
  }
  html[data-seat-struct="qml"] .seat.me .seat-avatar {
    width: 50px !important; height: 50px !important;
  }
}
/* Pastille de niveau (1/2/3/4/5) sur le bouton. */
#g-cardzoom { position: relative; }
#g-cardzoom[data-lvl="1"]::after, #g-cardzoom[data-lvl="2"]::after, #g-cardzoom[data-lvl="3"]::after {
  content: attr(data-lvl); position: absolute; top: -4px; right: -4px;
  min-width: 13px; height: 13px; border-radius: 7px; font-size: 9px; line-height: 13px;
  background: #1a1205; color: var(--accent, #E3C800); font-weight: 700; text-align: center;
}

/* Layout flex : styles NON-pokerth UNIQUEMENT (le pokerth garde sa grille native). */
html:not([data-seat-struct="qml"]) body.adv-hide-pbar .seat.me { max-width: 190px !important; min-width: 0 !important; }
html:not([data-seat-struct="qml"]) body.adv-hide-pbar .seat.me .seat-plate {
  display: flex !important; flex-wrap: wrap; max-width: 190px !important;
  align-items: center; justify-content: center; gap: var(--sp-2);
}
html:not([data-seat-struct="qml"]) body.adv-hide-pbar .seat.me .seat-avatar { order: 1; flex: none; }
html:not([data-seat-struct="qml"]) body.adv-hide-pbar .seat.me .seat-holecards.shc-big {
  order: 2; display: flex !important; gap: var(--sp-1); flex: none; grid-column: auto; grid-row: auto;
}
html:not([data-seat-struct="qml"]) body.adv-hide-pbar .seat.me .seat-info {
  order: 3; flex-basis: 100%;
  display: flex; flex-direction: column; align-items: center; gap: 0;
}
/* ── Packs héritage (sans structure QML) : EMPREINTE bornée au gabarit de la
   bisection (114×84 adversaires, self ~150×96) — même placement et mêmes
   tailles que le pack PokerTH, habillage du pack conservé (0.3.696). ── */
html:not([data-seat-struct="qml"]) .seat:not(.me) { max-width: 114px !important; gap: 1px; }
html:not([data-seat-struct="qml"]) .seat:not(.me) .seat-avatar { width: 40px !important; height: 40px !important; font-size: var(--fs-md) !important; }
html:not([data-seat-struct="qml"]) .seat:not(.me) .seat-name { font-size: var(--fs-xs) !important; line-height: 1.15; max-width: 106px !important; }
html:not([data-seat-struct="qml"]) .seat:not(.me) .seat-money { font-size: var(--fs-xs) !important; line-height: 1.15; }
html:not([data-seat-struct="qml"]) .seat:not(.me) .seat-foot { margin-top: 1px; gap: var(--sp-1); }
/* Selecteur "Placement des sieges" dans Options avancees. */
.adv-row .adv-sel{flex:0 0 auto;min-width:132px}
.adv-lang-btn{-webkit-appearance:none;appearance:none;width:100%;display:flex;align-items:center;gap:var(--sp-3);background:var(--field-bg);border:1px solid var(--border);border-radius:2px;color:var(--cream);padding:8px 32px 8px 11px;font-family:var(--ff-mono);font-size:var(--fs-base);text-align:left;outline:none;cursor:pointer}
.adv-lang-btn:focus{border-color:var(--gold)}
.adv-lang-btn .lf{display:inline-flex;width:22px;flex:none}
.adv-lang-btn .ln{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Option "révéler mes cartes au tap" : les dos de la player-bar sont cliquables,
   lueur or pour signaler l'interaction (uniquement quand masquées). */
#g-myseat-cards.own-peek{cursor:pointer}
#g-myseat-cards.own-peek .pk.back{box-shadow:0 0 0 2px var(--accent,#E3C800),0 0 9px rgba(227,200,0,.55);transition:box-shadow .15s}
/* Anti-Call accidentel : bouton Call en attente de confirmation (2e tap). */
.btn-action.btn-call.confirm-call{background:linear-gradient(#f0a020,#b36b00);border-color:#ffd24d;color:#1a1205;animation:callConfirmPulse .85s ease-in-out infinite}
@keyframes callConfirmPulse{0%,100%{box-shadow:0 0 0 2px #ffd24d,0 0 12px rgba(240,160,32,.55)}50%{box-shadow:0 0 0 2px #ffd24d,0 0 19px rgba(240,160,32,.9)}}
/* Moniteur d'odds (option) : panneau compact en haut-gauche, semi-transparent,
   display-only (pointer-events:none -> ne bloque pas les sièges derrière). */
/* ── Panneau info unifié : onglets Historique/Chances (QML GameInfoPanel) ── */
.gip-tabs { display:flex; flex:1; margin-right:6px; min-width:0; }
/* Poignée de déplacement dédiée : les onglets couvrent presque toute la barre,
   le ⠿ garantit une prise visible (les onglets se traînent aussi, au seuil). */
.gip-grip { flex:0 0 auto; color:var(--chatlog-muted, var(--text-dim,#9aa3b5)); font-size:var(--fs-xs);
  padding:0 6px 0 2px; cursor:move; user-select:none; }
.gip-tab { flex:0 1 auto; background:transparent; border:none; border-bottom:2px solid transparent;
  color:var(--chatlog-muted, var(--text-dim, #9aa3b5)); font-size:var(--fs-2xs); letter-spacing:0.12em; text-transform:uppercase;
  padding:3px 12px 4px; cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  touch-action:none; }
.gip-tab.gip-on { color:var(--chatlog-text, var(--gold)); border-bottom-color:var(--chatlog-accent, var(--box-accent, var(--chatlog-muted, var(--gold-dim)))); font-weight:700; }
.gip-tab:hover { color:var(--chatlog-text, var(--gold)); }
/* Onglet Chances : mêmes classes que l'ancien moniteur, tailles panneau. */
#g-odds-body .odds-hd{font-weight:700;font-size:var(--fs-sm);color:var(--box-accent, var(--chatlog-text, var(--gold)));margin:2px 0 5px;letter-spacing:.3px}
#g-odds-body .odds-approx{opacity:.65;font-weight:400}
#g-odds-body .odds-wait{opacity:.6;padding:4px 0;font-size:var(--fs-sm)}
#g-odds-body .odds-row{display:flex;align-items:center;gap:var(--sp-2);height:22px;font-size:var(--fs-sm)}
#g-odds-body .odds-ico{width:26px;height:17px;object-fit:contain;flex:0 0 auto}
#g-odds-body .odds-cat{flex:0 0 44%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:.9}
#g-odds-body .odds-bar{flex:1;height:7px;background:rgba(var(--gold-rgb),.14);border-radius:var(--r-xs);overflow:hidden}
#g-odds-body .odds-bar>i{display:block;height:100%;width:0;background:linear-gradient(90deg,#8a93a6,#5a6478);border-radius:3px}
#g-odds-body .odds-pct{flex:0 0 34px;text-align:right;font-variant-numeric:tabular-nums;opacity:.92}
#g-odds-body .odds-row.warm .odds-bar>i{background:linear-gradient(90deg,#f0a020,#b36b00)}
#g-odds-body .odds-row.hot .odds-bar>i{background:linear-gradient(90deg,#50c878,#1e8c4e)}
#g-odds-body .odds-row.hot .odds-pct{color:#7ce0a0;font-weight:700}
/* Chances : compression douce quand le panneau est court (parité avec le fix
   QML « elastic row height ») — sous ~270px de corps, lignes 17px + icônes
   réduites pour garder les 10 catégories visibles d'un regard ; en dessous,
   la scrollbar revient comme avant (dégradation douce, CSS pur). */
#g-odds-body{container-type:size}
@container (max-height:270px){
  #g-odds-body .odds-hd{margin:1px 0 3px;font-size:var(--fs-xs)}
  #g-odds-body .odds-row{height:17px;font-size:var(--fs-xs)}
  #g-odds-body .odds-ico{width:20px;height:13px}
  #g-odds-body .odds-bar{height:5px}
}

/* Onglet Stats (panneau info) : tableau VPIP/PFR/AF... des joueurs de la table. */
#g-stats-body .odds-hd{font-weight:700;font-size:var(--fs-sm);color:var(--box-accent, var(--chatlog-text, var(--gold)));margin:2px 0 5px;letter-spacing:.3px}
#g-stats-body .stats-wait{opacity:.6;padding:4px 0;font-size:var(--fs-sm)}
#g-stats-body .stats-scope{display:flex;gap:var(--sp-1);margin:0 0 6px}
#g-stats-body .stats-scope-btn{flex:0 0 auto;background:transparent;border:1px solid var(--border, rgba(255,255,255,.15));color:var(--chatlog-muted, var(--text-dim,#9aa3b5));font-size:var(--fs-2xs);letter-spacing:.1em;text-transform:uppercase;padding:2px 9px;border-radius:var(--r-sm);cursor:pointer}
#g-stats-body .stats-scope-btn.on{color:var(--chatlog-text, var(--gold));border-color:var(--box-accent, var(--gold-dim));background:rgba(var(--gold-rgb),.12);font-weight:700}
#g-stats-body .stats-scroll{overflow-x:auto}
#g-stats-body .stats-table{border-collapse:collapse;width:100%;font-size:var(--fs-xs);font-variant-numeric:tabular-nums}
#g-stats-body .stats-table th{color:var(--chatlog-muted, var(--text-dim,#9aa3b5));font-weight:700;text-align:right;padding:2px 5px;border-bottom:1px solid var(--border, rgba(255,255,255,.12));white-space:nowrap;cursor:help}
#g-stats-body .stats-table td{text-align:right;padding:2px 5px;border-bottom:1px solid rgba(255,255,255,.05);white-space:nowrap}
#g-stats-body .stats-table .stats-name{text-align:left;max-width:118px;color:var(--chatlog-text, var(--text));font-weight:600}
#g-stats-body .stats-name-wrap{display:flex;align-items:center;gap:4px;min-width:0}
#g-stats-body .stats-name-txt{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Bouton « range » de chaque ligne : discret au repos, révélé au survol de la
   ligne. Au doigt il n'y a pas de survol → il reste lisible en permanence. */
#g-stats-body .stats-range-btn{flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:16px;height:16px;padding:0;background:transparent;border:none;border-radius:var(--r-xs);
  color:var(--chatlog-muted, var(--text-dim,#9aa3b5));opacity:.4;cursor:pointer;
  transition:opacity .12s, color .12s}
#g-stats-body .stats-table tbody tr:hover .stats-range-btn{opacity:1}
#g-stats-body .stats-range-btn:hover,
#g-stats-body .stats-range-btn:focus-visible{opacity:1;color:var(--gold)}
@media (hover:none){ #g-stats-body .stats-range-btn{opacity:.75} }
#g-stats-body .stats-table tbody tr:hover td{background:rgba(var(--gold-rgb),.06)}
#g-stats-body .stats-empty{text-align:center;opacity:.6;padding:10px 0}

/* Bouton d'export .pdb dans l'onglet Stats. */
#g-stats-body .stats-export-btn{margin-left:auto;background:transparent;border:1px solid var(--border, rgba(255,255,255,.15));color:var(--chatlog-muted, var(--text-dim,#9aa3b5));font-size:var(--fs-2xs);letter-spacing:.06em;padding:2px 9px;border-radius:var(--r-sm);cursor:pointer}
#g-stats-body .stats-export-btn:hover{color:var(--chatlog-text, var(--gold));border-color:var(--box-accent, var(--gold-dim))}
#g-stats-body .stats-export-btn:disabled{opacity:.5;cursor:default}
#g-stats-body .stats-scope{align-items:center}
/* Boutons d'export dans les options avancees (section Log). */
.adv-export-row{display:flex;gap:var(--sp-3);flex-wrap:wrap;margin-top:4px}
.adv-export-btn:disabled{opacity:.5;cursor:default}


#odds-monitor{--ws:1;position:fixed;top:54px;left:8px;z-index:42;width:132px;padding:calc(5px*var(--ws)) calc(7px*var(--ws)) calc(6px*var(--ws));background:var(--panel);border:1px solid var(--border);border-radius:calc(9px*var(--ws));box-shadow:0 4px 14px rgba(0,0,0,.42);font-size:calc(10px*var(--ws));line-height:1.25;color:var(--text);pointer-events:auto;user-select:none;cursor:move;touch-action:none}
#odds-monitor.odds-drag{opacity:.96;box-shadow:0 8px 24px rgba(0,0,0,.58);border-color:var(--border-hi)}
#odds-monitor .odds-hd{font-weight:700;font-size:calc(10.5px*var(--ws));color:var(--gold);margin-bottom:calc(3px*var(--ws));letter-spacing:.3px;padding-right:calc(14px*var(--ws))}
#odds-monitor .odds-approx{opacity:.65;font-weight:400}
#odds-monitor .odds-wait{opacity:.6;padding:2px 0}
#odds-monitor .odds-row{display:flex;align-items:center;gap:calc(4px*var(--ws));height:calc(13px*var(--ws))}
#odds-monitor .odds-cat{flex:0 0 calc(50px*var(--ws));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:.88}
#odds-monitor .odds-bar{flex:1;height:calc(6px*var(--ws));background:rgba(var(--gold-rgb),.14);border-radius:calc(3px*var(--ws));overflow:hidden}
#odds-monitor .odds-bar>i{display:block;height:100%;width:0;background:linear-gradient(90deg,#8a93a6,#5a6478);border-radius:calc(3px*var(--ws))}
#odds-monitor .odds-pct{flex:0 0 calc(27px*var(--ws));text-align:right;font-variant-numeric:tabular-nums;opacity:.92}
#odds-monitor .odds-row.warm .odds-bar>i{background:linear-gradient(90deg,#f0a020,#b36b00)}
#odds-monitor .odds-row.hot .odds-bar>i{background:linear-gradient(90deg,#50c878,#1e8c4e)}
#odds-monitor .odds-row.hot .odds-pct{color:#7ce0a0;font-weight:700}
@media (min-width:600px){#odds-monitor{resize:horizontal;overflow:hidden;min-width:104px;max-width:340px}}
@media (max-width:599.98px){#odds-monitor{--ws:.95;width:120px;top:48px}}
/* Fenetre flottante "Assistance" (force de la main) -- meme modele que #odds-monitor. */
#assist-win{--ws:1;position:fixed;top:54px;right:8px;z-index:42;width:150px;min-width:110px;max-width:210px;padding:calc(5px*var(--ws)) calc(8px*var(--ws)) calc(7px*var(--ws));background:var(--panel);border:1px solid var(--border);border-radius:calc(9px*var(--ws));box-shadow:0 4px 14px rgba(0,0,0,.42);color:var(--text);pointer-events:auto;user-select:none;cursor:move;touch-action:none}
#assist-win.hsw-drag{opacity:.96;box-shadow:0 8px 24px rgba(0,0,0,.58);border-color:var(--border-hi)}
#assist-win .hsw-hd{font-weight:700;font-size:calc(10.5px*var(--ws));color:var(--gold);margin-bottom:calc(5px*var(--ws));letter-spacing:.3px;padding-right:calc(14px*var(--ws))}
#assist-win .hs-lbl{display:block;background:transparent;border:0;padding:0;max-width:none;overflow:hidden}
#assist-win .hs-txt{display:block;font-size:calc(11.5px*var(--ws)) !important;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:none}
#assist-win #hand-strength{position:static;left:auto;right:auto;top:auto;height:auto;pointer-events:none;margin-top:calc(6px*var(--ws))}
#assist-win .hs-track{display:block;position:static;left:auto;right:auto;top:auto;height:calc(6px*var(--ws));border-radius:calc(3px*var(--ws))}
#assist-win .hs-fill{border-radius:calc(3px*var(--ws))}
@media (min-width:600px){#assist-win{resize:horizontal;overflow:hidden;max-width:340px}}
@media (max-width:599.98px){#assist-win{--ws:.96;width:auto;min-width:110px;top:48px}}
/* Assistance (force de la main) integree en tete de l'onglet « Chances » du
   panneau info unifie : meme rendu que l'ancienne fenetre flottante, mais en flux. */
#gip-assist{padding:6px 12px 7px;border-bottom:1px solid var(--chatlog-border,var(--border));background:var(--chatlog-bg, var(--field-bg))}
#gip-assist .odds-hd{font-weight:700;font-size:var(--fs-sm);color:var(--box-accent, var(--chatlog-text, var(--gold)));margin:0 0 5px;letter-spacing:.3px}
#gip-assist .hs-lbl{display:block;background:transparent;border:0;padding:0;max-width:none;overflow:hidden}
#gip-assist .hs-txt{display:block;font-size:var(--fs-sm) !important;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:none;color:var(--chatlog-text, var(--cream))}
#gip-assist #hand-strength{position:static;left:auto;right:auto;top:auto;height:auto;pointer-events:none;margin-top:6px}
#gip-assist .hs-track{display:block;position:static;left:auto;right:auto;top:auto;height:6px;border-radius:var(--r-xs);background:rgba(var(--gold-rgb),.14)}
#gip-assist .hs-fill{border-radius:3px}
/* ── Style « Segments » de l'assistance (Options avancées → Assistance) ────
   10 blocs qui se remplissent : la force se lit d'un coup d'œil sans lire le
   chiffre, et la rangée tient en 9 px de haut (barre + marges = 12 px).
   Le style actif est porté par <html data-assist-style>. */
#gip-assist .hs-segs{display:none;gap:3px;margin-top:6px}
#gip-assist .hs-segs i{flex:1;height:9px;border-radius:2px;background:rgba(var(--gold-rgb),.14);transition:background .18s ease}
#gip-assist .hs-segs i.on{background:var(--hs-col, var(--gold))}
:root[data-assist-style="segs"] #gip-assist .hs-segs{display:flex}
/* On masque la PISTE, pas #hand-strength : ce dernier recoit un display
   INLINE depuis le JS (_hsHide / renderPreFlopStrength), qui l'emporte sur
   toute regle CSS — d'ou la double barre constatee en web.30. La piste, elle,
   n'est jamais touchee en inline. margin/height a zero pour ne pas laisser de
   creux a la place de la barre. */
:root[data-assist-style="segs"] #gip-assist .hs-track{display:none}
:root[data-assist-style="segs"] #gip-assist #hand-strength{margin:0;height:0}
/* Fenêtre détachée : le ↩ est en overlay au coin haut-droit. On lui RÉSERVE une
   gouttière sur la ligne de libellé pour qu'il ne recouvre jamais le texte
   (demande narmod), et on resserre les marges pour gagner de la place. */
#g-assist-panel #gip-assist{padding:6px 9px 7px}
/* Reduction maximale : sous 52 px de haut, la fenetre ne garde que la rangee de
   segments — la couleur et le nombre de blocs suffisent a lire la force, et le
   libelle revient des qu'on agrandit. La classe est posee par un ResizeObserver
   (assist-pane.mjs) : PAS de container query ici, car #g-assist-panel n'a pas de
   taille CSS explicite et `container-type:size` la ferait collapser a 0. */
:root[data-assist-style="segs"] #g-assist-panel.assist-tiny #gip-assist{padding:4px 7px;justify-content:center}
:root[data-assist-style="segs"] #g-assist-panel.assist-tiny #gip-assist .hs-lbl{display:none}
:root[data-assist-style="segs"] #g-assist-panel.assist-tiny #gip-assist .hs-segs{margin-top:0}
#g-assist-panel #gip-assist .hs-lbl{padding-right:22px}
:root[data-assist-style="segs"] #g-assist-panel #gip-assist .hs-segs{margin-top:5px}
/* ── Assistance : rangée de titre + bouton détacher (v0.3.908) ── */
.gip-assist-hd-row{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);margin:0 0 5px}
.gip-assist-hd-row .odds-hd{margin:0}
.gip-assist-pop{flex:0 0 auto;background:none;border:none;cursor:pointer;font-size:var(--fs-md);line-height:1;padding:0 2px;color:var(--chatlog-muted, var(--gold-dim))}
.gip-assist-pop:hover{color:var(--chatlog-text, var(--gold))}
/* ── Poignée de séparation Assistance / Cotes (drag vertical) ── */
.gip-split{flex:0 0 auto;height:10px;cursor:row-resize;touch-action:none;display:flex;align-items:center;justify-content:center;background:var(--chatlog-bg, var(--field-bg));border-bottom:1px solid var(--chatlog-border,var(--border))}
.gip-split-grip{width:34px;height:3px;border-radius:2px;background:rgba(var(--gold-rgb),.28)}
.gip-split:hover .gip-split-grip{background:rgba(var(--gold-rgb),.55)}
/* ── Fenêtre flottante « Assistance » détachée (chrome comme #g-log-panel) ── */
#g-assist-panel{position:fixed;z-index:100;background:var(--panel);border:1px solid var(--border);box-shadow:0 4px 20px rgba(0,0,0,0.6);border-radius:8px;overflow:hidden;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none}
#g-assist-panel.floating-win{display:flex;flex-direction:column}
#g-assist-panel .gip-assist-wtitle{flex:1;margin:0 6px;font-size:var(--fs-2xs);color:var(--gold-dim);letter-spacing:0.15em;text-transform:uppercase;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#g-assist-panel .gip-assist-dock{flex:0 0 auto;background:none;border:none;color:var(--chatlog-text2, var(--chatlog-text, var(--text)));cursor:pointer;font-size:var(--fs-md);padding:0 4px}
#g-assist-panel .gip-assist-dock-mini{display:block;position:absolute;top:1px;right:2px;z-index:3;background:none;border:none;color:var(--chatlog-text2, var(--chatlog-text, var(--text)));opacity:.7;cursor:pointer;font-size:var(--fs-base);padding:2px 7px;line-height:1;border-radius:4px}
#g-assist-panel .gip-assist-dock-mini:hover{opacity:1;color:var(--gold)}
/* Détaché : le corps remplit la fenêtre et centre le contenu (fond uniforme →
   pas d'espace blanc). La mise à l'échelle du texte est gérée par zoom:var(--wz)
   posé sur les enfants directs de .floating-win.win-zoom (header + host). */
#g-assist-panel #g-assist-host{flex:1;min-height:0;display:flex}
#g-assist-panel #gip-assist{flex:1;min-height:0;display:flex;flex-direction:column;justify-content:center;border-bottom:none;overflow:hidden;height:auto}
#g-assist-panel #gip-assist .hs-txt{white-space:normal;overflow:visible;text-overflow:clip}
/* Tactile (pointer:coarse) : sur la pastille (~44px de haut), les 8 poignées
   de resize mangeaient les bords (n+s = 12px, coins 13px) et volaient le drag
   au doigt → Android/iPad « du mal à déplacer ». On ne garde que le coin SE
   (celui qui porte le grip visuel), agrandi à 24px. */
@media (pointer: coarse){
  #g-assist-panel .win-rsz{display:none}
  #g-assist-panel .win-rsz-se{display:block;width:24px;height:24px}
}
/* « Mode pastille » sur TOUTES les plateformes (mobile, tablette, desktop) :
   en-tête masqué, ↩ en overlay dans le coin, drag via toute la fenêtre
   (handle = fenêtre, câblé dans assist-pane.mjs), double-clic n'importe où
   pour recentrer. */
#g-assist-panel .g-chat-panel-header{display:none}
/* Croix de fermeture des fenetres flottantes (odds + assistance). Suit --ws + le theme. */
.win-x{position:absolute;top:calc(2px*var(--ws,1));right:calc(3px*var(--ws,1));width:calc(15px*var(--ws,1));height:calc(15px*var(--ws,1));line-height:calc(14px*var(--ws,1));text-align:center;border:0;background:transparent;color:var(--text);opacity:.5;font-size:calc(12px*var(--ws,1));cursor:pointer;padding:0;border-radius:calc(4px*var(--ws,1));z-index:3;font-family:inherit}
.win-x:hover{opacity:1;color:var(--gold)}
/* Raccourcis clavier personnalisables (Options avancées). */
.adv-keys{display:flex;flex-direction:column;gap:5px;margin-top:2px}
.adv-keyrow{display:flex;align-items:center;gap:9px}
.adv-keyrow>span{flex:1;font-size:var(--fs-md)}
.kb-btn{flex:0 0 auto;min-width:42px;height:30px;padding:0 9px;border-radius:7px;border:1px solid rgba(var(--gold-rgb),.28);background:rgba(var(--gold-rgb),.08);color:var(--text);font-family:var(--ff-mono,monospace);font-weight:700;font-size:var(--fs-md);text-transform:uppercase;cursor:pointer;transition:background .12s,border-color .12s}
.kb-btn:hover{background:rgba(var(--gold-rgb),.16)}
.kb-btn.kb-active{border-color:var(--gold);background:rgba(var(--gold-rgb),.18);color:var(--gold);animation:kbPulse .9s ease-in-out infinite}
@keyframes kbPulse{0%,100%{box-shadow:0 0 0 1px var(--gold,#E3C800)}50%{box-shadow:0 0 0 2px var(--gold,#E3C800),0 0 8px rgba(227,200,0,.5)}}
.adv-keyhint{display:flex;align-items:center;gap:9px;margin-top:3px}
.adv-keyhint>span{flex:1;font-size:var(--fs-xs);opacity:.62;line-height:1.25}
.kb-reset{flex:0 0 auto;height:28px;padding:0 11px;border-radius:7px;border:1px solid rgba(var(--gold-rgb),.24);background:rgba(var(--gold-rgb),.06);color:var(--text);font-size:var(--fs-sm);cursor:pointer}
.kb-reset:hover{background:rgba(var(--gold-rgb),.13)}

/* ===== Options avancées : navigation par catégories (parité dialogue officiel PokerTH) ===== */
/* Tablette/desktop : sidebar icône+texte à gauche. Téléphone : barre d'icônes seules en haut. */
.adv-card{max-width:620px;container-type:inline-size}
.adv-layout{display:flex;gap:14px;margin-top:10px;align-items:stretch}
.adv-nav{flex:0 0 212px;display:flex;flex-direction:column;gap:var(--sp-0);border-right:1px solid var(--border);padding-right:10px;overflow-y:auto;max-height:62vh} /* 168→212 px : libellés de catégories entiers (« Paramètres par défaut »…) — demande narmod 2026-07-18 */
.adv-cat{display:flex;align-items:center;gap:var(--sp-4);width:100%;padding:8px 9px;border:0;border-radius:8px;background:transparent;color:var(--text);font-size:var(--fs-base);font-weight:600;text-align:left;cursor:pointer;transition:background .12s,color .12s}
.adv-cat:hover:not(:disabled):not(.is-active){background:rgba(var(--sel-rgb,var(--gold-rgb)),.08)}
.adv-cat .adv-cat-ic{flex:0 0 auto;width:19px;height:19px;display:inline-flex;align-items:center;justify-content:center;opacity:.85}
.adv-cat .adv-cat-ic svg{width:19px;height:19px;display:block}
.adv-cat .adv-cat-lbl{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.adv-cat.is-active{background:rgba(var(--sel-rgb,var(--gold-rgb)),.13);color:var(--sel,var(--gold));box-shadow:inset 2px 0 0 0 var(--sel,var(--gold))}
.adv-cat.is-active .adv-cat-ic{opacity:1}
.adv-cat:disabled,.adv-cat[disabled]{opacity:.38;cursor:default}
.adv-panels{flex:1;min-width:0;max-height:62vh;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding-right:2px} /* overflow-x:hidden : un contenu trop large (selects/inputs de la categorie Internet) faisait panner toute la fenetre lateralement au lieu de ne defiler qu'en vertical (constate narmod 2026-07-19) */
.adv-panel{display:none}
.adv-panel.is-active{display:block}
.adv-subtabs{display:flex;gap:var(--sp-2);margin:0 0 10px}
.adv-subtab{flex:1;padding:7px 6px;cursor:pointer;background:var(--inset);color:var(--text);border:1px solid var(--border);border-radius:var(--r-sm);font-size:var(--fs-base);font-weight:600;letter-spacing:0.02em;transition:background .15s,border-color .15s,color .15s}
.adv-subtab:hover{border-color:var(--border-hi);color:var(--text-hi)}
.adv-subtab.is-active{background:rgba(var(--sel-rgb,var(--gold-rgb)),.15);border-color:var(--sel,var(--gold));color:var(--sel,var(--gold))}
.adv-uipanel{display:none}
.adv-uipanel.is-active{display:block}
.adv-panels .adv-body{max-height:none;overflow:visible;margin-top:0}
/* Recherche dans les Options avancees (modules/ui/adv-search.mjs) : le champ
   reprend la coque de celui de l'Aide, et la liste de resultats prend la place
   des panneaux de categories tant qu'une recherche est en cours. */
.adv-panels.is-searching > .adv-panel.is-active{display:none}
#adv-results{padding:2px 2px 8px}
.adv-hit{animation:helpHit 1.5s ease;border-radius:6px}
/* Panneaux « reliés » : description + bouton d'accès à l'UI existante. */
.adv-link{display:flex;flex-direction:column;gap:var(--sp-5);align-items:flex-start;padding:6px 2px}
.adv-link-desc{font-size:var(--fs-base);line-height:1.4;color:var(--text)}
.adv-link-btn{align-self:stretch;padding:11px 14px;border-radius:var(--r-md);border:1px solid var(--gold-dim);background:rgba(var(--gold-rgb),.12);color:var(--gold);font-size:var(--fs-md);font-weight:700;cursor:pointer;transition:background .12s,border-color .12s,transform .08s}
.adv-link-btn:hover{background:rgba(var(--gold-rgb),.2);border-color:var(--gold)}
.adv-link-btn:active{transform:scale(.985)}
.adv-link-btn.adv-link-danger{border-color:var(--border-hi);background:transparent;color:var(--cream)}
.adv-link-btn.adv-link-danger:hover{background:var(--inset);border-color:var(--border-hi)}
.adv-srv{display:flex;align-items:center;gap:var(--sp-3);font-size:var(--fs-sm);color:var(--text);opacity:.82}
.adv-srv .adv-srv-host{color:var(--text);font-weight:600;font-family:var(--ff-mono,monospace);word-break:break-all}
.adv-soon-tag{display:inline-block;font-size:var(--fs-xs);letter-spacing:.08em;text-transform:uppercase;color:var(--gold-dim,#b09a00);border:1px solid var(--border);border-radius:11px;padding:3px 10px;opacity:.85}
.adv-panel-off{opacity:.55}
/* Téléphone : la nav devient une barre d'icônes scrollable en haut (libellés cachés).
   Seuil 600 = canon QML (« compact » = largeur < 600), aligné sur le gate JS (_advSetupFloat). */
@media (max-width:599.98px){
  .adv-card{max-width:560px}
  .adv-layout{flex-direction:column;gap:10px}
  .adv-nav{flex:0 0 auto;flex-direction:row;gap:var(--sp-1);border-right:0;border-bottom:1px solid var(--border);padding:0 0 8px;max-height:none;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}
  .adv-cat{flex:0 0 auto;width:auto;padding:7px 10px;border-radius:var(--r-md);justify-content:center}
  .adv-cat .adv-cat-lbl{display:none}
  .adv-cat.is-active{background:rgba(var(--sel-rgb,var(--gold-rgb)),.16);box-shadow:inset 0 -2px 0 0 var(--sel,var(--gold))}
  .adv-panels{max-height:none;min-height:0} /* la hauteur vient de la carte épinglée ci-dessous */
}
/* Portrait mobile : carte ÉPINGLÉE haut et bas (feuille plein écran à marges,
   demande narmod 2026-07-19). Plus aucun saut de taille entre catégories :
   la hauteur est fixée par les ancres top/bottom, le contenu défile dedans.
   NB : l'ancienne approche height:56dvh sur .adv-panels était inopérante —
   flex:1 (= flex-basis:0%) écrase height sur l'axe principal d'une colonne. */
@media (max-width:599.98px) and (orientation:portrait){
  #adv-modal .adv-card{width:calc(100% - 20px);max-width:none;height:calc(100% - max(28px, 2*env(safe-area-inset-top), 2*env(safe-area-inset-bottom)));margin:0;display:flex;flex-direction:column;overflow:hidden} /* dimensionnee DANS #adv-modal (inset:0, flex centre) et non en position:fixed : un ancetre transforme detournait le fixed et decalait la feuille (constate narmod 2026-07-19). Hauteur constante -> aucun saut, centrage garanti par le flex du conteneur. */
  #adv-modal .adv-card .adv-layout{flex:1 1 auto;min-height:0}
  #adv-modal .adv-card .adv-nav{flex:0 0 auto}
  #adv-modal .adv-card .adv-panels{flex:1 1 auto;min-height:0;height:auto}
}
/* Options avancees en fenetre flottante (largeur >= 600, canon QML), pas sur mobile.
   Drag/resize/zoom via le systeme generique (cf. _advSetupFloat / _enableFloating). */
@media (min-width:600px){
  .adv-card{overflow:hidden;width:620px;height:min(82vh,640px);min-width:380px;min-height:360px;max-width:94vw;max-height:92vh} /* min-width 524→380 : aligné sur minW JS, la barre d'icônes (container query) légitime une fenêtre étroite */
  #adv-modal .adv-card.floating-win{position:fixed;margin:0;max-width:none;max-height:none} /* fenetre du systeme generique (_enableFloating) ; bornes gerees en JS (_clampWinToViewport) */ /* min-width 480→524 : compense la sidebar élargie (212 px) */
  .adv-card .adv-layout{flex:1 1 auto;min-height:0}
  .adv-card .adv-nav{max-height:none;min-height:0}
  .adv-card .adv-panels{max-height:none;min-height:0}
  .adv-card .km-title{cursor:move;user-select:none;touch-action:none}
}
/* Layout du dialogue piloté par la largeur de la FENÊTRE elle-même (container
   query), plus seulement par le viewport (demande narmod 2026-07-19) : une
   fenêtre flottante rétrécie sous 600 px passe en barre d'icônes (« portrait »),
   ré-élargie elle repasse en sidebar (« landscape ») — même logique responsive
   que le client QML applique à sa propre fenêtre, et même comportement que les
   autres fenêtres flottantes. Les règles @media restent telles quelles en repli
   (mobile + navigateurs sans container queries). Seuil 600 = canon QML. */
@container (max-width:599.98px){
  .adv-layout{flex-direction:column;gap:10px}
  .adv-nav{flex:0 0 auto;flex-direction:row;gap:var(--sp-1);border-right:0;border-bottom:1px solid var(--border);padding:0 0 8px;max-height:none;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}
  .adv-cat{flex:0 0 auto;width:auto;padding:7px 10px;border-radius:var(--r-md);justify-content:center}
  .adv-cat .adv-cat-lbl{display:none}
  .adv-cat.is-active{background:rgba(var(--sel-rgb,var(--gold-rgb)),.16);box-shadow:inset 0 -2px 0 0 var(--sel,var(--gold))}
}

/* ── Badge « main gagnante » sous les community cards (parité QML WinningHandBadge,
   bible 28/06 §9) : pastille vert sombre rgba(0.05,0.24,0.05,0.92), bord #FFD700,
   halo or. Dans .felt-oval → suit le zoom/pan comme les cartes. Visible pendant
   tout le showdown, masqué à la main suivante. top posé en JS (sous #g-comm). ── */
.win-hand-badge {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  z-index: 50;                 /* au-dessus des cartes, sous les overlays */
  /* Taille suit communityScale (--comm-scale, posé sur <html>) — comme le
     badge du pot (.pot-badge) : le libellé de résultat (« Carte haute »,
     « Brelan d'As »…) s'adapte à l'écran au lieu d'un 12 px fixe. Base QML
     12 px bold, plancher 10 px. */
  padding: max(2px, calc(3px * var(--comm-scale, 1))) max(7px, calc(12px * var(--comm-scale, 1)));
  border-radius: var(--r-pill);
  background: rgba(13, 61, 13, 0.92);
  border: 1px solid #FFD700;
  box-shadow: 0 0 10px rgba(255, 215, 0, 0.45);
  color: #FFD700;              /* AppText QML : or #FFD700, 12 px bold */
  font-size: max(10px, calc(12px * var(--comm-scale, 1)));
  font-weight: 700;
  letter-spacing: 0.04em;
  white-space: nowrap;
  pointer-events: none;
}
/* Pop d'apparition (winHandPop QML : scale 1→1.18 en 110 ms OutQuad puis
   retour 1.0 en 170 ms OutBack — léger passage sous 1.0 avant stabilisation). */
.win-hand-badge.win-pop { animation: winHandPop 0.28s ease-out; }
@keyframes winHandPop {
  0% { scale: 1; }
  39% { scale: 1.18; }
  82% { scale: 0.985; }
  100% { scale: 1; }
}

/* ── Mode « effets réduits » (parité QmlReduceEffects, bible §1) : coupe les
   effets décoratifs coûteux — ombres, glow, backdrop-filter — pour les
   appareils faibles. Opt-in : Options avancées → Tapis. ── */
html.reduce-fx *, html.reduce-fx *::before, html.reduce-fx *::after {
  box-shadow: none !important;
  text-shadow: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
/* Effets réduits : coupe AUSSI les animations « pop » des pucks
   (Dealer/SB/BB, drapeau — chipPop) et des badges d'action
   (seatBadgePop/stripPop) — demande sp0ck 2026-07-17. Les éléments
   apparaissent alors sans animation, à leur état final. */
html.reduce-fx .dealer-chip,
html.reduce-fx .blind-chip,
html.reduce-fx .seat-flag,
html.reduce-fx .seat-action-badge {
  animation: none !important;
}
/* Anti-replay (sp0ck 2026-07-17) : un re-render SANS changement d'état pour ce
   siège ne rejoue pas les pops (pucks/drapeau/badge) — classe posée par
   renderSeats quand la signature du siège est identique au rendu précédent. */
.dealer-chip.no-pop, .blind-chip.no-pop, .seat-flag.no-pop, .seat-action-badge.no-pop,
.seat-bet.no-pop {
  animation: none !important;
}

/* ── Icônes des 10 mains dans le moniteur d'odds (resources/hands/ QML) ── */
#odds-monitor .odds-ico {
  flex: 0 0 auto;
  width: calc(19px * var(--ws));
  height: calc(12.5px * var(--ws));
  opacity: 0.9;
}

/* ── LobbyStatsBar (parité QML) : bandeau discret sous la liste des tables ── */
.lobby-statsbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 5px 10px;
  font-size: var(--fs-xs);
  color: var(--text-dim, #8b93a7);
  border-top: 1px solid var(--line, #2c3548);
}
.lobby-statsbar a { color: var(--gold-dim, #9a824a); text-decoration: none; }
.lobby-statsbar a:hover { text-decoration: underline; }

/* ── Sondages produit (modale lobby, extension web) ───────────────────────
   Meme moule que la modale de bienvenue du monolithe : fond assombri, carte
   centree, tokens de theme. Voir modules/ui/poll.mjs. */
.poll-back {
  position: fixed; inset: 0; z-index: 10000;
  display: flex; align-items: center; justify-content: center; padding: 18px;
  background: rgba(0, 0, 0, .55);
}
.poll-card {
  max-width: 420px; width: 100%; max-height: 84vh;
  display: flex; flex-direction: column;
  background: var(--modal-bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, .5);
}
.poll-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 13px 18px; border-bottom: 1px solid var(--border);
}
.poll-title {
  font-family: var(--ff-display); font-weight: 700;
  font-size: var(--fs-3xs, .6rem); letter-spacing: .15em; text-transform: uppercase;
  color: var(--gold-dim, #9a824a);
}
.poll-x {
  border: 0; background: none; cursor: pointer; padding: 0 2px; line-height: 1;
  font-size: .85rem; color: var(--text-dim, #8b93a7);
}
.poll-x:hover { color: var(--text-hi, #e8ecf5); }
.poll-body { padding: 16px 18px; overflow: auto; display: flex; flex-direction: column; gap: 12px; }
.poll-q { font-size: .95rem; line-height: 1.45; color: var(--text-hi, #e8ecf5); }
.poll-opts { display: flex; flex-direction: column; gap: 7px; }
.poll-opt {
  width: 100%; text-align: left;
  padding: 10px 12px; border-radius: 10px;
  border: 1px solid var(--border, #39435a);
  background: var(--panel, #232b3b); color: var(--text, #c3cad9);
  font-size: .88rem; cursor: pointer;
}
.poll-opt:hover:not(:disabled) { border-color: var(--gold-dim, #9a824a); color: var(--text-hi, #e8ecf5); }
.poll-opt:disabled { opacity: .5; cursor: default; }
.poll-err:empty { display: none; }
.poll-err { font-size: var(--fs-xs, .72rem); color: var(--red, #d94040); }
/* Resultats : une ligne par option, barre proportionnelle, choix de
   l'utilisateur souligne par l'accent or. */
.poll-res { display: flex; flex-direction: column; gap: 6px; }
.poll-row { display: flex; align-items: center; gap: 8px; font-size: .82rem; }
.poll-lb { flex: 0 0 38%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text, #c3cad9); }
.poll-row.is-mine .poll-lb { color: var(--gold, #e3c800); }
.poll-track { flex: 1; min-width: 0; height: 7px; border-radius: 4px; background: var(--inset, rgba(0,0,0,.28)); overflow: hidden; }
.poll-track i { display: block; height: 100%; border-radius: 4px; background: var(--gold-dim, #9a824a); }
.poll-row.is-mine .poll-track i { background: var(--gold, #e3c800); }
.poll-pct { flex: 0 0 34px; text-align: right; color: var(--text-dim, #8b93a7); }
.poll-foot { font-size: var(--fs-3xs, .6rem); color: var(--text-dim, #8b93a7); }

/* Palettes officielles : pas de trait au-dessus de la statsbar (parite QML). */
:root[data-theme^="pokerth"] .lobby-statsbar { border-top: none; }

/* ── Page « À propos de PokerTH » (parité AboutPage QML 2.1.3) ── */
#about-page { position:fixed; inset:0; z-index:120; display:none; flex-direction:column; background:var(--felt); overflow-y:auto; }

/* ── Page Privacy in-app : même entête cp-header que « Créer une partie ». ── */
#privacy-page { position:fixed; inset:0; z-index:120; display:none; flex-direction:column; background:var(--felt); }
.pv-scroll { flex:1; overflow-y:auto; }
.pv-body { max-width:720px; margin:0 auto; padding:10px 18px 48px; color:var(--text); font-size:var(--fs-md); line-height:1.65; }
.pv-updated { color:var(--text-dim); font-size:var(--fs-base); margin:4px 0 14px; }
.pv-body h2 { color:var(--text-hi); font-size:var(--fs-lg); margin:22px 0 8px; padding-top:16px; border-top:1px solid var(--border-hi); }
.pv-body h2:first-of-type { border-top:0; padding-top:0; }
.pv-body a { color:var(--gold); }
.pv-body strong { color:var(--text-hi); font-weight:600; }
.pv-body ul { padding-left:20px; }
.pv-body li { margin:5px 0; }
.ab-head { display:flex; align-items:center; padding:10px 10px 0; flex-shrink:0; }
/* Installed PWA (iOS black-translucent / Android edge-to-edge): #about-page is
   position:fixed inset:0, so it spans the whole screen and its back button
   would sit under the status bar. Keep the 10px base when env() is 0. */
@media (display-mode: fullscreen), (display-mode: standalone) {
  .ab-head {
    padding-top: max(10px, env(safe-area-inset-top));
    padding-left: max(10px, env(safe-area-inset-left));
    padding-right: max(10px, env(safe-area-inset-right));
  }
}
.ab-back { background:none; border:none; cursor:pointer; color:var(--text-hi); font-size:1.7rem; line-height:1; padding:2px 12px; border-radius:8px; }
.ab-back:hover { background:var(--inset-hi); }
.ab-title { font-size:1.25rem; font-weight:700; color:var(--text-hi); padding:4px 16px 10px; flex-shrink:0; }
.ab-tabs { display:flex; margin:0 16px; flex-shrink:0; }
/* Libellé trop long pour la largeur de l'onglet (portrait étroit, langues
   longues) : retour à la ligne sur 2 lignes max au lieu d'une troncature
   (« Bibliothèques tie… ») ; la barre grandit avec (parité QML CustomTabBar,
   stable 27/08/2026 — wrapMode WordWrap + maximumLineCount 2).
   Le clamp vit sur un SPAN interne (.ab-tab-lbl) : un <button> garde son
   rendu interne propre et ignore display:-webkit-box, le clamp posé dessus
   était donc inopérant (constat iPhone, web.149). Et la coupe se fait par
   césure typographique (hyphens:auto — le lang du document suit la langue
   de l'interface), plus par break-word qui tranchait en plein mot sans
   trait d'union (« Journa / l des / modifi… ») ; break-word ne reste qu'en
   ultime repli pour les langues sans dictionnaire de césure. */
.ab-tab { flex:1 1 0; min-width:0; border:none; cursor:pointer; padding:8px 4px; font-size:var(--fs-base); color:var(--text-hi); background:var(--inset); font-family:var(--ff-display); white-space:normal; text-align:center; }
.ab-tab-lbl { display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; hyphens:auto; overflow-wrap:break-word; }
/* Six onglets sur un téléphone : police réduite pour que des mots entiers
   tiennent sur la ligne avant de recourir à la césure. */
@media (max-width: 560px) { .ab-tab { font-size:var(--fs-sm); } }
.ab-tab.active { background:var(--panel-hi); font-weight:600; }
:root[data-theme="pokerth-light"] .ab-tab { background:#dfe3ea; }
:root[data-theme="pokerth-light"] .ab-tab.active { background:#a9b2c6; }
.ab-body { flex:1 1 auto; padding:14px 16px 28px; }
.ab-logo { text-align:center; margin:8px 0 16px; }
.ab-suits { font-size:var(--fs-md); letter-spacing:0.3em; margin-top:8px; opacity:0.9; color:var(--text-hi); }
.ab-suits .r { color:#c0392b; }
.ab-vername { font-weight:700; font-size:var(--fs-lg); margin-top:8px; color:var(--text-hi); }
.ab-feats { margin:14px 0; color:var(--text-hi); font-size:var(--fs-md); line-height:1.75; }
/* Onglet Changelog : même typographie que les autres onglets (--fs-md,
   police héritée), entêtes de version en or, contenu anglais forcé LTR.
   Les sous-onglets Client web | Autres clients sont une déclinaison
   réduite des .ab-tab. */
.ab-cl-tabs { display: flex; gap: 2px; margin-bottom: 12px; }
.ab-cl-tab { flex: 0 1 auto; border: none; cursor: pointer; padding: 6px 16px; font-size: var(--fs-sm); color: var(--text-hi); background: var(--inset); font-family: var(--ff-display); white-space: nowrap; }
.ab-cl-tab.active { background: var(--panel-hi); font-weight: 600; }
:root[data-theme="pokerth-light"] .ab-cl-tab { background: #dfe3ea; }
:root[data-theme="pokerth-light"] .ab-cl-tab.active { background: #a9b2c6; }
.ab-cl { font-size: var(--fs-md); line-height: 1.6; overflow-wrap: anywhere; direction: ltr; text-align: left; color: var(--text-hi); max-width: 980px; }
.ab-cl-ver { color: var(--gold, #d4af37); font-weight: 700; margin: 12px 0 4px; }
.ab-cl-ver:first-child { margin-top: 0; }
.ab-cl-line { color: var(--text-hi); }
.ab-cl-gap { height: 8px; }
/* Sous-titres de regroupement du changelog (New / Improvements / Bug fixes) */
.ab-cl-grp { color: var(--text-dim, #9aa3b2); font-weight: 600; font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: .06em; margin: 10px 0 2px; }
.ab-cl-item { padding-left: 12px; }
.ab-cl-item::before { content: '\2013\00a0'; color: var(--text-dim, #9aa3b2); margin-left: -12px; }
.ab-copy { color:var(--text); font-size:var(--fs-base); margin-top:10px; }
.ab-net { text-align:right; margin-top:16px; }
.ab-pane a { color:var(--info,#2a6fb0); }
.ab-pane p { color:var(--text-hi); font-size:var(--fs-md); line-height:1.6; max-width:820px; }
.ab-pane ul { color:var(--text-hi); font-size:var(--fs-md); line-height:1.9; padding-left:22px; }

/* ── Galerie d'avatars d'exemple (client Qt-Widgets officiel) ── */
.avgal-overlay {
  position: fixed; inset: 0; z-index: 1200;
  display: none; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.55);
}
.avgal-box {
  position: relative;
  background: var(--panel, #1c2230);
  border: 1px solid var(--line, #2c3548);
  border-radius: 14px;
  width: min(560px, 92vw);
  max-height: 80vh;
  display: flex; flex-direction: column;
  padding: 14px;
}
.avgal-title {
  font-weight: 700; margin: 0 22px 8px 0;
  color: var(--gold, #c8a84a);
  letter-spacing: 0.06em; text-transform: uppercase; font-size: var(--fs-sm);
}
.avgal-body { overflow-y: auto; }
.avgal-cat {
  margin: 10px 0 6px; font-size: var(--fs-xs);
  color: var(--text-dim, #8b93a7);
  text-transform: uppercase; letter-spacing: 0.1em;
}
.avgal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(52px, 1fr));
  gap: var(--sp-2);
}
.avgal-item {
  padding: 0; border: 1px solid transparent; border-radius: 8px;
  background: transparent; cursor: pointer;
}
.avgal-item:hover, .avgal-item:focus-visible { border-color: var(--gold, #c8a84a); }
.avgal-item img {
  width: 100%; aspect-ratio: 1; display: block;
  border-radius: 7px; object-fit: cover;
}

/* ── Picker emoji complet : panneau plus haut + titres de sections ── */
.chat-emoji-panel { max-height: 180px; }
.chat-emoji-sec {
  grid-column: 1 / -1;
  font-size: var(--fs-2xs);
  color: var(--text-dim, #8b93a7);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 4px 2px 1px;
}

/* ── Indicateur de ping sur mon avatar (opt-in, coin de la player bar) ── */
.player-bar-left { position: relative; }
.ping-dot {
  position: absolute;
  left: 1px; top: 1px;
  width: 9px; height: 9px;
  border-radius: 50%;
  border: 1.5px solid var(--panel, #1c2230);
  z-index: 3;
  pointer-events: none;
}

/* ── Activité joueur dans le panneau « Joueurs » : 🎮 nom de la table ── */
/* Variante A : sections « En partie / Au lobby » ; le nom de la table
   redevient une sous-ligne discrète sous le pseudo. */
.pl-row .pl-game {
  display: block;
  font-size: var(--fs-2xs);
  color: var(--text-dim, #8b93a7);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  line-height: 1.2;
}
.players-list .pl-sec {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 6px 2px 4px;
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-dim, #c8a84a);
  border-bottom: 1px solid var(--inset, rgba(255,255,255,0.08));
}
.players-list .pl-sec-n { color: var(--text-dim, #8b93a7); font-weight: 600; }

/* ── Bouton « Show » post-main : flotte AU-DESSUS de mes cartes (player bar),
   pastille or, visible seulement pendant la fenêtre d'attente. ── */
.pb-cards { position: relative; }
.show-cards-btn {
  position: absolute;
  left: 50%;
  top: -26px;
  transform: translateX(-50%);
  z-index: 6;
  padding: 3px 12px;
  border-radius: var(--r-pill);
  border: 1px solid var(--gold, #c8a84a);
  background: var(--panel, #1c2230);
  color: var(--gold, #c8a84a);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.05em;
  cursor: pointer;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
}
.show-cards-btn:hover { background: var(--gold, #c8a84a); color: #1c2230; }

/* ===== Boutons d'action : pack « PokerTH » (axe data-buttons="pokerth") ====
   GameActionBar.qml au pixel : radius 9, dégradé Top→Bottom par action
   (§12.3), bord = couleur Edge, police 15px bold letterSpacing .5 (12px en
   landscapeCompact), rangée 54px (40px landscapeCompact), press scale .96
   (90ms). « Call $X » passe sur 2 lignes en portrait (1 ligne en compact). */
html[data-buttons="pokerth"] .btn-action {
  border-radius: var(--r-md);
  font-weight: 700;
  letter-spacing: 0.5px;
  font-size: calc(15px * var(--bar-kf, 1));
  color: #fff;
  min-height: calc(54px * var(--bar-k, 1));
  border-width: 1px;
  border-style: solid;
  transition: transform 0.09s ease-out;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}
html[data-buttons="pokerth"] .btn-action:active:not([disabled]) { transform: scale(0.96); }
html[data-buttons="pokerth"] .btn-fold {
  background: linear-gradient(to bottom, #d94040, #8b1a1a); border-color: #e87070;
}
html[data-buttons="pokerth"] .btn-call,
html[data-buttons="pokerth"] .btn-check {
  background: linear-gradient(to bottom, #4080d8, #1a3d8b); border-color: #6aa0e8;
}
html[data-buttons="pokerth"] .btn-raise {
  background: linear-gradient(to bottom, #50b840, #1e6614); border-color: #7ad06a;
}
html[data-buttons="pokerth"] .btn-allin {
  background: linear-gradient(to bottom, #9e2a2a, #5c1111); border-color: #ef5350;
}
html[data-buttons="pokerth"] .btn-action[disabled] { filter: grayscale(0.55) brightness(0.7); }
/* Portrait : montant du Call/All-In sur une 2e ligne (libellé QML 2 lignes). */
@media (orientation: portrait) {
  html[data-buttons="pokerth"] .btn-call b,
  html[data-buttons="pokerth"] .btn-allin b { display: block; font-size: 0.88em; line-height: 1.15; }
}
/* landscapeCompact (bible §2) : rangée 40px, police 12px, 1 ligne.
   Mobile = paysage h<600 ; desktop = ratio>2.1 && h<1300 (un 16:9 MAXIMISE,
   ratio ~1.91, reste non-compact — aligne sur le client officiel 2.1.1). */
@media (orientation: landscape) and (max-height: 600px),
       (min-aspect-ratio: 210/100) and (max-height: 799px) {
  html[data-buttons="pokerth"] .btn-action { min-height: 40px; font-size: 12px; padding-top: 6px; padding-bottom: 6px; }
}

/* ── Lueur chaude derrière les cartes communes (bible §9 :
   rgba(1, 0.93, 0.72, 0.12) floutée). ::before sous les cartes. ── */
#g-comm { position: relative; isolation: isolate; }
#g-comm::before {
  content: '';
  position: absolute;
  inset: -16px -24px;
  background: radial-gradient(ellipse at center, rgba(255, 237, 184, 0.12), transparent 70%);
  filter: blur(10px);
  z-index: -1;
  pointer-events: none;
}

/* ── Pop du badge de pot à chaque hausse (bible §9). ── */
@keyframes potPop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.16); }
  100% { transform: scale(1); }
}
.pot-pop { animation: potPop 0.3s ease-out; display: inline-block; }

/* ── Tri du panneau Joueurs (A–Z / pays) + 5e pastille de filtre ── */
.players-search { display: flex; align-items: center; gap: var(--sp-2); }
.players-search input { flex: 1; min-width: 0; }
.pl-sort { display: flex; gap: var(--sp-0); flex: none; }
.pl-sort-btn {
  background: var(--inset, rgba(255,255,255,0.06));
  border: 1px solid var(--border, rgba(255,255,255,0.14));
  border-radius: 8px;
  color: var(--text-dim, #8b93a7);
  cursor: pointer;
  font-size: var(--fs-2xs);
  font-weight: 700;
  padding: 5px 8px;
  line-height: 1;
}
.pl-sort-btn.active {
  color: var(--gold, #c8a84a);
  border-color: var(--gold-dim, #c8a84a);
}
.filter-chips { flex-wrap: wrap; }

/* ═══════════════════════════════════════════════════════════════════
   Lobby responsive v2 — parité QML LobbyPage (Phase 1b)
   ≥900 : 3 colonnes REDIMENSIONNABLES (poignées ; voisins suivent).
          Colonne droite = Infos (haut) / Chat (bas) + séparateur vertical.
   <900  : Parties + Chat INTÉGRÉ (empilés) ; Joueurs & Infos en slide-in.
   ═══════════════════════════════════════════════════════════════════ */

#s-lobby .lobby-grid > .create-col { display: none; }

/* Barre du bas : Joueur + Créer une partie (les 2 modes, épinglée en pied) */
.lobby-footbar { display:flex; align-items:center; gap:var(--sp-5); padding:8px 14px;
  border-top:1px solid var(--border); background:var(--panel); flex-shrink:0;
  position:sticky; bottom:0; z-index:20; }
.lobby-footbar .lfb-name { color:var(--text-hi); font-weight:700; font-size:var(--fs-base); white-space:nowrap; }
.lobby-footbar .lfb-name b { color:var(--gold); }
/* Boutons de la barre du bas. Forme commune : largeurs ÉGALES et adaptatives —
   « Joueur : … » (nowrap) garde sa place et les boutons rétrécissent quand le
   pseudo est long (flex:1 1 0 + min-width:0 + ellipsis). Couleurs : Rejoindre en
   vert et Spectateur en bleu (comme les boutons des lignes) ; Créer en or/neutre. */
.lobby-footbar .lfb-create,
.lobby-footbar .lfb-join,
.lobby-footbar .lfb-spectate {
  flex:1 1 0; min-width:0; text-align:center;
  border:1px solid transparent; border-radius:8px; padding:9px 14px; cursor:pointer;
  font-family:var(--ff-display); font-weight:600; font-size:var(--fs-md);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* Créer : palette QML (or/neutre) */
.lobby-footbar .lfb-create { border-color:transparent; background:#2b3444; color:#ffffff; }
.lobby-footbar .lfb-create:hover { background:#384252; }
/* Palette claire : bouton gris neutre (col500 light), texte sombre (parite QML). */
:root[data-theme="pokerth-light"] .lobby-footbar .lfb-create { background:#c0c2c6; color:#1d222b; }
:root[data-theme="pokerth-light"] .lobby-footbar .lfb-create:hover { background:#b2b4b9; }
/* Portrait/compact (palette claire) : barre posée sur le fond gris QML #e3e8f0 (col700 2.1.6),
   sans trait -> plus de cadre visible autour de « Joueur / Créer une partie ». */
@media (max-width: 899.98px) {
  :root[data-theme="pokerth-light"] .lobby-footbar { background:#e3e8f0; border-top:none; }
}
/* Rejoindre : vert (identique au bouton Rejoindre des lignes) */
.lobby-footbar .lfb-join { border-color:var(--green); background:rgba(var(--green-rgb),0.12); color:var(--green); }
.lobby-footbar .lfb-join:hover { background:rgba(var(--green-rgb),0.28); }
/* Spectateur : bleu (identique au bouton Spectateur des lignes) */
.lobby-footbar .lfb-spectate { border-color:rgba(var(--blue-rgb),0.55); background:rgba(var(--blue-rgb),0.12); color:var(--info); }
.lobby-footbar .lfb-spectate:hover { background:rgba(var(--blue-rgb),0.22); }

/* Mobile (téléphone) : le pseudo passe sur sa propre ligne et les boutons
   Rejoindre/Spectateur/Créer se placent EN DESSOUS, côte à côte à parts égales
   (sinon 3 éléments sur une ligne étroite tronquent les libellés). */
@media (max-width: 599.98px) {
  .lobby-footbar { flex-wrap:wrap; }
  .lobby-footbar .lfb-name { flex:1 1 100%; }
  /* Mobile : Rejoindre/Spectateur uniquement sur les cartes de table.
     On masque leurs doublons de la barre du bas (pseudo + Créer restent). */
  .lobby-footbar .lfb-join,
  .lobby-footbar .lfb-spectate { display: none; }
}

/* Desktop / tablette (largeur >= 600, canon QML) : Rejoindre/Spectateur uniquement via la barre du
   bas (sur la table selectionnee). On retire leurs doublons sur chaque carte —
   la ligne reste cliquable pour selectionner la table. */
@media (min-width: 600px) {
  #s-lobby .game-row.gcard .btn-join { display: none; }
}

/* Infos placeholder (rempli en Phase 2) */
#lobby-gameinfo { display:none; flex-direction:column; }
#lobby-gameinfo .lgi-empty { flex:1 1 auto; display:flex; align-items:center; justify-content:center;
  color:var(--text-dim,#8b93a7); font-size:var(--fs-sm); padding:18px; text-align:center; }

/* Chat toujours présent → on masque son bouton de fermeture et son trigger header */
#lobby-chat-panel .g-chat-panel-header button[onclick*="toggleLobbyChat"] { display:none; }
#lobby-chat-btn { display:none !important; }
/* Corps du chat : remplit le panneau (override du max-height:120px inline) — 2 modes */
#s-lobby .lobby-grid > #lobby-chat-panel .chat-msgs {
  flex:1 1 auto !important; min-height:0 !important; max-height:none !important; overflow-y:auto;
}

/* Poignées de redimensionnement (wide) — occupent leur piste de grille */
.lob-h { display:none; position:relative; }
.lob-h::after { content:''; position:absolute; background:var(--border); transition:background .12s; }
.lob-h:hover::after, .lob-h.drag::after { background:var(--gold); }
.lob-h-l, .lob-h-r { cursor:col-resize; }
.lob-h-l { grid-area:hl; } .lob-h-r { grid-area:hr; } .lob-h-v { grid-area:hv; cursor:row-resize; }
.lob-h-l::after, .lob-h-r::after { top:0; bottom:0; left:50%; width:2px; transform:translateX(-50%); }
.lob-h-v::after { left:0; right:0; top:50%; height:2px; transform:translateY(-50%); }

/* Scrim pour les slide-in mobiles */
.lob-scrim { display:none; position:fixed; inset:0; background:rgba(0,0,0,0.45); z-index:98; }
.lob-scrim.on { display:block; }

/* ── WIDE ≥900 : 3 colonnes redimensionnables ── */
@media (min-width: 900px) {
  #s-lobby { overflow:hidden; }
  #s-lobby .lobby-body { flex:1 1 auto; min-height:0; overflow:hidden; display:flex; flex-direction:column; padding:12px 14px; }
  #s-lobby .lobby-grid {
    position:relative; flex:1 1 auto; min-height:0; gap:0; display:grid;
    grid-template-columns:
      minmax(160px, min(var(--lc-l,240px), 30%)) 12px
      minmax(280px, 1fr) 12px
      minmax(200px, min(var(--lc-r,300px), 30%));
    grid-template-rows: minmax(120px, min(var(--lc-info,220px), 60%)) 12px minmax(120px,1fr) auto;
    grid-template-areas:
      "players hl games hr info"
      "players hl games hr hv"
      "players hl games hr chat"
      "players hl foot  hr chat";
    align-items:stretch;
  }
  /* Barre du bas (Joueur + Créer) : rangée dédiée SOUS la colonne Parties,
     hors du cadre du panneau (parité QML : la barre est posée sur le fond). */
  #s-lobby .lobby-grid > .lobby-footbar {
    grid-area:foot; position:static; z-index:auto;
    background:transparent; border-top:none; padding:10px 0 0;
  }
  .lob-h { display:block; }

  #s-lobby .lobby-grid > #players-panel    { grid-area:players; }
  #s-lobby .lobby-grid > .games-col        { grid-area:games; }
  #s-lobby .lobby-grid > #lobby-gameinfo   { grid-area:info; display:flex; }
  #s-lobby .lobby-grid > #lobby-chat-panel { grid-area:chat; }

  /* Ma partie en attente : liste (games) à droite ; mes infos (info) + le chat
     au centre. Simple ré-affectation des zones — le contenu ne bouge pas. */
  #s-lobby.lobby-waiting .lobby-grid {
    grid-template-areas:
      "players hl info hr games"
      "players hl hv   hr games"
      "players hl chat hr games"
      "players hl foot hr games";
  }

  /* Panneaux : sortis du mode overlay → posés statiquement dans leur colonne */
  #s-lobby .lobby-grid > #players-panel,
  #s-lobby .lobby-grid > #lobby-gameinfo,
  #s-lobby .lobby-grid > #lobby-chat-panel {
    position:static !important; inset:auto !important; transform:none !important;
    width:auto !important; height:auto !important; max-height:none !important; margin:0 !important;
    box-shadow:none; border:1px solid var(--border); border-radius:8px;
    background:var(--panel); min-height:0;
  }
  #s-lobby .lobby-grid > #players-panel,
  #s-lobby .lobby-grid > #lobby-chat-panel { display:flex !important; }

  #s-lobby .lobby-grid > #players-panel .players-list,
  #s-lobby .lobby-grid > #lobby-chat-panel .chat-msgs {
    flex:1 1 auto; min-height:0; max-height:none !important; overflow-y:auto;
  }
  #s-lobby .lobby-grid > .games-col { display:flex; flex-direction:column; min-height:0; overflow:hidden; }
  #s-lobby .lobby-grid > #players-panel,
  #s-lobby .lobby-grid > #lobby-gameinfo,
  #s-lobby .lobby-grid > #lobby-chat-panel { overflow:hidden; min-width:0; }
  #s-lobby .lobby-grid > .games-col .games-scroll { flex:1 1 auto; min-height:0; overflow-y:auto; align-content:start; }

  #s-lobby #h-players { display:none !important; }
  #s-lobby .lobby-grid > #players-panel .g-chat-panel-header button[onclick*="togglePlayersPanel"] { display:none; }
}

/* ── COMPACT <900 : Parties + Chat intégrés ; Joueurs/Infos en slide-in ── */
@media (max-width: 899.98px) {
  #s-lobby { overflow:hidden; }
  #s-lobby .lobby-body { flex:1 1 auto; min-height:0; overflow:hidden; display:flex; flex-direction:column; padding:10px 10px; }
  #s-lobby .lobby-grid { flex:1 1 auto; min-height:0; display:flex; flex-direction:column; gap:var(--sp-3); align-items:stretch; }
  .lob-h { display:none !important; }

  #s-lobby .lobby-grid > .games-col { order:1; flex:0 0 clamp(120px, var(--m-games-h, 52%), 62%); min-height:0; display:flex; flex-direction:column; }
  #s-lobby .lobby-grid > .games-col .games-scroll { flex:1 1 auto; min-height:0; overflow-y:auto; align-content:start; }

  /* Chat intégré sous les tables (plus de fenêtre flottante ni d'overlay) */
  #s-lobby .lobby-grid > #lobby-chat-panel {
    order:3; position:static !important; inset:auto !important; top:auto !important; right:auto !important; bottom:auto !important; left:auto !important; transform:none !important;
    display:flex !important; width:auto !important; flex:1 1 0; min-height:140px;
    max-height:none !important; box-shadow:none; z-index:auto;
    border:1px solid var(--border); border-radius:8px;
  }

  /* Joueurs : slide-in depuis la gauche */
  #s-lobby .lobby-grid > #players-panel {
    position:fixed !important; top:var(--ovl-top,48px); bottom:0; left:0; right:auto !important;
    width:min(320px,84vw); max-height:none !important; display:flex !important;
    transform:translateX(-100%); transition:transform .22s ease; z-index:100; /* au-dessus du contenu du lobby ; l'invariant vis-a-vis du chat est garanti par le mode docke (le chat n'est plus un overlay en compact) */ border-radius:0;
    box-shadow:none; background:var(--panel);
  }
  #s-lobby.pl-open .lobby-grid > #players-panel { transform:translateX(0); box-shadow:2px 0 22px rgba(0,0,0,0.55); }

  /* Infos : slide-in depuis la droite (contenu Phase 2) */
  #s-lobby .lobby-grid > #lobby-gameinfo {
    position:fixed !important; top:var(--ovl-top,48px); bottom:0; right:0; left:auto;
    width:min(340px,88vw); display:flex !important; max-height:none !important; border-radius:0;
    transform:translateX(100%); transition:transform .22s ease; z-index:100;
    box-shadow:none; border-left:1px solid var(--border); background:var(--panel);
  }
  #s-lobby.gi-open .lobby-grid > #lobby-gameinfo { transform:translateX(0); box-shadow:-2px 0 22px rgba(0,0,0,0.55); }
}

/* Barre de stats : tout en bas, pleine largeur (déplacée hors de la colonne Parties) */
.lobby-statsbar { flex-shrink:0; }

/* Bouton Joueurs (👥) dans la barre de filtre — mobile seulement (parité photo) */
.fbar-players { display:none; }
@media (max-width: 899.98px) {
  .fbar-players {
    display:inline-flex; align-items:center; justify-content:center;
    width:36px; height:32px; flex:0 0 auto; border:1px solid var(--border);
    border-radius:8px; background:var(--inset); cursor:pointer; font-size:var(--fs-md); line-height:1;
  }
  .fbar-players:hover { border-color:var(--gold-dim); }
  #h-players { display:none !important; }   /* redondant avec le bouton de la barre de filtre */
}

/* ── Barre du haut du lobby : recherche joueur + filtre parties, au-dessus des colonnes ── */
.lobby-topbar { display:flex; align-items:center; gap:var(--sp-4); flex-wrap:wrap; flex-shrink:0; padding:0 0 10px; }
.lobby-topbar .players-search {
  flex:1 1 280px; display:flex; align-items:center; gap:var(--sp-2);
  padding:0 !important; background:transparent !important; border:none !important;
}
.lobby-topbar .players-search input { flex:1 1 auto; width:auto; }
.lobby-topbar #g-filter-bar { flex:1 1 260px; padding:0 !important; margin:0; display:flex; align-items:center; gap:var(--sp-3); }
.lobby-topbar #g-filter-bar .gfilt-wrap { flex:1 1 auto; }
/* En mobile la barre s'empile proprement (recherche puis filtre) */
@media (max-width: 899.98px) {
  .lobby-topbar { gap:var(--sp-3); padding:0 0 8px; }
  .lobby-topbar .players-search { flex:1 1 100%; }
  .lobby-topbar #g-filter-bar { flex:1 1 100%; }
}

/* Colonne Parties : encadrée + fond plein comme les 3 autres colonnes
   (bordure/rayon/fond identiques ; supprime aussi le fond rayé qui traversait). */
#s-lobby .lobby-grid > .games-col {
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel);
  padding: var(--sp-3);
}

/* Header « Tables disponibles » : collé aux bords de la colonne (annule le
   padding 8px) → même forme pleine-largeur que les headers des 3 autres
   panneaux ; overflow:hidden + border-radius de .games-col arrondissent ses
   coins hauts pour un rendu identique. */
#s-lobby .lobby-grid > .games-col > .g-chat-panel-header {
  margin: -8px -8px 8px;
}

/* Poignée de redimensionnement Tables/Chat — MOBILE uniquement (SplitView vertical) */
.lob-hm { display:none; }
@media (max-width: 899.98px) {
  .lob-hm {
    display:block; order:2; flex:0 0 16px; position:relative;
    cursor:row-resize; touch-action:none;
  }
  .lob-hm::after {
    content:''; position:absolute; left:30%; right:30%; top:50%; height:4px;
    transform:translateY(-50%); background:var(--border); border-radius:var(--r-xs);
  }
  .lob-hm:active::after, .lob-hm.drag::after { background:var(--gold); }
}

/* ── Panneau « Infos de partie » (contenu, parité officielle) ── */
#lobby-gameinfo .g-chat-panel-header { display:flex; align-items:center; justify-content:space-between; }
#lobby-gameinfo .lgi-htitle { font-size:var(--fs-xs); color:var(--gold-dim); letter-spacing:0.12em; text-transform:uppercase; font-weight:600; }
.lgi-report { background:none; border:none; cursor:pointer; font-size:var(--fs-md); line-height:1; padding:0 2px; opacity:0.85; }
.lgi-report:hover { opacity:1; transform:scale(1.05); }
.lgi-scroll { flex:1 1 auto; min-height:0; overflow-y:auto; padding:10px 12px; display:flex; flex-direction:column; gap:var(--sp-2); }
.lgi-row { font-size:var(--fs-base); color:var(--text); display:flex; align-items:center; gap:var(--sp-2); }
.lgi-row .lgi-ico { opacity:0.85; }
.lgi-blinds { color:var(--text-dim,#8b93a7); }
.lgi-ptitle { font-weight:700; color:var(--text); font-size:var(--fs-base); margin-top:6px; }
.lgi-players { display:flex; flex-direction:column; gap:5px; }
.lgi-gname { font-weight:700; color:var(--cream); font-size:var(--fs-md); }
/* Barre du bas (centre) : options d'attente qui remplacent « + Créer une
   partie » quand je suis dans une partie non démarrée (parité officiel). */
#lobby-wait-actions {
  flex:0 0 auto;
  display:flex; flex-wrap:wrap; align-items:center; gap:8px 12px;
  padding:10px 12px; border-top:1px solid var(--border);
}
/* En mode « ma partie en attente », la barre du bas (Joueur/Créer) n'a plus
   lieu d'être : les actions Démarrer/Quitter sont au centre, sous le chat. */
#s-lobby.lobby-waiting .lobby-footbar { display:none; }
#lobby-wait-actions .wp-fillbots { flex:1 1 100%; justify-content:center; padding:var(--sp-0); }
#lobby-wait-actions .wp-actions { flex:1 1 auto; display:flex; gap:var(--sp-4); width:100%; }
#lobby-wait-actions .wp-actions .wp-btn { flex:1 1 auto; }
#lobby-wait-actions .lfb-waithint { flex:1 1 100%; font-size:var(--fs-xs); color:var(--gold-dim); font-style:italic; text-align:center; }
.lgi-prow { display:flex; align-items:center; gap:var(--sp-3); padding:6px 8px; border-radius:var(--r-sm); background:var(--inset); border:1px solid var(--border); }
.lgi-pname { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:var(--fs-base); color:var(--text); }
.lgi-prow.lgi-pending { opacity:0.6; }
/* Ligne joueur cliquable (popup joueur, comme la liste « Joueurs en ligne ») :
   curseur main, AUCUN soulignement/ligne, juste le pseudo qui passe or au survol. */
.lgi-prow.lgi-click { cursor:pointer; -webkit-tap-highlight-color:transparent; }
.lgi-prow.lgi-click, .lgi-prow.lgi-click .lgi-pname { text-decoration:none; }
.lgi-prow.lgi-click:hover .lgi-pname, .lgi-prow.lgi-click:active .lgi-pname { color:var(--gold-hi); }
.lgi-prow.lgi-click:focus { outline:none; }
.lgi-prow.lgi-click:focus-visible { border-color:var(--gold-hi); }
.lgi-pempty { color:var(--text-dim,#8b93a7); font-size:var(--fs-sm); padding:var(--sp-3); }

/* Ligne de partie sélectionnée */
/* Liste des tables focalisée au clavier (parité QML b170786) : cadre au
   clavier seulement, à l'intérieur pour ne pas être rogné par la colonne. */
#g-list:focus { outline: none; }
#g-list:focus-visible { outline: 2px solid var(--gold-hi); outline-offset: -2px; border-radius: var(--r-md); }
.game-row.gcard.sel { border-color:var(--gold); box-shadow:0 0 0 1px var(--gold) inset; background:rgba(var(--gold-rgb),0.06); }
/* Palette claire : selection = fond kaki plein (accent or #E3C800 sur col600),
   sans liseré or sombre (parite QML). */
:root[data-theme="pokerth-light"] .game-row.gcard.sel {
  border-color:transparent; box-shadow:none; background:rgba(227,200,0,0.28);
}

/* Heure des messages de chat [HH:MM:SS] — gris discret, monospace (parité officielle) */
.msg .msg-time { color: var(--text-dim, #8b93a7); font-family: var(--ff-mono, monospace); font-size: 0.88em; margin-right: 2px; }
.st-tabbar::-webkit-scrollbar { display: none; } /* barre d'onglets styles coulissante : scrollbar masquée */
.adv-no-chatts .msg .msg-time { display: none; } /* option « heure dans le chat » decochee */

/* Icônes d'action par joueur (colonne Joueurs connectés) : ⊘ ignorer + 📊 stats */
.players-list .pl-row .pl-acts { display:inline-flex; align-items:center; gap:var(--sp-0); justify-self:end; margin-left:0; }
.pl-act { background:none; border:none; padding:3px; cursor:pointer; color:var(--gold-dim,#9a824a); display:inline-flex; align-items:center; border-radius:4px; line-height:0; }
/* Colonne « Inviter » (page d'attente) : icône verte, état envoyé estompé. */
.pl-act-inv { color:var(--green); }
.pl-act-inv:hover { background:var(--inset-hi); }
.pl-act-inv.sent { opacity:.45; cursor:default; }
.pl-act:hover { color:var(--gold); background:rgba(var(--gold-rgb),0.12); }
.pl-act.pl-act-ban.on { color:var(--red,#e05050); }
/* Marteau (kickban admin) : rouge, comme le bouton du popup joueur. */
.pl-act-gavel { color:var(--red,#e05050); }
.pl-act-gavel:hover { color:var(--red,#e05050); background:rgba(224,80,80,0.14); }

/* Déroulant de tri en bas de la colonne Joueurs connectés (parité officielle) */
.pl-sortbar { flex:0 0 auto; padding:6px 8px; border-top:1px solid var(--border); background:var(--chrome-tint,var(--panel)); }
.pl-sortbar .sel-wrap { width:100%; }

/* En-tête colonnes (afficher/masquer) de la liste « Joueurs en ligne ».
   Rendu comme 1er enfant STICKY du corps scrollable → partage la largeur et la
   gouttière de scroll des lignes. Grille alignée sur les .pl-row : mêmes pistes
   (gabarit complet posé en inline) + même gap 8px + padding latéral 0 (le 12px
   latéral vient du padding du conteneur). Chaque pastille remplit sa cellule
   (width:100%) → elle s'élargit avec la colonne. Le Nom = cellule vide. */
.pl-colhead {
  position:sticky; top:0; z-index:2;
  display:grid; align-items:center; gap:var(--sp-3);
  padding:4px 12px 6px; margin:0 -12px 2px;
  background:linear-gradient(var(--chrome-tint),var(--chrome-tint)),var(--panel); border-bottom:1px solid var(--border);
}
.pl-colh-spacer { min-width:0; }
.pl-colh-chip {
  box-sizing:border-box; width:100%;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--field-bg); border:1px solid var(--border); border-radius:var(--r-xs);
  padding:3px 0; cursor:pointer; line-height:0;
  color:var(--gold-dim); opacity:0.7;
}
.pl-colh-chip:hover { background:rgba(var(--gold-rgb),0.12); }
.pl-colh-chip.on { color:var(--gold); opacity:1; border-color:rgba(var(--gold-rgb),0.35); background:rgba(var(--gold-rgb),0.10); }
.pl-colh-lock { cursor:default; }
.pl-colh-lock:hover { background:rgba(var(--gold-rgb),0.10); }
.pl-colh-star { font-size:var(--fs-base); line-height:1; }

/* Colonne statut « en partie » : manette allumée (vert) si le joueur joue, éteinte sinon */
.players-list .pl-row .pl-status { flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center; width:22px; color:var(--border); line-height:0; }
.players-list .pl-row .pl-status.on { color:var(--green,#50c878); }

/* Nom du joueur dans la barre du bas : cliquable → ouvre le profil */
.lobby-footbar .lfb-name-link { cursor:pointer; text-decoration:none; }
.lobby-footbar .lfb-name-link:hover { color:var(--gold-hi,var(--gold)); }


/* Voile derrière les slide-in Joueurs/Infos en compact (assombrit le fond,
   clic = fermeture). Le panneau lui-même a un fond opaque var(--panel). */
#s-lobby .lob-scrim { position:fixed; top:var(--ovl-top,48px); left:0; right:0; bottom:0; background:rgba(0,0,0,0.5); z-index:98; /* sous les tiroirs (100) : n'assombrit QUE le fond, jamais les tiroirs eux-memes (parite QML : aucun voile sur le panneau) */ opacity:0; pointer-events:none; transition:opacity .22s ease; }
#s-lobby .lob-scrim.on { opacity:1; pointer-events:auto; }

/* Avatar avant le nom dans la barre du bas du lobby */
#lobby-foot-av { display:inline-flex; align-items:center; vertical-align:middle; margin-right:6px; }
#lobby-foot-av .pl-av { width:20px; height:20px; }

/* Bouton « Spectateur » (parties en cours) : même gabarit que Rejoindre, teinte bleue */
.game-row.gcard .btn-spectate {
  border-color: rgba(var(--blue-rgb), 0.55);
  background: rgba(var(--blue-rgb), 0.12);
  color: var(--info);
}
.game-row.gcard .btn-spectate:hover { background: rgba(var(--blue-rgb), 0.22); }

/* ── Teinte par tapis (QML tablestyle.xml) : couleurs chat/log + accent de box.
   Variables injectees par theme.mjs pour les tapis QML skinnes ; repli sur les
   couleurs de palette quand non definies -> AUCUN impact sur les autres tapis.
   L'accent de box est garde par html[data-box-accent] (les replis par style de
   siege different, donc on n'applique la regle que quand un tapis skinne est actif). */
#g-chat-panel, #g-reaction-panel, #g-log-panel { background: var(--chatlog-bg, var(--panel)); border-bottom-color: var(--chatlog-border, var(--border)); }
.g-chat-panel-header { border-bottom-color: var(--chatlog-border, var(--border)); }
.gchat-input, .g-chat-input { background: var(--chatlog-surface, var(--panel)); border-bottom-color: var(--chatlog-border, var(--border)); }
.gchat-input input, .g-chat-input input { background: var(--chatlog-surface, var(--field-bg)); border-color: var(--chatlog-border, var(--border)); color: var(--chatlog-text, var(--cream)); }
.log-panel-body { color: var(--chatlog-text, var(--cream)); }
#g-chat-msgs { color: var(--chatlog-text, var(--text-hi)); }
#g-chat-msgs .msg .txt { color: var(--chatlog-text, var(--text-hi)); }
#g-chat-msgs .msg.sys { color: var(--chatlog-text2, var(--chatlog-text, var(--text))); }
#g-chat-msgs .msg .who { color: var(--chatlog-accent, var(--box-accent, var(--gold))); }
/* Fenetre « Combinaisons du poker » : c'est une fenetre de jeu au meme titre
   que le chat, le journal et les reactions -> elle suit la teinte du tapis
   (ChatLog* du tablestyle.xml) au lieu de rester sur la palette de l'app.
   Replis = valeurs d'origine, donc aucun changement sans tapis skinne. */
#hands-card-inner .hand-name { color: var(--chatlog-text, var(--text-hi)); }
#hands-card-inner .hand-desc,
#hands-card-inner .hands-legend { color: var(--chatlog-text2, var(--chatlog-text, var(--text))); }
#hands-card-inner .hand-row:hover { background: var(--chatlog-surface, var(--inset-hi)); }
#hands-card-inner .hand-row,
#hands-card-inner .hands-legend { border-bottom-color: var(--chatlog-border, rgba(var(--gold-rgb), 0.07)); }
#g-chat-msgs .msg .msg-time { color: var(--chatlog-muted, var(--text-dim, #8b93a7)); }
#g-chat-msgs .msg.mention .txt { color: var(--chatlog-accent, var(--box-accent, var(--gold))); font-weight: 700; }

/* ── Roles du journal (upstream e90593e « qml: table theme ivoire fine-tuning »)
   Le client officiel colore trois roles dans le journal : le gagnant du pot
   principal, celui d'un side pot, et les etapes de tableau (flop/turn/river).
   Les valeurs viennent du tapis (--chatlog-*) ; sans tapis teinte, les replis
   ci-dessous sont les defauts amont, jeu clair sous la palette claire — c'est
   exactement l'ecueil que le commit corrige : jaune et orange sur fond clair
   sont les premieres couleurs que l'oeil perd. */
:root { --lg-win-fb: #FFFF00; --lg-win-side-fb: #FFFFCC; --lg-board-fb: #FF6633; }
:root[data-theme="pokerth-light"] { --lg-win-fb: #6b5400; --lg-win-side-fb: #8a6a2a; --lg-board-fb: #a8431a; }
.log-line.lg-win      { color: var(--chatlog-winner, var(--lg-win-fb)); }
.log-line.lg-win-side { color: var(--chatlog-winner-side, var(--lg-win-side-fb)); }
.log-line.lg-board    { color: var(--chatlog-board, var(--lg-board-fb)); }
html[data-box-accent][data-seat-struct="qml"] .seat-plate { border-color: var(--box-accent); }

/* ═══════════════════════════════════════════════════════════════════════════
   PERF iOS — Glows composités GPU (remplace les pulses box-shadow)
   ───────────────────────────────────────────────────────────────────────────
   Problème : animer `box-shadow` n'est PAS composité sur iOS Safari → repaint
   CPU à chaque frame (60 fps). Pendant une partie il y a quasi toujours ≥1
   glow actif (anneau du siège au tour ; barre d'action pendant MON tour où
   plusieurs se cumulaient) → chauffe soutenue.
   Correctif : le box-shadow devient STATIQUE (rasterisé une fois) et la
   pulsation passe par un calque ::before/::after dont on n'anime QUE
   `opacity`/`transform` (pur compositing GPU, aucun repaint).
   Fidélité visuelle conservée (§4.3 QML « cadre or pulsé »).
   Neutralisé automatiquement par `html.reduce-fx` (box-shadow:none sur *::after)
   et par prefers-reduced-motion (iteration-count:1). Sélecteurs préfixés :root
   pour tenir la spécificité des bases data-table-fs. ═════════════════════════ */

@keyframes glowRingFade  { 0%,100% { opacity:.55; transform:scale(.94); } 50% { opacity:1; transform:scale(1); } }
@keyframes glowPulseFade { 0%,100% { opacity:.5; } 50% { opacity:1; } }
@keyframes actBarPopT    { 0% { transform:scale(1); } 35% { transform:scale(1.03); } 100% { transform:scale(1); } }

/* ── Siège au tour : anneau (ex-activeRing) ── */
:root .seat.active .seat-avatar { animation: none !important; }   /* base statique de la 3583 conservée */
:root .seat.active .seat-avatar::before {
  content:""; position:absolute; inset:0; border-radius:50%;
  pointer-events:none; z-index:-1;
  box-shadow: 0 0 0 4px rgba(var(--gold-rgb), 0.55), 0 0 20px rgba(var(--gold-rgb), 0.6);
  will-change: opacity, transform;
  animation: glowRingFade 1.2s ease-in-out infinite;
}

/* ── Mon tour : barre d'action (ex-myTurnBorder / myTurnPanelGlow / actBarTurnPulse) ── */
:root .my-zone.my-turn-active { animation: none; border-top: none; }
:root .my-zone.my-turn-active .action-grid {
  position: relative;
  border-color: var(--gold);
  box-shadow: 0 0 0 2px var(--gold), 0 0 16px 2px rgba(var(--gold-rgb), 0.42);  /* statique */
  animation: actBarPopT 0.55s cubic-bezier(.2,.9,.3,1.15) both;                  /* pop = transform seul */
}
:root .my-zone.my-turn-active .action-grid::after {
  content:""; position:absolute; inset:0; border-radius:inherit;
  pointer-events:none; z-index:-1;
  box-shadow: 0 0 0 2px #ffe066, 0 0 32px 7px rgba(var(--gold-rgb), 0.74);       /* pic, statique */
  will-change: opacity;
  animation: glowPulseFade 1.4s ease-in-out 0.5s infinite;
}

/* ── Dock joueur actif (ex-pulse-gold) ── */
:root .player-bar.pb-active .player-bar-left { position: relative; animation: none; }
:root .player-bar.pb-active .player-bar-left::after {
  content:""; position:absolute; inset:0; border-radius:inherit;
  pointer-events:none; z-index:-1;
  box-shadow: 0 0 22px rgba(var(--gold-rgb), 0.9);
  will-change: opacity;
  animation: glowPulseFade 1.5s ease-in-out infinite;
}

/* ── Anti-call : bouton Call en attente de 2e tap (ex-callConfirmPulse) ── */
:root .btn-action.btn-call.confirm-call {
  position: relative; animation: none;
  box-shadow: 0 0 0 2px #ffd24d, 0 0 12px rgba(240,160,32,.55);                  /* statique */
}
:root .btn-action.btn-call.confirm-call::after {
  content:""; position:absolute; inset:0; border-radius:inherit;
  pointer-events:none; z-index:-1;
  box-shadow: 0 0 0 2px #ffd24d, 0 0 19px rgba(240,160,32,.9);
  will-change: opacity;
  animation: glowPulseFade .85s ease-in-out infinite;
}

/* ── Barre d'action en PORTRAIT téléphone ──────────────────────────────
   Les contraintes de largeur de la barre n'existaient que dans le bloc
   paysage (@media max-height:500 landscape) ; en portrait la barre gardait
   les tailles « desktop » (Tapis flex:1.5, dropdown large, btn min-width:80)
   et debordait de l'ecran a droite/gauche. On reproduit ici les memes
   contraintes + le Tapis a fond plat (le border-image 9-slice du skin se
   dechire sur ce petit bouton). */
/* Exception canon (décision B) — barre d'action portrait, voir §RESPONSIVE OVAL SIZE. */
@media (max-width: 740px) and (orientation: portrait) {
  .my-zone .action-grid { width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box; gap: 5px; }
  .my-zone .action-grid > * { min-width: 0; }
  /* .action-grid est un flex-item de .my-zone : sans min-width:0 il ne rentre
     pas sous la largeur de son contenu et deborde de l'ecran a mon tour
     (en apercu le wrapper .actions-preview masquait le probleme). */
  .my-zone .actions-preview { min-width: 0; max-width: 100%; box-sizing: border-box; }
  .my-zone .mid-row { gap: 5px; align-items: center; }
  /* Geometrie QML phonePortrait (GameActionBar 2.1.3, raiseRowHeight 26) :
     quick-bets 38x26 a gauche, Tapis 52x26 juste apres, dropdown de mode
     132 px POUSSE a droite (margin-left:auto). Les 38 px de haut de la passe
     precedente ECRASAIENT les valeurs QML du bloc portrait historique
     (specificite egale, plus bas dans le fichier) -> rangee ~12 px trop
     haute sur tout telephone portrait. */
  .my-zone .mid-row .pct-row { flex: 0 0 auto; gap: var(--sp-1); min-width: 0; }
  .my-zone .mid-row .mode-sel-wrap { flex: 0 1 132px; min-width: 0; height: 26px; margin-left: auto; }
  .my-zone .mid-row .mode-sel-wrap select { height: 100%; padding-top: 0; padding-bottom: 0; line-height: 1.1; }
  .my-zone .act-buttons-row { gap: var(--sp-3); }
  /* Rangee Fold / Suivre / Relancer : rangee 56 px (Theme.compact QML) MAIS
     bouton visible 45 px (marges internes QML 5+6) — source 2.1.3. */
  .my-zone .act-buttons-row .btn-action { min-width: 0; min-height: 45px; font-size: var(--fs-md); letter-spacing: 0.5px; border-radius: var(--act-btn-radius, 9px); padding: 0 8px; }
  .my-zone .mid-row .btn-pct { flex: 0 0 38px; width: 38px; min-width: 38px; height: 26px; min-height: 26px; padding: 0 2px; border-radius: 5px; font-size: 11px; font-weight: 700; }
  /* Pas de badges de touches clavier sur mobile (le QML n'en affiche aucun). */
  .my-zone .act-key { display: none !important; }
  /* Champ montant borné : non contraint, il faisait ~180px et elargissait la
     bet-row -> .action-grid -> tout debordait. (Le bloc paysage le bornait deja.) */
  .my-zone .bet-row { gap: var(--sp-2); }
  /* Champ montant : 78 px de large, police 13 px (spec QML §5.2). */
  .my-zone .bet-row .raise-amt-field { width: 78px; min-width: 0; flex: 0 0 auto; height: 26px; min-height: 26px; padding: 1px 6px; font-size: 13px; }
  .my-zone .bet-row .raise-slider { flex: 1 1 auto; min-width: 40px; }
  /* Toute la chaine de conteneurs doit pouvoir se comprimer : sans min-width:0
     sur .action-wrap ET #g-actions (flex-items), le contenu min-content bloque
     le retrecissement et la barre deborde de l'ecran a mon tour. */
  .my-zone .action-wrap { max-width: 100%; min-width: 0; width: 100%; }
  .my-zone #g-actions { min-width: 0; max-width: 100%; width: 100%; }
  .my-zone .mid-row .btn-allin {
    flex: 0 0 52px; width: 52px; white-space: nowrap; min-width: 52px; height: 26px; min-height: 26px; font-size: 12px;
    border-image: none !important;
    border: 1px solid #ef5350 !important;
    background: linear-gradient(180deg, #9e2a2a, #5c1111) !important;
    color: #fff !important;
    padding: 0 2px !important;
    border-radius: var(--r-md);
  }
}

/* ── Traduction du chat (API navigateur, opt-in) : bouton 🌐 par message,
   visible seulement quand body.chat-tr-on (option activee ET API dispo). ── */
.chat-tr-btn { display: none; background: none; border: none; cursor: pointer; font-size: var(--fs-xs); padding: 0 3px; margin-left: 4px; opacity: 0.45; vertical-align: baseline; line-height: 1; }
body.chat-tr-on .msg .chat-tr-btn { display: inline; visibility: hidden; }
/* Parite QML 2.1.5 : le globe n'apparait que sur la ligne sous le pointeur.
   On masque en VISIBILITY (pas display) : la place reste reservee, donc aucun
   reflow au survol -- QML, lui, insere/retire le symbole dans un document
   RichText unique et doit gerer le rewrap que cela provoque. */
@media (hover: hover) { body.chat-tr-on .msg:hover .chat-tr-btn { visibility: visible; } }
/* Tactile : pas de survol -- un tap sur la ligne pose .tr-reveal (ui/chat.mjs),
   repli identique a celui de QML (tap sans lien -> _updateHoverLine). */
body.chat-tr-on .msg.tr-reveal .chat-tr-btn,
/* Option avancee « bouton toujours visible » (chat_tr_always, defaut OFF). */
body.chat-tr-always.chat-tr-on .msg .chat-tr-btn,
/* Une traduction en cours / faite / en erreur reste visible sans survol. */
body.chat-tr-on .msg .chat-tr-btn.tr-active,
body.chat-tr-on .msg .chat-tr-btn.tr-busy,
body.chat-tr-on .msg .chat-tr-btn.tr-err,
body.chat-tr-on .msg .chat-tr-btn.tr-same { visibility: visible; }
.chat-tr-btn:hover { opacity: 0.9; }
.chat-tr-btn.tr-active { opacity: 1; filter: drop-shadow(0 0 2px var(--gold)); }
.chat-tr-btn.tr-busy { opacity: 0.3; cursor: wait; }
.chat-tr-btn.tr-err { opacity: 0.7; filter: grayscale(1); }
.chat-tr-btn.tr-same { opacity: 0.25; cursor: default; }
/* Same globe in private messages (#pm-msgs): a bubble replaces the chat line,
   so the button sits next to the timestamp inside the bubble footer.
   Named .pm-bfoot, NOT .pm-foot: that class already belongs to the dialogue
   footer (input + counter + Send) further up this sheet. Sharing it made this
   rule win by source order and shrink the whole input row to its content,
   pinned to the right edge. */
.pm-bfoot { display: flex; align-items: center; gap: 2px; align-self: flex-end; margin-top: 2px; }
.pm-bfoot .pm-ts { margin-top: 0; }
body.chat-tr-on .pm-line .chat-tr-btn { display: inline; visibility: hidden; }
@media (hover: hover) { body.chat-tr-on .pm-line:hover .chat-tr-btn { visibility: visible; } }
body.chat-tr-on .pm-line.tr-reveal .chat-tr-btn,
body.chat-tr-always.chat-tr-on .pm-line .chat-tr-btn,
body.chat-tr-on .pm-line .chat-tr-btn.tr-active,
body.chat-tr-on .pm-line .chat-tr-btn.tr-busy,
body.chat-tr-on .pm-line .chat-tr-btn.tr-err,
body.chat-tr-on .pm-line .chat-tr-btn.tr-same { visibility: visible; }
/* Abréviations de chat (gg, nh, utg…) : soulignement pointillé discret + bulle
   native au survol (title = sens localisé). Voir modules/chat/abbrev.mjs. */
.chat-abbr { border-bottom: 1px dotted currentColor; cursor: help; }


/* ── Barre d'action : panneau QML (GameActionBar 2.1.3) — desktop/tablette ──
   QML : panelWidth = min(largeur, max(largeurCommunity, 380)) CENTRÉ, fond
   rgba(0,0,0,0.82), radius 10, flottant à 8 px du bord bas (le tapis se voit
   dessous). Largeur community ≈ (5×46 + 4×3) × --comm-scale = 242×cs.
   Scopé à body.adv-hide-pbar (barre joueur masquée, le défaut) : si la barre
   joueur est réaffichée, l'ancienne implantation pleine largeur reste.
   Condition élargie (0.3.567) : ≥600 px de large + souris (hover+fine) — la
   double garde 900×600 laissait une ZONE MORTE (fenêtre basse <600 ou étroite
   600–900) sans aucune géométrie QML. Les téléphones (pointer:coarse) gardent
   leurs blocs portrait/paysage dédiés. */
@media (min-width: 600px) and (hover: hover) and (pointer: fine) {
  /* .my-zone = POSITIONNEMENT SEUL (transparent, sans ombre) : en mode
     plein-tapis elle est déjà transparente !important et c'est .action-grid
     qui porte le panneau — une ombre posée ici dessinait un contour fantôme
     autour d'une boîte invisible. */
  body.adv-hide-pbar .my-zone {
    left: 50%; right: auto;
    transform: translateX(-50%);
    /* QML exact : panelWidth = max(communityVisualWidth, 380) avec
       communityVisualWidth = 264 × communityScale (et non 242+32 : la barre
       était plus étroite que le QML — retour narmod). */
    width: clamp(380px, calc(264px * var(--comm-scale, 1)), 96vw);
    bottom: 8px;
    background: transparent !important;
    border: none !important;
    box-shadow: none;
    padding: 0;
  }
  /* Le PANNEAU visuel QML vit sur .action-grid (fs et non-fs identiques) :
     fond rgba(0,0,0,0.82), radius 10, ombre douce, pleine largeur du bloc. */
  body.adv-hide-pbar .my-zone .action-grid {
    max-width: none; width: 100%;
    background: rgba(0, 0, 0, 0.82);
    border-radius: var(--r-bar);
    /* Paddings QML exacts (raiseSection top 4 / cotes 8 / bas 2 ; la rangee
       d'action porte ses propres marges verticales 5/5 en dessous). */
    padding: 4px 8px 2px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  }
  /* Rangée montant/slider QML : champ 78×26, police 13, spacing 6. */
  body.adv-hide-pbar .my-zone .bet-row { gap: var(--sp-2); }
  body.adv-hide-pbar .my-zone .bet-row .raise-amt-field {
    width: 78px; height: 26px; min-height: 26px; font-size: 13px; flex: 0 0 auto;
  }
}


/* ── Barre d'action QML — finitions desktop/tablette (suite 0.3.541) ── */
@media (min-width: 600px) and (hover: hover) and (pointer: fine) {
  /* Les effets « mon tour » (cadre/glow dorés) sont CONSERVÉS — retrait
     annulé à la demande de narmod (0.3.544). */
  /* Rangée intermédiaire QML (raiseRowHeight 26) : 1/3 · 1/2 · Pot · TAPIS
     sont tous des pilules de 26 px — côté web, Tapis portait la classe des
     GROS boutons d'action (~37 px) et gonflait toute la rangée : c'était la
     principale disparité perçue avec le QML. Le dropdown se raccorde à la
     même hauteur. */
  /* Toute la rangée intermédiaire à 26 px EXACTEMENT (raiseRowHeight QML) :
     1/3 · 1/2 · Pot avaient un padding sans plafond de hauteur → plus hauts
     que Tapis (26 px explicites). Harmonisation. */
  body.adv-hide-pbar .my-zone .pct-row .btn-pct {
    height: 26px; min-height: 26px;
    padding: 0 8px;
    font-size: var(--fs-xs);
    line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
  }
  body.adv-hide-pbar .my-zone .mid-row > .btn-action.btn-allin {
    height: 26px; min-height: 26px;
    padding: 0 10px !important;
    font-size: var(--fs-xs) !important;
    letter-spacing: 0.3px;
    flex: 0 0 auto;
  }
  body.adv-hide-pbar .my-zone .mid-row .mode-sel-wrap,
  body.adv-hide-pbar .my-zone .mid-row .mode-sel-wrap select { height: 26px; }
  /* Derniers gabarits QML (GameActionBar) : largeurs fixes et polices. */
  body.adv-hide-pbar .my-zone .pct-row .btn-pct { flex: 0 0 38px; width: 38px !important; min-width: 38px !important; padding: 0 2px !important; font-size: 11px; }
  body.adv-hide-pbar .my-zone .pct-row .btn-pct .pct-p { font-size: 11px; }
  body.adv-hide-pbar .my-zone .mid-row > .btn-action.btn-allin { flex: 0 0 52px; width: 52px; padding: 0 2px !important; font-size: 12px !important; }
  /* Thèmes à SVG de boutons (data-btn-img) : Tapis 52×26 en image du thème
     ÉTIRÉE 100%/100% comme le QML (Image.Stretch) — identique au mode
     portrait/iOS. Le repli 9-slice à bords 6px se déchirait sur Edge/desktop
     (retour narmod 15/07) ; si un letterboxing SVG réapparaît sur Linux, le
     corriger dans le SVG (preserveAspectRatio) et non ici. */
  :root[data-btn-img] body.adv-hide-pbar .my-zone .mid-row > .btn-action.btn-allin {
    border: none !important;
    background: transparent !important;
    background-image: var(--btn-allin-img) !important;
    background-size: 100% 100% !important;
    background-repeat: no-repeat !important;
    border-radius: var(--r-sm);
    padding: 0 !important;
  }
  body.adv-hide-pbar .my-zone .mid-row .mode-sel-wrap select { font-size: 11px; }
  /* Slider QML : rail 4 px, poignée 18×18 ronde. */
  body.adv-hide-pbar .my-zone .bet-row input[type="range"] {
    -webkit-appearance: none; appearance: none;
    height: 4px; border-radius: 2px;
    background: rgba(255, 255, 255, 0.22);
  }
  body.adv-hide-pbar .my-zone .bet-row input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    width: 18px; height: 18px; border-radius: var(--r-md);
    background: #4CAF50; border: 1px solid rgba(255, 255, 255, 0.5);
    cursor: pointer;
  }
  body.adv-hide-pbar .my-zone .bet-row input[type="range"]::-moz-range-track {
    height: 4px; border-radius: 2px; background: rgba(255, 255, 255, 0.22);
  }
  body.adv-hide-pbar .my-zone .bet-row input[type="range"]::-moz-range-thumb {
    width: 18px; height: 18px; border-radius: var(--r-md);
    background: #4CAF50; border: 1px solid rgba(255, 255, 255, 0.5);
    cursor: pointer;
  }
  /* Dropdown de mode : 132 px comme le QML, hauteur raccordée à la rangée. */
  body.adv-hide-pbar .my-zone .mid-row .mode-sel-wrap { flex: 0 0 132px; }
  body.adv-hide-pbar .my-zone .mid-row .mode-sel-wrap select {
    padding-top: 4px; padding-bottom: 4px; font-size: var(--fs-xs);
  }
}


/* ── Zoom des cartes du joueur RETIRÉ (demande narmod) : dans les packs
   pokerth les cartes self sont à taille QML fixe, le bouton n'a plus d'objet ;
   il est masqué partout pour une UI identique sur tous les écrans. ── */
#g-cardzoom { display: none !important; }

/* ── Barre d'action QML — dernière passe (mode panneau desktop/tablette) ── */
@media (min-width: 600px) and (hover: hover) and (pointer: fine) {
  /* Pastilles de raccourci [F]/[C]/[R] : extension web absente du QML. */
  body.adv-hide-pbar .my-zone .act-key { display: none !important; }
  /* Boutons d'action : typo QML exacte — 15px bold, letterSpacing 0.5,
     radius 9 (GameActionBar §5.5). */
  /* Rangée du bas = spec QML COMPLÈTE (GameActionBar, rectifiée) : les
     boutons REMPLISSENT la rangée actionRowHeight 54 (fillWidth+fillHeight),
     largeurs égales, espacement 8 — le 168×43 n'est que l'espace de dessin
     des SVG de thème, étiré sur toute la hauteur (correction : pas d'« air »
     interne). Police 15 bold ls 0.5 lineHeight 0.95, radius 9. */
  /* Hauteur pilotée par le régime QML discret (--bar-k, updateBottomLayout) :
     54 desktop (≥900) · 56 Theme.compact (600–900) — plus de 54 en dur, la
     fenêtre étroite retombe sur la bonne valeur au lieu d'une zone morte. */
  /* La RANGEE fait 54 (56 compact) mais le RowLayout QML des boutons a des
     marges internes top 5 / bottom 5 (6 compact) -> le BOUTON visible fait
     rangee - 10 (44 desktop, ~45 compact). C'etait l'erreur de la passe
     0.3.541 (« pas d'air interne ») : les boutons web etaient 10 px trop
     hauts par rapport au client officiel. */
  body.adv-hide-pbar .my-zone .act-buttons-row { height: calc(54px * var(--bar-k, 1)); box-sizing: border-box; padding: 5px 0; gap: 8px; align-items: stretch; }
  body.adv-hide-pbar .my-zone .act-buttons-row .btn-action {
    height: calc(54px * var(--bar-k, 1) - 10px) !important; min-height: 0 !important; max-height: calc(54px * var(--bar-k, 1) - 10px) !important;
    flex: 1 1 0;
    padding: 0 10px !important;
    font-size: calc(15px * var(--bar-kf, 1)) !important;
    font-weight: 700;
    letter-spacing: 0.5px;
    border-radius: var(--act-btn-radius, 9px);
    line-height: 1.05;
  }
  /* Montant sur une DEUXIÈME ligne (« Suivre / $40 »), comme le QML. */
  body.adv-hide-pbar .my-zone .act-buttons-row .btn-action > b,
  body.adv-hide-pbar .my-zone .act-buttons-row .btn-action .raise-btn-amt {
    display: block;
    line-height: 1.05;
  }
  body.adv-hide-pbar .my-zone .action-grid { gap: 3px; }
  body.adv-hide-pbar .my-zone .mid-row { gap: var(--sp-1); }
  /* (liseré/ombre gérés sur .action-grid ci-dessus) */
}


/* ── Parité QML : plate pokerth amincie vers GamePlayerBox 2.1.3 (opp base
   ≈121×71/84 vs nos 164×101 mesurés) — des boîtes intrinsèques plus petites
   agrandissent la lacune verticale → commScale/rivière/barre remontent au
   niveau QML sans toucher aux formules (calibration narmod). ── */
html[data-seat-struct="qml"] .seat-plate { padding: 3px 5px !important; gap: 2px !important; }
html[data-seat-struct="qml"] .seat:not(.me) .seat-avatar { width: 34px !important; height: 34px !important; }
html[data-seat-struct="qml"] .seat:not(.me) .seat-holecards .pk { width: 25px; height: 34px; font-size: var(--fs-3xs); }
html[data-seat-struct="qml"] .seat:not(.me) .seat-name { font-size: var(--fs-2xs); line-height: 1.1; }
html[data-seat-struct="qml"] .seat:not(.me) .seat-money { font-size: var(--fs-xs); line-height: 1.1; }
html[data-seat-struct="qml"] .seat:not(.me) .seat-narrow .seat-avatar { width: 36px !important; height: 36px !important; }
html[data-seat-struct="qml"] .seat:not(.me) .seat-narrow .seat-holecards .pk { width: 26px; height: 36px; }
/* ── HUD par siège + grille de range (thème client) ─────────────────────── */
#hud-layer{position:absolute;inset:0;pointer-events:none;z-index:6;overflow:visible}
.hud-box{position:absolute;pointer-events:auto;cursor:pointer;
  min-width:148px;background:var(--panel-hi);border:1px solid var(--border-hi);border-radius:var(--r-md);
  padding:4px 7px 5px;box-shadow:0 8px 26px rgba(0,0,0,.55);backdrop-filter:blur(4px);
  font-size:var(--fs-2xs);line-height:1.3;user-select:none;transition:border-color .15s}
.hud-box:hover{border-color:rgba(var(--gold-rgb),.55)}
.hud-box{cursor:grab;touch-action:none}
.hud-box.hud-dragging{cursor:grabbing;opacity:.92;z-index:11;box-shadow:0 6px 18px rgba(0,0,0,.6);transition:none}
.hud-head{display:flex;align-items:baseline;gap:var(--sp-2);margin-bottom:2px}
.hud-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  color:var(--gold);font-weight:700;letter-spacing:.02em;font-size:var(--fs-sm)}
.hud-hands{color:var(--text);font-variant-numeric:tabular-nums;font-size:var(--fs-2xs);opacity:.8}
.hud-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:2px 8px}
.hud-stat{display:flex;align-items:baseline;justify-content:space-between;gap:3px}
.hud-lbl{color:var(--text);font-style:normal;font-size:var(--fs-2xs);letter-spacing:.03em;opacity:.75}
.hud-val{color:var(--text-hi);font-weight:600;font-variant-numeric:tabular-nums;font-size:var(--fs-xs)}
/* Badge d'archétype : cadran 2×2 (serré↔large en X, passif↔agressif en Y) et
   libellé en toutes lettres. .hud-style-est = estimation sous 25 mains. */
.hud-style{display:flex;align-items:center;gap:5px;margin-top:3px;padding-top:3px;
  border-top:1px solid var(--border);color:var(--text-hi);font-size:var(--fs-2xs);line-height:1.2}
.hud-style-ico{flex:none;display:block;color:var(--gold)}
.hud-style-lbl{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.02em}
.hud-style-est{opacity:.5}

/* Fenêtre « range » — même patron que #adv-modal / #help-modal : conteneur
   transparent qui laisse passer les clics (AUCUN voile sombre : le jeu reste
   visible et jouable derrière), carte promue en .floating-win par
   _rangeSetupFloat au-dessus de 600 px. */
#range-modal{position:fixed;inset:0;z-index:1200;display:none;align-items:center;justify-content:center;
  padding:16px;pointer-events:none}
#range-modal .range-card{pointer-events:auto;background:var(--panel-hi);border:1px solid var(--border);border-radius:var(--r-lg);
  box-shadow:0 12px 40px rgba(0,0,0,.55);max-width:min(96vw,720px);max-height:92vh;display:flex;flex-direction:column;overflow:hidden}
#range-modal .range-head{display:flex;align-items:center;gap:var(--sp-5);flex-wrap:wrap;
  padding:9px 12px;border-bottom:1px solid var(--border)}
#range-modal .range-title{flex:1;min-width:120px;color:var(--gold);font-weight:700;font-size:var(--fs-base)}
/* Barre de titre = poignée de déplacement, comme .rk-title / .km-title. */
#range-modal .range-card.floating-win{position:fixed;margin:0;max-width:none;max-height:none}
#range-modal .range-card.floating-win .range-title{cursor:move;touch-action:none;-webkit-user-select:none;user-select:none}
#range-modal .range-card.floating-win .range-title.win-dragging{cursor:grabbing}
/* En fenêtre, le corps prend la hauteur restante et défile seul. */
#range-modal .range-card.floating-win .range-body{flex:1 1 auto;min-height:0}
#range-modal .range-filters{display:flex;gap:10px}
#range-modal .range-filters label{color:var(--text);font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.08em;display:flex;align-items:center;gap:4px}
#range-modal .range-filters select{background:var(--field-bg);color:var(--text-hi);border:1px solid var(--border);border-radius:5px;padding:1px 4px;font-size:var(--fs-xs)}
#range-modal .range-close{background:none;border:none;color:var(--text);cursor:pointer;font-size:var(--fs-md);padding:0 4px}
#range-modal .range-close:hover{color:var(--gold)}
#range-modal .range-body{overflow:auto;padding:10px 12px}
#range-modal .range-legend{padding:6px 12px 9px;color:var(--text);opacity:.7;font-size:var(--fs-2xs);border-top:1px solid var(--border)}
/* Panneau détaillé d'un joueur : prolongement de la boîte HUD (popover ancré,
   dans #hud-layer). S'ouvre au-dessus/en dessous selon la position du siège. */
#hud-detail{position:absolute;z-index:9;pointer-events:auto;width:204px;max-width:78vw;
  background:var(--panel-hi);border:1px solid var(--border-hi);border-radius:var(--r-md);
  box-shadow:0 8px 26px rgba(0,0,0,.55);backdrop-filter:blur(4px);padding:6px 8px 7px;
  font-size:var(--fs-xs);line-height:1.35;animation:hud-d-in .12s ease-out}
@keyframes hud-d-in{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}
#hud-detail .hud-d-head{display:flex;align-items:baseline;gap:var(--sp-3);margin-bottom:4px;padding-bottom:4px;border-bottom:1px solid var(--border)}
#hud-detail .hud-d-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--gold);font-weight:700;font-size:var(--fs-base)}
#hud-detail .hud-d-hands{color:var(--text);font-size:var(--fs-2xs);opacity:.8;font-variant-numeric:tabular-nums;flex-shrink:0}
/* Portee des stats limitee a la session courante (joueurs invites) : le pseudo
   d'un invite peut etre repris par une autre personne d'une session a l'autre. */
/* Bloc stats de comportement dans la fenetre d'avatar (popup profil joueur).
   Independant du HUD flottant : visible meme HUD desactive.
   Calque sur .pim-ig (le cadre Stack/Miser) : meme largeur pleine, meme fond,
   meme bordure, memes tokens de couleur. --text-dim n'existe pas dans le
   theme : les libelles utilisent --gold-dim comme .pim-ig-k, les valeurs
   --cream comme .pim-ig-v (la colorisation par seuils passe en style inline). */
.pim-stat-box{
  width: 100%;
  margin-top: var(--sp-2);
  padding: 10px 12px;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.pim-stat-head{display:flex;align-items:baseline;gap:var(--sp-2);margin-bottom:8px;padding-bottom:6px;border-bottom:1px solid var(--border)}
.pim-stat-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--gold-dim);font-family:var(--ff-mono);font-size:var(--fs-base);letter-spacing:.04em;text-transform:uppercase}
.pim-stat-scope{flex-shrink:0;color:var(--gold-dim);font-family:var(--ff-mono);font-size:var(--fs-sm);border:1px solid var(--gold-dim);border-radius:6px;padding:0 5px;line-height:1.6}
.pim-stat-hands{flex-shrink:0;color:var(--cream);font-family:var(--ff-mono);font-size:var(--fs-sm);opacity:.9}
/* Deux colonnes fixes : les libelles restent alignes et la valeur colle a sa
   propre etiquette, comme les lignes du cadre Stack juste au-dessus. */
.pim-stat-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px var(--sp-5)}
.pim-stat{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-2);min-width:0;font-size:var(--fs-base)}
.pim-stat i{font-style:normal;color:var(--gold-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pim-stat b{color:var(--cream);font-family:var(--ff-mono);font-weight:700;flex-shrink:0}
.pim-trend{display:flex;align-items:baseline;gap:var(--sp-2);margin-top:8px;padding-top:6px;border-top:1px solid var(--border);font-size:var(--fs-base)}
.pim-t-lbl{flex:1;min-width:0;color:var(--gold-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pim-t-cell{display:inline-flex;align-items:baseline;gap:4px;flex-shrink:0}
.pim-t-cell i{font-style:normal;color:var(--gold-dim)}
.pim-t-cell b{color:var(--cream);font-family:var(--ff-mono);font-weight:700}
.pim-t-cell u{text-decoration:none;color:var(--gold);font-weight:700}
/* Tendance recente : VPIP/AF sur les 20 dernieres mains vs la moyenne globale.
   La fleche marque un ecart significatif (cf. _TREND_EPS), la double barre un
   comportement stable. */
#hud-detail .hud-d-trend{display:flex;align-items:center;gap:var(--sp-2);margin-top:5px;padding-top:5px;border-top:1px solid var(--border);font-size:var(--fs-2xs)}
#hud-detail .hud-d-t-lbl{flex:1;min-width:0;color:var(--text-dim,#a0acc4);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#hud-detail .hud-d-t-cell{display:inline-flex;align-items:baseline;gap:3px;flex-shrink:0}
#hud-detail .hud-d-t-cell i{font-style:normal;color:var(--text-dim,#a0acc4);opacity:.85}
#hud-detail .hud-d-t-cell b{color:var(--text);font-variant-numeric:tabular-nums}
#hud-detail .hud-d-t-cell u{text-decoration:none;color:var(--gold);font-weight:700}
#hud-detail .hud-d-scope{flex-shrink:0;color:var(--text-dim,#a0acc4);font-size:var(--fs-3xs,10px);opacity:.75;border:1px solid var(--border);border-radius:6px;padding:0 5px;line-height:1.5}
#hud-detail .hud-d-style{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:4px;
  padding-bottom:4px;border-bottom:1px solid var(--border);font-size:var(--fs-xs)}
#hud-detail .hud-d-style-k{flex:none;color:var(--text);opacity:.85}
#hud-detail .hud-d-style-v{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  color:var(--text-hi);font-weight:700;text-align:right}
#hud-detail .hud-d-grid{display:grid;grid-template-columns:1fr 1fr;gap:2px 12px}
#hud-detail .hud-d-row{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-2);padding:1px 0}
#hud-detail .hud-d-lbl{color:var(--text);font-size:var(--fs-xs);opacity:.85}
#hud-detail .hud-d-val{color:var(--text-hi);font-weight:700;font-variant-numeric:tabular-nums;font-size:var(--fs-xs)}
#hud-detail .hud-d-range{margin-top:6px;width:100%;background:rgba(var(--gold-rgb),.14);border:1px solid var(--border-hi);color:var(--gold);font-size:var(--fs-xs);letter-spacing:.03em;padding:4px 8px;border-radius:var(--r-sm);cursor:pointer}
#hud-detail .hud-d-range:hover{background:rgba(var(--gold-rgb),.22)}
.range-grid{border-collapse:separate;border-spacing:2px;margin:0 auto}
.range-grid td{width:38px;height:34px;text-align:center;vertical-align:middle;border-radius:4px;
  background:var(--field-bg);color:var(--text);font-size:var(--fs-3xs);line-height:1.05;position:relative}
.range-grid td.filled{font-weight:700}
.range-grid .rc-combo{display:block;font-size:var(--fs-2xs)}
.range-grid .rc-count{display:block;font-size:var(--fs-3xs);opacity:.85;font-variant-numeric:tabular-nums}
/* Mobile : HUD très compact — VPIP/PFR/AF sur une ligne, et le badge
   d'archétype réduit à son cadran (le libellé prendrait toute la largeur). */
@media (max-width:599.98px){
  .range-grid td{width:26px;height:26px;font-size:var(--fs-3xs)}
  .range-grid .rc-combo{font-size:var(--fs-3xs)}
  .hud-box{min-width:0;width:auto;max-width:120px;font-size:var(--fs-3xs);padding:3px 5px;border-radius:var(--r-sm);line-height:1.2}
  .hud-head{gap:4px;margin-bottom:2px}
  .hud-name{font-size:var(--fs-2xs);max-width:76px}
  .hud-hands{font-size:var(--fs-3xs)}
  .hud-grid{grid-template-columns:repeat(3,auto);gap:1px 7px;justify-content:start}
  .hud-lbl{font-size:var(--fs-3xs)}
  .hud-val{font-size:var(--fs-2xs)}
  .hud-style{justify-content:center;gap:0;margin-top:2px;padding-top:2px}
  .hud-style-lbl{display:none}
}
/* Très petits écrans (portrait étroit) : encore plus dense. */
@media (max-width:400px){
  .hud-box{max-width:108px;padding:2px 4px}
  .hud-name{max-width:66px}
  .hud-grid{gap:1px 6px}
}
/* Mobile paysage (peu de hauteur) : même compacité qu'en portrait,
   indépendamment de la largeur. */
@media (orientation:landscape) and (max-height:600px){
  .hud-box{min-width:0;width:auto;max-width:128px;font-size:var(--fs-3xs);padding:3px 5px;border-radius:var(--r-sm);line-height:1.2}
  .hud-head{gap:4px;margin-bottom:2px}
  .hud-name{font-size:var(--fs-2xs);max-width:82px}
  .hud-hands{font-size:var(--fs-3xs)}
  .hud-grid{grid-template-columns:repeat(3,auto);gap:1px 7px;justify-content:start}
  .hud-lbl{font-size:var(--fs-3xs)}
  .hud-val{font-size:var(--fs-2xs)}
  .hud-style{justify-content:center;gap:0;margin-top:2px;padding-top:2px}
  .hud-style-lbl{display:none}
}

/* ── Zoom de fenêtre (demande narmod 2026-07-18) : le contenu (texte,
   contrôles, espacements) suit la taille de la fenêtre — propriété CSS `zoom`
   (standardisée) sur les enfants directs : le layout interne se calcule en
   coordonnées locales puis est mis à l'échelle, flex et scroll restent
   corrects. Les poignées de redimensionnement (.win-rsz) gardent leur taille
   réelle pour rester faciles à attraper. --wz est posé en JS
   (_applyWinZoom / _advApplyZoom) = taille fenêtre / taille par défaut ×
   facteur global du viewport de l'app. ── */
.floating-win.win-zoom > :not(.win-rsz) { zoom: var(--wz, 1); }

/* ── Défilement STRICTEMENT vertical partout (demande narmod 2026-07-19) ──
   overflow-y:auto seul laisse overflow-x se calculer en auto : n'importe quel
   contenu trop large fait alors panner le panneau latéralement (constaté sur
   la catégorie Internet des options avancées). On verrouille l'axe X de TOUS
   les conteneurs à défilement vertical de l'app d'un bloc, ici en fin de
   feuille pour gagner la cascade. Exceptions volontairement ABSENTES de cette
   liste (défilement horizontal légitime) : .avp-cats, .rk-awards, .rk-ps-tw,
   .rk-pr-tw, #g-stats-body .stats-scroll, .adv-nav (mobile), .st-tabbar,
   #range-modal .range-body (grille 2D). */
.cp-scroll, #s-connect, .gim-card, .pim-card, .avp-grid, #s-lobby,
.games-scroll, .chat-msgs, .emote-suggest, .react-grid,
#stats-overlay, .log-panel-body, .wc-players, .game-log, .wait-page,
.km-card, .lobby-body, .chat-emoji-panel, .music-body, .rk-body,
#rk-profile, .adv-body, #about-page, .pv-scroll, .avgal-body, .lgi-scroll
{ overflow-x: hidden; }

/* ── Lot 7 — Adaptation petits écrans (≤380px) : réduction du barème
   typographique uniquement, pour limiter les débordements sur très petits
   téléphones. Bloc en fin de fichier → l'emporte sur les défauts :root quand il
   matche. Les éléments ayant déjà leur propre override à ce breakpoint gardent
   leur taille (leur règle gagne). Voir docs/DESIGN_TOKENS.md §Responsive. */
@media (max-width: 380px) {
  :root {
    --fs-3xs: .52rem; --fs-2xs: .58rem; --fs-xs: .66rem;  --fs-sm: .74rem;
    --fs-base: .80rem; --fs-md: .90rem; --fs-lg: 1.0rem;  --fs-xl: 1.28rem;
    --fs-2xl: 1.5rem;  --fs-hero: 2.4rem;
  }
}

/* Trophée classement : or vif en thème sombre, or foncé en thème clair (lisibilité). */
/* Le trophee suit la couleur de l'icone du bouton (neutre au repos, or sous
   .win-open) — meme traitement que les autres icones d'en-tete. */
.rk-trophy{fill:currentColor}

/* ── Succès / Achievements — onglet « Trophées » du ranking modal (mode entraînement) ── */
#rk-ach-wrap { display: flex; flex-direction: column; gap: 6px; padding: 4px 2px 2px; }
.rk-ach-head { display: flex; align-items: baseline; justify-content: space-between; margin: 2px 2px 2px; }
.rk-ach-mode { font-size: var(--fs-sm); letter-spacing: 0.06em; color: var(--text); opacity: 0.75; font-weight: 600; }
.rk-ach-prog { font-size: var(--fs-sm); letter-spacing: 0.06em; color: var(--gold); font-weight: 700; }
.rk-ach-cat { font-size: var(--fs-sm); letter-spacing: 0.12em; text-transform: uppercase; color: var(--gold-dim); margin: 8px 2px 0; }
.rk-ach-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 9px; background: rgba(var(--gold-rgb), 0.05); border: 1px solid var(--border); }
.rk-ach-row.on { border-color: rgba(var(--gold-rgb), 0.35); }
.rk-ach-row.off { opacity: 0.42; filter: grayscale(1); }
.rk-ach-ico { font-size: 1.35rem; line-height: 1; width: 1.8rem; text-align: center; flex: none; }
.rk-ach-txt { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.rk-ach-txt b { font-size: var(--fs-base); color: var(--text-hi); }
.rk-ach-txt small { font-size: var(--fs-sm); color: var(--text); opacity: 0.8; white-space: normal; }
.rk-ach-badge { flex: none; font-size: 0.95rem; color: var(--gold); }

/* ── Encart « Trophées X/10 » (popup Mon profil + overlay fin de partie, entraînement) ── */
.ach-badge { display: flex; align-items: center; gap: 8px; margin-top: 10px; padding: 8px 12px; border-radius: 9px; background: rgba(var(--gold-rgb), 0.08); border: 1px solid rgba(var(--gold-rgb), 0.35); cursor: pointer; }
.ach-badge:hover { background: rgba(var(--gold-rgb), 0.14); }
.ach-badge-ico { font-size: 1.1rem; line-height: 1; }
.ach-badge-lbl { font-weight: 600; color: var(--text-hi); flex: 1; }
.ach-badge-val { font-weight: 700; color: var(--gold); font-variant-numeric: tabular-nums; }
.ach-badge-endgame { margin-top: 12px; justify-content: center; }
.rk-ach-req { flex: none; font-size: 0.66rem; color: var(--gold-dim); background: rgba(var(--gold-rgb), 0.10); border: 1px solid rgba(var(--gold-rgb), 0.25); border-radius: 999px; padding: 1px 7px; white-space: nowrap; }

/* ── Indicateur « disquette » de synchronisation (headers) ─────────────────── */
.hdr-sync { display: none; align-items: center; justify-content: center; width: 24px; height: 24px; flex: none; color: var(--gold-dim); }
.hdr-sync.on { display: inline-flex; animation: hdrSyncPulse 1.15s ease-in-out infinite; }
.hdr-sync svg { width: 17px; height: 17px; display: block; }
@keyframes hdrSyncPulse { 0%, 100% { opacity: 0.32; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .hdr-sync.on { animation: none; opacity: 0.85; } }
/* Portrait mobile : feuille épinglée haut/bas — miroir de la règle #adv-modal. */
@media (max-width:599.98px){
  #range-modal{padding:0}
  #range-modal .range-card{width:calc(100% - 20px);max-width:none;height:calc(100% - max(28px, 2*env(safe-area-inset-top), 2*env(safe-area-inset-bottom)));margin:0}
  #range-modal .range-body{flex:1 1 auto;min-height:0}
}

/* ── Help window (#help-modal, modules/help/index.mjs) ─────────────────────
   Entrée « Aide » des menus des 5 headers. Même patron que #adv-modal :
   aucun voile, carte adv-card (fenêtre flottante ≥600 px via _enableFloating,
   feuille épinglée en portrait mobile, barre d'icônes sous 600 px par la
   container query .adv-* partagée). Corps .help-* propre. */
#help-modal{position:fixed;inset:0;z-index:1200;display:flex;align-items:center;justify-content:center;pointer-events:none}
#help-modal .km-backdrop{display:none}
#help-modal .km-card{pointer-events:auto}
body.adv-no-helpbtn .help-menu-btn{display:none}
.help-search{margin-top:8px}
.help-search input{width:100%;box-sizing:border-box;background:var(--field-bg);border:1px solid var(--border);border-radius:6px;color:var(--text);padding:7px 10px;font-size:0.85rem}
.help-search input:focus{outline:none;border-color:var(--gold-dim)}
.help-body{padding:2px 2px 8px}
.help-ch-t{font-size:1.02rem;margin:2px 0 10px;color:var(--text);display:flex;align-items:center;gap:8px;font-weight:700}
.help-ch-ic{font-size:1.05em;line-height:1}
.help-cat-ic{font-size:1rem;line-height:1}
.help-sec{margin:0 0 14px;border-radius:6px}
.help-sec-t{font-size:var(--fs-2xs,0.68rem);letter-spacing:0.12em;text-transform:uppercase;color:var(--gold-dim);margin:14px 0 5px;font-weight:700}
.help-p{margin:5px 0;font-size:0.85rem;line-height:1.55;color:var(--text)}
.help-wip{opacity:.65;font-style:italic}
.help-ul{margin:5px 0 5px 18px;padding:0;font-size:0.85rem;line-height:1.55;color:var(--text)}
.help-ul li{margin:4px 0}
.help-keys{margin:6px 0}
.help-keyrow{display:flex;gap:10px;align-items:baseline;margin:5px 0;font-size:0.82rem;color:var(--text)}
.help-kbd{flex:0 0 auto;min-width:120px;text-align:center;background:var(--field-bg);border:1px solid var(--border);border-radius:5px;padding:2px 8px;color:var(--gold-dim);font-size:0.72rem;white-space:nowrap}
.help-result{display:flex;flex-direction:column;align-items:flex-start;gap:2px;width:100%;text-align:left;background:none;border:0;border-bottom:1px solid var(--border);padding:8px 6px;cursor:pointer;color:var(--text);font:inherit}
.help-result:hover{background:rgba(var(--gold-rgb,227,200,0),.08)}
.help-result-ch{font-size:0.68rem;color:var(--gold-dim);letter-spacing:0.08em;text-transform:uppercase}
.help-result-t{font-size:0.85rem}
.help-sec.help-hit{animation:helpHit 1.5s ease}
@keyframes helpHit{0%{background:rgba(var(--gold-rgb,227,200,0),.2)}100%{background:transparent}}
/* Portrait mobile : feuille épinglée haut/bas — miroir de la règle #adv-modal. */
@media (max-width:599.98px) and (orientation:portrait){
  #help-modal .adv-card{width:calc(100% - 20px);max-width:none;height:calc(100% - max(28px, 2*env(safe-area-inset-top), 2*env(safe-area-inset-bottom)));margin:0;display:flex;flex-direction:column;overflow:hidden}
  #help-modal .adv-card .adv-layout{flex:1 1 auto;min-height:0}
  #help-modal .adv-card .adv-nav{flex:0 0 auto}
  #help-modal .adv-card .adv-panels{flex:1 1 auto;min-height:0;height:auto}
}
/* Fenêtre flottante ≥600 px — miroir de la règle #adv-modal. */
@media (min-width:600px){
  #help-modal .adv-card.floating-win{position:fixed;margin:0;max-width:none;max-height:none}
}
/* ActionButtonBorderRadius (style de table) : le bouton All-In suit lui aussi
   le rayon du style, comme les trois boutons de la rangee. Sans cela son cadre
   d'etat (or de pre-selection) ne colle pas au contour du SVG du style — c'est
   exactement la raison pour laquelle le QML expose ce rayon. Les replis
   reprennent les valeurs actuelles : rien ne bouge tant qu'aucun style ne
   definit la cle. */
.my-zone .mid-row .btn-allin { border-radius: var(--act-btn-radius, 8px); }
:root[data-btn-img] .my-zone .mid-row .btn-allin { border-radius: var(--act-btn-radius, var(--r-sm)); }

/* Notes plateforme/navigateur de l'aide (champ note des sections). */
.help-note{display:flex;gap:8px;align-items:flex-start;margin:8px 0;padding:7px 10px;font-size:0.78rem;line-height:1.5;color:var(--text);background:rgba(var(--gold-rgb,227,200,0),.07);border-left:3px solid var(--gold-dim);border-radius:0 6px 6px 0}
.help-note-ic{flex:0 0 auto;color:var(--gold-dim);font-size:0.85rem;line-height:1.4}

/* ── Avatar studio (fenêtre avatar à onglets : Galerie / Créer / Importer) ── */
/* Onglets : même modèle générique que .stats-tab (Cinzel, majuscules,
   soulignement accent QML --sel #E3C800). */
.avp-tabs { display: flex; border-bottom: 1px solid var(--border); background: var(--chrome-tint); }
.avp-tab {
  flex: 1; background: none; border: none; cursor: pointer;
  padding: 8px 4px; font-size: var(--fs-2xs); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text); font-family: var(--ff-display);
  border-bottom: 2px solid transparent; transition: color 0.12s, border-color 0.12s;
}
.avp-tab.selected { color: var(--sel,var(--gold)); border-bottom-color: var(--sel,var(--gold)); }
body.adv-no-avcreate #avp-tab-create { display: none !important; }
#avp-pane-create, #avp-pane-import { padding: var(--sp-5); }
/* ── Pane Créer (matrice) ── */
.avm-sticky {
  position: sticky; top: 0; z-index: 2;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  margin: calc(-1 * var(--sp-5)) calc(-1 * var(--sp-5)) var(--sp-6);
  padding: var(--sp-3) var(--sp-5) 0;
}
/* En mode modal, le bloc epingle reprend exactement le fond de la carte
   (--modal-bg) : plus de bandeau discordant, palette claire incluse. */
#avatar-popup.avatar-popup-as-modal .avm-sticky {
  background: var(--modal-bg);
}
.avm-head {
  display: flex; gap: var(--sp-5); align-items: center; justify-content: center;
  padding-bottom: var(--sp-3);
}
.avm-head-btns { display: flex; flex-direction: column; gap: var(--sp-3); }
.avm-step {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) 0 var(--sp-3);
  border-top: 1px solid var(--border);
}
.avm-step-arr {
  flex: 0 0 auto; width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  background: var(--inset); border: 1px solid var(--border);
  color: var(--sel,var(--gold)); font-size: 1.3rem; line-height: 1;
  border-radius: var(--r-sm); cursor: pointer; padding: 0;
  transition: background 0.12s, border-color 0.12s;
}
.avm-step-arr:hover { background: rgba(var(--gold-rgb), 0.1); border-color: var(--gold-dim); }
.avm-step-mid {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: 8px;
  min-width: 0;
}
.avm-step-name {
  font-family: var(--ff-display); font-size: var(--fs-2xs);
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--sel,var(--gold)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.avm-step-count { font-size: var(--fs-2xs); color: var(--text); flex: 0 0 auto; }
.avm-group-ico { font-size: 1rem; line-height: 1; }
.avm-sex-opt { font-size: 1.25rem; line-height: 1; padding: 5px 15px; }
.avm-preview {
  width: 120px; height: 120px; flex: 0 0 auto; border-radius: 4px; overflow: hidden;
  background: var(--field-bg);
  display: flex; align-items: center; justify-content: center;
}
.avm-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avm-missing { font-size: 2rem; opacity: 0.45; }
.avm-rows { display: flex; flex-direction: column; gap: var(--sp-4); }
.avm-axis-label {
  display: block; font-size: var(--fs-2xs); letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--gold-dim); margin-bottom: 4px;
}
.avm-axis-opts { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.avm-opt {
  background: var(--inset); border: 1px solid transparent; color: var(--text-hi);
  padding: 6px 13px; border-radius: var(--r-sm); font-size: var(--fs-sm); cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.avm-opt:hover { background: rgba(var(--gold-rgb), 0.1); }
.avm-opt.selected { border-color: var(--sel,var(--gold)); color: var(--sel,var(--gold)); background: rgba(var(--gold-rgb), 0.12); }
.avm-btn {
  background: var(--inset); border: 1px solid var(--border); color: var(--text-hi);
  padding: 7px 14px; border-radius: var(--r-sm); font-size: var(--fs-sm); cursor: pointer;
}
.avm-btn:hover { background: rgba(var(--gold-rgb), 0.1); }
.avm-btn:disabled { opacity: 0.4; cursor: default; }
.avm-swatch {
  width: 26px; height: 26px; padding: 2px; border-radius: 50%;
  background: none; border: 2px solid transparent; cursor: pointer; line-height: 0;
}
.avm-swatch span { display: block; width: 100%; height: 100%; border-radius: 50%; border: 1px solid rgba(0,0,0,0.35); }
.avm-swatch.selected { border-color: var(--sel,var(--gold)); }
.avm-mini {
  padding: 2px; border-radius: 8px; line-height: 0; overflow: hidden;
  background: var(--inset); border: 2px solid transparent; cursor: pointer;
}
.avm-mini svg { border-radius: 5px; }
.avm-mini.selected { border-color: var(--sel,var(--gold)); }
.avm-mini svg { display: block; }
.avm-none-opt { min-width: 40px; }
.avm-use { border-color: var(--gold-dim); color: var(--sel,var(--gold)); }
/* ── Pane Importer ── */
.avi-drop {
  border: 2px dashed var(--border); border-radius: var(--r-md); padding: 20px 14px;
  text-align: center; display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
}
.avi-drop.over { border-color: var(--sel,var(--gold)); background: rgba(var(--gold-rgb), 0.08); }
.avi-ico { font-size: 1.8rem; }
.avi-line { font-size: var(--fs-md); color: var(--text-hi); }
.avi-or { font-size: var(--fs-xs); color: var(--text); }
.avi-hint { font-size: var(--fs-xs); color: var(--text); max-width: 300px; }
/* ── Mode modal (lobby / mobile) : les nouveaux éléments rejoignent la
   « carte » comme .avp-header/.avp-cats/.avp-grid (fond --modal-bg, largeur
   fixe, lisere or). Le pane visible porte le bord/rayon inférieur. ── */
#avatar-popup.avatar-popup-as-modal .avp-tabs,
#avatar-popup.avatar-popup-as-modal #avp-pane-create,
#avatar-popup.avatar-popup-as-modal #avp-pane-import {
  background: var(--modal-bg);
  width: min(360px, 100%);
  max-width: 360px;
  border-left: 1px solid var(--gold-dim);
  border-right: 1px solid var(--gold-dim);
}
#avatar-popup.avatar-popup-as-modal #avp-pane-gallery {
  display: flex; flex-direction: column; align-items: center; width: 100%;
}
#avatar-popup.avatar-popup-as-modal #avp-pane-gallery[style*="none"] { display: none; }
#avatar-popup.avatar-popup-as-modal #avp-pane-create,
#avatar-popup.avatar-popup-as-modal #avp-pane-import {
  max-height: 60vh; overflow-y: auto;
  border-bottom: 1px solid var(--gold-dim);
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
  -webkit-overflow-scrolling: touch;
}

/* ══ INVITE-FRIENDS DIALOG (#invite-dialog — modules/ui/invite.mjs) ══
   Opened by App.shareTableLink(): QR code + copy row + messenger
   shortcut buttons + native share sheet on phones. Same card family
   (.ld-card) as the confirmation dialogs. z-index 1300: the waiting-room
   bar lives in the LOBBY view, whose panels stack up to ~900 and whose
   modals (ranking/forum) sit at 1200 — at the game-dialog level (120)
   this dialog would open BEHIND the lobby UI. Kept below toasts (10000). */
#invite-dialog {
  position: fixed; inset: 0; z-index: 1300;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.7);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  animation: ldFade 0.25s ease-out;
}
.inv-card { max-width: 370px; text-align: center; }
.inv-title { display: flex; align-items: center; justify-content: space-between; }
.inv-close {
  background: none; border: none; color: var(--cream);
  font-size: 1.05rem; line-height: 1; cursor: pointer; padding: 2px 6px;
}
.inv-close:hover { color: var(--text-hi); }
.inv-table {
  font-family: var(--ff-display); font-weight: 700;
  color: var(--text-hi); margin-bottom: 12px; word-break: break-word;
}
.inv-qr { display: block; margin: 0 auto 6px; border-radius: 8px; background: #fff; }
.inv-scan { font-size: var(--fs-sm); color: var(--cream); opacity: 0.75; margin-bottom: 14px; }
.inv-linkrow { display: flex; gap: 8px; align-items: stretch; margin-bottom: 14px; }
.inv-link {
  flex: 1 1 auto; min-width: 0;
  font-size: 0.72rem; color: var(--cream);
  background: var(--inset); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 8px 10px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-align: left; direction: ltr;
  display: flex; align-items: center;
}
.inv-copy { flex: 0 0 auto; letter-spacing: 0.04em; }
.inv-share { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.inv-sbtn {
  padding: 8px 12px;
  background: var(--inset); border: 1px solid var(--border);
  border-radius: var(--r-md); color: var(--cream);
  font-size: var(--fs-sm); cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.inv-sbtn:hover { border-color: var(--border-hi); }

/* ══ INVITE LANDING BANNER (login screen — filled by the #join parser) ══ */
#invite-banner {
  margin: 10px auto 4px; padding: 10px 14px; max-width: 420px;
  background: var(--inset);
  border: 1px solid var(--border-hi);
  border-radius: var(--r-md);
  color: var(--text-hi); font-size: var(--fs-md);
  text-align: center; line-height: 1.4;
}
#invite-banner-name { font-weight: 700; }


/* ══ PLAYER NOTES & COLOUR LABELS (modules/notes — web-only extra) ══ */

/* Pastille devant le pseudo (siège ET liste des joueurs). Inline-block plutôt
   qu'un ::before : la pastille doit disparaître avec l'élément quand le joueur
   n'est pas étiqueté, sans laisser d'espace résiduel dans un nom déjà
   tronqué. Son title porte l'aperçu (libellé + extrait de note) au survol. */
.seat-note-tag {
  display: inline-block; vertical-align: middle;
  width: 7px; height: 7px; margin-right: 4px;
  border-radius: var(--r-pill);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45);
}
/* Note en étoiles au siège et dans la liste des joueurs : « ★N » et non cinq
   glyphes — la boîte adverse est à largeur fixe et le pseudo y est déjà
   tronqué (même arbitrage que PlayerNoteBadge.qml). Comme la pastille, le
   span n'existe pas quand le joueur n'est pas noté : rien à masquer, rien qui
   prenne de la place. */
.seat-note-stars {
  display: inline-block; vertical-align: middle;
  margin-right: 4px;
  font-size: var(--fs-2xs); line-height: 1;
  color: var(--star); white-space: nowrap;
}
/* Les DEUX marques — pastille de couleur et badge d'étoiles — se rangent à
   l'autre bout de la ligne du pseudo, qui s'élide contre elles (upstream
   d72d109 pour le badge, demande narmod 07/09 pour les réunir). Elles vivent
   dans un .seat-nv commun : un seul bloc à pousser à droite, et l'écart entre
   les deux ne dépend pas de la présence de l'autre. La ligne ne devient un
   flex que lorsqu'il y a quelque chose à montrer (.has-nv posé par
   seat-render) : sans note ni étiquette, le pseudo garde son rendu d'avant. */
.seat-name.has-nv { display: flex; align-items: center; }
.seat-name.has-nv .seat-name-txt {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.seat-nv { display: inline-flex; align-items: center; }
.seat-name.has-nv .seat-nv { flex: 0 0 auto; margin-left: 4px; }
/* Dans le groupe, la pastille précède les étoiles et n'a plus à s'écarter
   d'un pseudo qui n'est plus derrière elle. */
.seat-name.has-nv .seat-note-tag,
.seat-name.has-nv .seat-note-stars { margin-right: 0; }
.seat-name.has-nv .seat-note-tag + .seat-note-stars { margin-left: 3px; }
/* Structure QML : le pseudo est déjà cadré à gauche, le groupe va se coller au
   bord droit de la boîte — l'ancrage exact de GamePlayerBox.qml. Ailleurs
   (ancienne structure, pseudo centré) le couple pseudo + badges reste centré,
   pour ne pas décaler des sièges qui ne le demandaient pas. */
html[data-seat-struct="qml"] .seat-name.has-nv { justify-content: flex-start; }
html[data-seat-struct="qml"] .seat-name.has-nv .seat-nv { margin-left: auto; }
html:not([data-seat-struct="qml"]) .seat-name.has-nv { justify-content: center; }
/* Tailles de glyphe de d72d109 : 13 px dans la boîte large, 10 en compact. */
html[data-seat-struct="qml"] .seat-name .seat-note-stars { font-size: 13px; }
html[data-seat-struct="qml"] .seat-narrow .seat-name .seat-note-stars { font-size: 10px; }

/* Bloc de la carte joueur */
/* Même gabarit que les blocs voisins de la carte joueur (.pim-ig,
   .pim-stat-box) : la carte est un flex colonne centré, sans width:100% le
   bloc se rétracte à son contenu — d'où la peau et la largeur calquées. */
.nv-block {
  width: 100%; box-sizing: border-box;
  padding: 10px 12px;
  background: var(--field-bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  text-align: left;
}
.nv-hd {
  font-size: var(--fs-2xs); color: var(--text);
  text-transform: uppercase; letter-spacing: 0.08em;
  margin-bottom: 8px;
}
/* Barre de notation de la carte joueur : glyphes ★/☆ posés sur des boutons
   nus, donc alignés sur la taille du texte et colorables — pas d'image à
   fournir par densité d'écran (parité PlayerRatingStars.qml). La zone
   cliquable reste au gabarit tactile grâce au padding. */
.nv-stars { display: flex; gap: 2px; margin-bottom: 8px; }
.nv-star {
  padding: 2px 4px;
  background: none; border: 0;
  color: var(--text); opacity: 0.55;
  font-size: var(--fs-lg); line-height: 1;
  cursor: pointer; transition: transform 0.12s, opacity 0.12s;
}
.nv-star.on { color: var(--star); opacity: 1; }
.nv-star:hover { transform: scale(1.12); }
.nv-star:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.nv-swatches { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.nv-sw {
  width: 22px; height: 22px; padding: 0;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  cursor: pointer; transition: transform 0.12s, box-shadow 0.12s;
}
.nv-sw:hover { transform: scale(1.12); }
.nv-sw.on { box-shadow: 0 0 0 2px var(--gold); }
.nv-sw:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.nv-sw-none {
  background: transparent; color: var(--text);
  font-size: var(--fs-2xs); line-height: 1;
}
/* Champ de renommage du libellé — visible seulement quand une couleur est
   posée. Même peau que la zone de note, sur une seule ligne. */
.nv-label-row { margin-bottom: 8px; }
.nv-label {
  display: block; width: 100%; box-sizing: border-box;
  padding: 5px 8px;
  background: var(--card, rgba(0, 0, 0, 0.25));
  border: 1px solid var(--border); border-radius: var(--r-sm);
  color: var(--text-hi); font-family: inherit; font-size: var(--fs-sm);
}
.nv-label:focus { border-color: var(--border-hi); outline: none; }
.nv-text {
  display: block; width: 100%; box-sizing: border-box;
  padding: 6px 8px; resize: vertical; min-height: 54px;
  background: var(--card, rgba(0, 0, 0, 0.25));
  border: 1px solid var(--border); border-radius: var(--r-sm);
  color: var(--text-hi); font-family: inherit; font-size: var(--fs-sm);
  line-height: 1.35;
}
.nv-text:focus { border-color: var(--border-hi); outline: none; }
.nv-foot { min-height: 14px; margin-top: 4px; }
.nv-saved { font-size: var(--fs-2xs); color: var(--gold-dim); }

@media (prefers-reduced-motion: reduce) {
  .nv-sw { transition: none; }
  .nv-sw:hover { transform: none; }
  .nv-star { transition: none; }
  .nv-star:hover { transform: none; }
}

/* ══ Live / embedded spectator mode (/live) ═══════════════════════════════
   Driven by :root[data-live="1"], set by the LIVE_BOOT script proxy.js
   injects for /live only. The markup is the ordinary client — live mode just
   hides the chrome that has no meaning for a guest watching inside an iframe
   on pokerth.net, and reveals the .live-only pieces of the slim header
   (identity, appearance, build). The table itself is untouched, which is the
   whole point: table work is inherited here rather than ported twice. */
.live-only { display: none; }
/* Outside /live nothing .live-only may ever show. The plain rule above
   (0,1,0) lost to `.header .btn-sm { display: inline-flex }` (0,2,0), so the
   live appearance button and the (empty) light/dark button showed in the web
   client's lobby and table headers. Scoped to normal mode, /live is untouched. */
:root:not([data-live="1"]) .live-only { display: none !important; }
:root[data-live="1"] .live-only { display: inline-flex; align-items: center; }
:root[data-live="1"] .live-id {
  gap: 6px; font-weight: 600; color: var(--text-hi);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 45%; min-width: 0;
}
:root[data-live="1"] .live-ver {
  opacity: .5; font-size: .72rem; margin-left: 6px; white-space: nowrap;
}
/* Lobby header — no account actions, no side panels: those arrive as tabs in
   the live lobby instead. */
:root[data-live="1"] #s-lobby .header .header-pills,
:root[data-live="1"] #lobby-chat-btn,
:root[data-live="1"] #pm-btn-lobby,
:root[data-live="1"] #forum-btn-lobby,
:root[data-live="1"] #ranking-btn-lobby,
:root[data-live="1"] #s-lobby .hdr-ovf-wrap,
/* Game header — the back arrow, the table name and sound stay; everything a
   spectator cannot act on goes. */
:root[data-live="1"] #forum-btn-game,
:root[data-live="1"] #ranking-btn-game,
:root[data-live="1"] #tableranking-btn-game,
:root[data-live="1"] #haptic-toggle-btn,
:root[data-live="1"] #voice-toggle-btn,
:root[data-live="1"] #admin-startnobots-btn,
:root[data-live="1"] #admin-start-btn,
:root[data-live="1"] #admin-close-btn,
:root[data-live="1"] #admin-kick-btn,
:root[data-live="1"] #s-game .hdr-ovf-wrap,
/* No install prompt inside an iframe. */
:root[data-live="1"] #install-btn { display: none !important; }

/* ── Live mode: the connect screen is one guest Login button ──────────────
   The machinery underneath is the ordinary one (server-mode = pokerthnet,
   guest mode on, empty nick → persistent guest name); live mode only strips
   the choices a guest watching from the website never makes. The CONNECT
   button and the status line are the real ones, so errors and progress still
   report exactly as they do in the full client. */
:root[data-live="1"] #login-step1 { display: none !important; }
:root[data-live="1"] #login-step2 { display: block !important; }
:root[data-live="1"] #s-connect .header,
:root[data-live="1"] #s-connect .login-back,
:root[data-live="1"] #s-connect .login-avatar-top,
:root[data-live="1"] #s-connect .guest-line,
:root[data-live="1"] #s-connect .connect-footer,
:root[data-live="1"] #server-mode-seg,
:root[data-live="1"] #login-form,
:root[data-live="1"] #tls-row,
:root[data-live="1"] #register-link-row,
:root[data-live="1"] #invite-banner { display: none !important; }
:root[data-live="1"] #s-connect .btn-primary { margin: 18px auto; display: block; }

/* ── Live mode: the table list ────────────────────────────────────────────
   Replaces the full lobby body on /live with the row-per-table layout of the
   spectator tool it succeeds. Columns follow the same order: name, seats,
   status, type, private, spectators, timeouts. */
:root[data-live="1"] .lobby-body,
:root[data-live="1"] #players-panel { display: none !important; }

/* Two columns: the table list, a drag grip, and the lobby chat on the right —
   same arrangement as the full client and the QML lobby. The width lives in
   --live-chat-w so the grip only has to move one custom property. */
/* The chat panel is a direct child of #s-lobby, not of .lobby-body, so hiding
   the body does not hide it. Docked, its parent is #live-chat-side and this
   selector no longer matches. Undocked — the client's reparent() ran, the
   module failed, a stale file is being served — it is hidden instead of
   smeared across the top of the lobby. A layout must fail closed. */
:root[data-live="1"] #s-lobby > #lobby-chat-panel { display: none !important; }

:root[data-live="1"] .live-lobby {
  --live-chat-h: 200px;
  display: flex !important; flex-direction: column !important;
  /* .live-only sets align-items:center for the small inline bits that share
     the class. Inherited here it shrank both panels to their content width
     and centred them, which is what left the list floating in the middle of
     an empty lobby. */
  align-items: stretch !important; align-self: stretch; width: 100%;
  flex: 1; min-height: 0; gap: 0; padding: 12px;
}
/* Same frame as the lobby's own columns: one border, 8px radius, panel
   background, sp-3 of padding — so /live reads as the same product. */
:root[data-live="1"] .live-lobby .llb-main,
:root[data-live="1"] .live-lobby .llb-side {
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--panel); padding: var(--sp-3); overflow: hidden;
  display: flex; flex-direction: column; min-width: 0; min-height: 0;
}
:root[data-live="1"] .live-lobby .llb-main { flex: 1; }
:root[data-live="1"] .live-lobby .llb-side {
  height: var(--live-chat-h); flex: 0 0 var(--live-chat-h);
}
/* The chat panel is the client's own node, moved here. Its inline styles cap
   the height for the dropdown it normally is, so they have to be overridden. */
/* The chat panel is a floating window elsewhere in the client: fixed, placed
   by inline styles, raised above everything. Docked here it has to be an
   ordinary block again, or it draws itself across the top of the lobby
   whatever its parent is — the client does the same reset for its own grid. */
:root[data-live="1"] .live-lobby #lobby-chat-panel {
  display: flex !important; flex: 1; min-height: 0;
  position: static !important; inset: auto !important;
  transform: none !important; z-index: auto !important;
  left: auto !important; top: auto !important;
  right: auto !important; bottom: auto !important;
  width: auto !important; height: auto !important;
  max-height: none !important; box-shadow: none !important;
  border-radius: 0; background: transparent; border: 0;
}
/* Cancel the column padding so the header spans the full width, exactly as
   the lobby does for its own panels. */
:root[data-live="1"] .live-lobby #lobby-chat-panel > .g-chat-panel-header {
  margin: calc(var(--sp-3) * -1) calc(var(--sp-3) * -1) var(--sp-3);
}
:root[data-live="1"] .live-lobby #lobby-chat-panel #chat {
  max-height: none !important; flex: 1;
}
/* Horizontal grip between the list and the chat strip. Same 18px gutter the
   lobby leaves between its own panels, with the handle drawn inside it. */
:root[data-live="1"] .live-lobby .llb-resizer {
  flex: 0 0 18px; cursor: row-resize; touch-action: none;
  position: relative; background: transparent;
}
:root[data-live="1"] .live-lobby .llb-resizer::after {
  content: ''; position: absolute; left: 42%; right: 42%; top: 50%; height: 4px;
  transform: translateY(-50%); border-radius: var(--r-xs);
  background: var(--border); transition: background-color .12s;
}
:root[data-live="1"] .live-lobby .llb-resizer:hover::after,
:root[data-live="1"] .live-lobby .llb-resizer:focus-visible::after,
:root[data-live="1"] .live-lobby .llb-resizer.dragging::after {
  background: var(--gold);
}
:root[data-live="1"] .live-lobby .llb-resizer:focus-visible { outline: none; }
/* Narrow screens: a fixed share of the height, no dragging. */
@media (max-width: 720px) {
  :root[data-live="1"] .live-lobby .llb-side { flex: 0 0 34%; height: auto; }
  :root[data-live="1"] .live-lobby .llb-resizer { flex-basis: 12px; }
}
/* The flag has no intrinsic size of its own here, and a button with an empty
   SVG is a button nobody can find. */
/* These ids are the old header twins of the ••• menu entries, hidden at all
   sizes by the rule near .header above and again in the mobile block. Live
   mode reuses them on purpose — i18n.mjs already keeps the active flag in
   sync with exactly these three — so it has to win those two rules back. */
:root[data-live="1"] #lang-toggle-lobby,
:root[data-live="1"] #lang-toggle-game,
:root[data-live="1"] #fs-btn-lobby,
:root[data-live="1"] #fs-btn-game,
:root[data-live="1"] #s-lobby #lang-toggle-lobby,
:root[data-live="1"] #s-lobby #fs-btn-lobby { display: inline-flex !important; }

:root[data-live="1"] .header [id^="lang-toggle-"] svg,
:root[data-live="1"] .live-lang-row [id^="lang-toggle-"] svg {
  display: block; width: 22px; height: 22px;
}
:root[data-live="1"] .header [id^="lang-toggle-"],
:root[data-live="1"] .live-lang-row [id^="lang-toggle-"] { min-width: 30px; }
.live-lobby .llb-head,
.live-lobby .llb-line {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 62px 42px 92px 62px 52px 76px 26px;
  align-items: center; gap: 6px;
}
.live-lobby .llb-head {
  padding: 4px 10px 6px; font-size: .68rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-dim, var(--text));
  opacity: .75; border-bottom: 1px solid var(--border, rgba(255,255,255,.12));
}
.live-lobby .llb-head .llb-chev { display: none; }
.live-lobby .llb-row {
  border-bottom: 1px solid var(--border, rgba(255,255,255,.08));
  background: var(--field-bg, transparent);
}
.live-lobby .llb-row.open { background: var(--panel-bg, rgba(255,255,255,.04)); }
.live-lobby .llb-line { padding: 9px 10px; cursor: pointer; }
.live-lobby .llb-line:hover { background: rgba(255, 255, 255, .05); }
.live-lobby .llb-c-name {
  font-weight: 600; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.live-lobby .llb-c-num,
.live-lobby .llb-c-ico,
.live-lobby .llb-c-time,
.live-lobby .llb-c-type { text-align: center; font-size: .82rem; }
.live-lobby .llb-chev { text-align: right; opacity: .6; }
.live-lobby .llb-detail { padding: 2px 12px 14px; }
.live-lobby .llb-seats { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.live-lobby .llb-seat {
  padding: 2px 8px; border-radius: 999px; font-size: .8rem;
  background: rgba(255, 255, 255, .07);
}
.live-lobby .llb-meta {
  display: flex; flex-wrap: wrap; gap: 4px 18px;
  font-size: .8rem; opacity: .85; margin-bottom: 10px;
}
.live-lobby .llb-spec {
  font: inherit; font-size: .85rem; font-weight: 600; cursor: pointer;
  padding: 6px 14px; border-radius: 6px;
  color: var(--text-hi); background: var(--btn-bg, rgba(255,255,255,.1));
  border: 1px solid var(--border-hi, rgba(255,255,255,.25));
}
.live-lobby .llb-empty { padding: 22px 10px; text-align: center; opacity: .7; }
@media (max-width: 720px) {
  .live-lobby .llb-head,
  .live-lobby .llb-line { grid-template-columns: minmax(0, 1fr) 52px 34px 44px 42px 26px; }
  .live-lobby .llb-c-type,
  .live-lobby .llb-c-time { display: none; }
}

/* ── Live mode: tabs above the list, and the players pane ────────────────── */
.live-lobby .llb-tabs {
  display: flex; gap: 2px; flex-shrink: 0;
  background: var(--chrome-tint); border-bottom: 1px solid var(--border);
}
:root[data-live="1"] .live-lobby .llb-tabs {
  margin: calc(var(--sp-3) * -1) calc(var(--sp-3) * -1) var(--sp-3);
}
.live-lobby .llb-tab {
  font: inherit; font-size: var(--fs-2xs); font-weight: 600; cursor: pointer;
  letter-spacing: .15em; text-transform: uppercase;
  padding: 8px 14px; color: var(--gold-dim);
  background: transparent; border: 0; border-bottom: 2px solid transparent;
}
.live-lobby .llb-tab:hover { background: rgba(255, 255, 255, .06); }
.live-lobby .llb-tab.on {
  color: var(--text-hi); border-bottom-color: var(--gold, #c8a24a);
}
:root[data-live="1"] .live-lobby .llb-main { overflow-y: auto; }
.live-lobby .llb-players { padding-top: 2px; }
/* Two lines per player, avatar on the left, Spectate on the right — the
   layout of the spectator tool this replaces. */
.live-lobby .llb-pl {
  display: flex; align-items: center; gap: 10px; padding: 6px 10px;
  border-bottom: 1px solid var(--border);
}
.live-lobby .llb-pl-av {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%;
  object-fit: cover; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--inset); font-size: .8rem; font-weight: 700;
}
.live-lobby .llb-pl-tx {
  flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.25;
}
.live-lobby .llb-pl-name {
  font-weight: 600; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.live-lobby .llb-pl-name:hover { color: var(--gold); }
.live-lobby .llb-pl-at {
  min-width: 0; font-size: .78rem; opacity: .7;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.live-lobby .llb-c-spec { white-space: nowrap; }
.live-lobby .llb-spec-sm { padding: 3px 10px; font-size: .78rem; }

/* Read-only chat: an embedded viewer watches, it does not post. The panel is
   the client's own node, so the composer is hidden rather than removed. */
:root[data-live="1"] .live-lobby #lobby-chat-panel .chat-input,
:root[data-live="1"] .live-lobby #lobby-chat-panel .chat-emoji-panel,
:root[data-live="1"] .live-lobby #lobby-wait-actions { display: none !important; }

/* ── Live mode: the login card ────────────────────────────────────────────
   The card carries the server's own figures — players online, tables running,
   players waiting, games today — rather than a bare status line. They are the
   client's #lc-live node, moved here from the Internet card of step 1. */
/* Nothing goes under the button but the figures. The card says what this is
   and the figures say what the server is doing; "Internet connection (auth)
   v5.1 — 22 players…" is the web client talking about itself. Errors are the
   exception: a spectator who cannot connect must be told why. */
:root[data-live="1"] #cstatus { display: none; }
:root[data-live="1"] #cstatus.err { display: block; }

:root[data-live="1"] .live-stats-host { display: block; margin: 16px 0 2px; }
:root[data-live="1"] .live-stats-host #lc-live {
  display: grid !important;
  grid-template-columns: repeat(2, auto); justify-content: center;
  gap: 7px 22px; font-size: .82rem; opacity: .92;
}
:root[data-live="1"] .live-stats-host .lcl {
  margin-right: 0 !important; gap: 6px !important; justify-self: start;
}
/* The wording only shows here — in the Internet card it stays on the title. */
:root[data-live="1"] .live-stats-host .lcl-x { display: inline; opacity: .82; }
/* Login card: language pinned to the top-left corner, light/dark to the
   top-right. They are chrome for the card, not items in its column, so they
   sit on it rather than in the flow — which also keeps the logo centred. */
:root[data-live="1"] #s-connect .card { position: relative; }
:root[data-live="1"] .live-card-corners { display: block; }
:root[data-live="1"] .live-card-corners .live-tl,
:root[data-live="1"] .live-card-corners .live-tr {
  position: absolute; top: 10px; z-index: 2;
}
:root[data-live="1"] .live-card-corners .live-tl { left: 10px; }
:root[data-live="1"] .live-card-corners .live-tr { right: 10px; }
.lcl-x { display: none; }
@media (max-width: 420px) {
  :root[data-live="1"] .live-stats-host #lc-live { grid-template-columns: auto; }
}

/* ── Embedded on the website (?embed=1) ───────────────────────────────────
   Same page as /live, inside an iframe on pokerth.net. Only what has no
   meaning in a frame is removed: the page cannot be installed as an app from
   inside one. The layout is plain /live — the host page sizes the frame, the
   screens fill it, and the table list scrolls inside it. (Up to web.114 the
   screens went height:auto here so the frame could report a content height;
   that dropped the list's scroller and left the table view at 420px.) */
:root[data-embed="1"], :root[data-embed="1"] body { overflow: hidden; }
:root[data-embed="1"] #install-btn,
:root[data-embed="1"] .connect-footer { display: none !important; }

/* Operator chose to run /live without the lobby chat (admin → Defaults). The
   strip and its grip go; the table list takes the height. */
:root[data-live-nochat="1"] .live-lobby .llb-side,
:root[data-live-nochat="1"] .live-lobby .llb-resizer { display: none !important; }

/* ── Live mode: header tail ───────────────────────────────────────────────
   The build string was useful while /live was being brought up; on a page
   embedded in the site it is noise. The language, light/dark and appearance
   buttons belong hard right, so the guest name keeps the left. */
:root[data-live="1"] .live-ver { display: none !important; }
/* Fullscreen is now the first visible button of the group in both headers, so
   it carries the margin that pushes the whole set hard right. */
:root[data-live="1"] #fs-btn-lobby,
:root[data-live="1"] #fs-btn-game { margin-left: auto !important; }
/* Appearance is a table setting — deck, felt, seat pack. In the lobby there
   is no table to dress, so the button has nothing to change there. */
:root[data-live="1"] #live-theme-lobby { display: none !important; }
:root[data-live="1"] #live-mode-lobby,
:root[data-live="1"] #live-mode-game,
:root[data-live="1"] #live-mode-connect { display: inline-flex !important; }
:root[data-live="1"] .live-lang-row { gap: 6px; }

/* ── Live mode: nothing to open on the felt ───────────────────────────────
   The hand log and the poker-combinations card are player aids: one is a
   record of a session the spectator has no part in, the other a reminder for
   someone deciding a move. Their buttons go, and so do the panels, which are
   never built because nothing can open them. */
:root[data-live="1"] #hands-toggle-btn,
:root[data-live="1"] #log-toggle-btn,
:root[data-live="1"] #g-log-panel,
:root[data-live="1"] #hands-overlay { display: none !important; }

/* ── Live mode: no table chat ─────────────────────────────────────────────
   A visitor on the site does not read the chat of the table being watched.
   The seated web client still lets a spectator read it — this is /live only.
   The felt button, the floating button and the panel go; index.mjs stubs the
   opener and drops incoming table chat. !important also beats the inline
   display the opener would set. */
:root[data-live="1"] #chat-toggle-btn,
:root[data-live="1"] #gchat-fab,
:root[data-live="1"] #g-chat-panel { display: none !important; }

/* ── Live mode: no fullscreen inside a frame ──────────────────────────────
   From inside an iframe, fullscreen depends on how the host page built it,
   and on pokerth.net the button does nothing. Hidden wherever /live is framed
   (embed.mjs sets data-framed, ?embed=1 or not); the standalone page keeps
   it. The margin that pushes the group hard right moves to the next visible
   button of each header. Specificity beats the rules that win it back above. */
:root[data-live="1"][data-framed="1"] #s-game #fs-btn-game,
:root[data-live="1"][data-framed="1"] #s-lobby #fs-btn-lobby { display: none !important; }
:root[data-live="1"][data-framed="1"] #s-lobby #lang-toggle-lobby,
:root[data-live="1"][data-framed="1"] #s-game #sound-toggle-btn { margin-left: auto !important; }
