:root {
    --bg: #0b1120;
    --bg-soft: #111a2e;
    --panel: #16223b;
    --panel-2: #1c2b48;
    --border: #26365a;
    --text: #e6edf7;
    --muted: #8ea1c2;
    --accent: #38bdf8;
    --accent-strong: #0ea5e9;
    --hit: #f43f5e;
    --miss: #64748b;
    --ship: #334769;
    --good: #34d399;
    --amber: #f59e0b;
    --violet: #a78bfa;
    --indigo: #6366f1;
    --radius: 14px;
    --shadow: 0 10px 40px rgba(0, 0, 0, 0.35);

    /* Paliers de succès (page /succes) */
    --t-bronze: #e0894a;
    --t-argent: #cbd5e1;
    --t-or: #f6c344;
    --t-amethyste: #b083f0;
    --t-diamant: #6ee7f0;
    --t-cosmique: #d946ef;
    --cosmic-gradient: linear-gradient(120deg, #7c3aed, #d946ef, #38bdf8, #7c3aed);
}

* { box-sizing: border-box; }

/* Scrollbar aux couleurs du site (Firefox via scrollbar-color/-width,
   Chrome/Edge/Safari via ::-webkit-scrollbar) — même recette que
   .profile-shelf ci-dessous (thumb var(--border), survol var(--muted)),
   généralisée à toute la page plutôt que ce seul élément. */
html { scrollbar-width: thin; scrollbar-color: var(--border) var(--bg-soft); }
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--bg-soft); }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; border: 3px solid var(--bg-soft); }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }
::-webkit-scrollbar-corner { background: var(--bg-soft); }

body {
    margin: 0;
    font-family: "Space Grotesk", system-ui, sans-serif;
    background: radial-gradient(1200px 600px at 80% -10%, #1a2748 0%, var(--bg) 55%);
    color: var(--text);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    animation: page-fade-in .4s ease both;
}
@keyframes page-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    body { animation: none; }
}

a { color: inherit; text-decoration: none; }

/* ---------- Effets visuels (togglables depuis /admin/effects) ---------- */
.starfield { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.starfield canvas { display: block; }

.hyperspace-overlay { position: fixed; inset: 0; z-index: 500; opacity: 1; transition: opacity .38s ease; }
.hyperspace-overlay.hyperspace-reveal { opacity: 0; }
.hyperspace-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* Zoom + fondu de la div de connexion, en parallèle du saut (voir login.html) */
.auth-panel.login-zoom-out {
    transition: transform .35s cubic-bezier(.3, 0, .7, 1), opacity .3s ease;
    transform: scale(2.4);
    opacity: 0;
    pointer-events: none;
}

/* ---------- Topbar ---------- */
.topbar {
    display: flex;
    align-items: baseline;
    gap: 16px;
    padding: 18px 28px;
    border-bottom: 1px solid var(--border);
    background: rgba(11, 17, 32, 0.6);
    backdrop-filter: blur(8px);
    /* backdrop-filter établit son propre contexte d'empilement : sans
       position+z-index explicites ici, les dropdowns internes (notif/user/
       squad/search, z-index 120) restent piégés DANS ce contexte et ne
       peuvent jamais dépasser le contenu de page qui suit dans le DOM (visible
       surtout sur /profil, dont le bloc héros démarre juste sous la topbar). */
    position: relative;
    z-index: 130;
}
/* Masque la topbar sur la page de connexion (pas de session) sans retirer
   l'élément du DOM : `.topbar-nav` n'est rendue que pour un utilisateur
   authentifié (voir base.html), et la bascule douce post-connexion
   (softNavigateToLibrary dans effects.js) réutilise ce même <header> en
   remplaçant juste son innerHTML — un `{% block %}` qui aurait retiré le
   header sur cette page cassait cette bascule (curTopbar devenait introuvable,
   la topbar ne réapparaissait jamais après connexion). Cette règle se
   corrige donc toute seule dès que le contenu swappé contient la nav. */
.topbar:not(:has(.topbar-nav)) { display: none; }
.brand { font-weight: 700; font-size: 1.25rem; display: inline-flex; align-items: center; }
.brand-mark { height: 28px; width: auto; margin-right: 8px; border-radius: 6px; }
.topbar-sub { color: var(--muted); font-size: 0.85rem; }
.topbar-nav { margin-left: auto; display: flex; align-items: center; gap: 14px; }
.role-badge {
    font-size: 0.68rem; text-transform: uppercase; letter-spacing: .5px;
    padding: 2px 7px; border-radius: 999px; border: 1px solid var(--accent); color: var(--accent);
}
.role-badge.role-admin, .role-badge.role-superadmin { border-color: #f59e0b; color: #f59e0b; }

.user-menu-wrap { position: relative; }
.user-menu-btn {
    width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border);
    background: var(--panel-2); color: var(--text); font-size: 0.85rem; font-weight: 700;
    display: flex; align-items: center; justify-content: center; cursor: pointer; line-height: 1;
}
.user-menu-btn:hover { border-color: var(--accent); color: var(--accent); }
.user-dropdown {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 120; width: 220px;
    background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
    box-shadow: 0 14px 34px rgba(0,0,0,.45); padding: 6px;
}
.user-dropdown.hidden { display: none; }
.user-dropdown-head {
    display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
    padding: 10px 10px 12px; border-bottom: 1px solid var(--border); margin-bottom: 6px;
}
.user-dropdown-name { font-size: 0.9rem; font-weight: 600; color: var(--text); word-break: break-all; }
.user-dropdown-item {
    display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; background: none; border: none;
    color: var(--text); font-size: 0.85rem; font-family: inherit; cursor: pointer;
    padding: 10px 10px; border-radius: 8px; text-decoration: none;
}
.user-dropdown-item svg { flex: none; }
.user-dropdown-item:hover { background: var(--bg-soft); }
.user-dropdown-item-logout { color: var(--hit); }
.user-dropdown form { margin: 0; }

.notif-bell-wrap { position: relative; }
.notif-bell-btn {
    position: relative; display: flex; align-items: center; justify-content: center;
    background: none; border: none; cursor: pointer;
    padding: 8px; border-radius: 8px; color: var(--muted);
}
.notif-bell-btn:hover { background: var(--bg-soft); color: var(--text); }
.notif-badge {
    position: absolute; top: 1px; right: 1px; min-width: 16px; height: 16px; padding: 0 3px;
    border-radius: 999px; background: var(--hit); color: #fff; font-size: 0.62rem; font-weight: 700;
    display: flex; align-items: center; justify-content: center; line-height: 1;
}
.notif-badge.hidden { display: none; }
.notif-dropdown {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 120; width: 300px; max-height: 360px;
    overflow-y: auto; background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
    box-shadow: 0 14px 34px rgba(0,0,0,.45); padding: 6px;
}
.notif-dropdown.hidden { display: none; }
.notif-item {
    display: flex; align-items: center; gap: 8px; padding: 10px 10px; border-radius: 8px; font-size: 0.85rem;
}
.notif-item:hover { background: var(--bg-soft); }
.notif-item-text { flex: 1; min-width: 0; }
.notif-dismiss-btn {
    flex-shrink: 0; background: none; border: 1px solid var(--border); color: var(--muted);
    width: 22px; height: 22px; border-radius: 50%; cursor: pointer; font-size: 0.68rem; line-height: 1;
    transition: border-color .12s, color .12s;
}
.notif-dismiss-btn:hover { border-color: var(--hit); color: var(--hit); }
.notif-empty { padding: 14px 10px; text-align: center; font-size: 0.85rem; }
.role-badge.role-guest { border-color: var(--muted); color: var(--muted); }

/* ---------- Popup de notification (toast) ----------
   Alerte immédiate à l'arrivée d'une invitation (salon, squad) ou d'une
   demande d'accès invité (admin), en complément de la cloche — la cloche
   reste la source persistante, un popup fermé/expiré sans agir laisse
   l'entrée intacte dans le dropdown. Prototypé dans static/preview.html
   avant généralisation ici. `top` suit --topbar-height (voir base.html)
   pour toujours se placer juste sous la topbar, quelle que soit sa hauteur
   réelle (chargement du webfont, etc.). */
.toast-stack {
    position: fixed; top: calc(var(--topbar-height, 66px) + 14px); right: 20px; z-index: 250;
    display: flex; flex-direction: column; gap: 10px;
    width: min(360px, 92vw); pointer-events: none;
}
.toast {
    pointer-events: auto; position: relative; overflow: hidden;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: 0 16px 44px rgba(0,0,0,.5);
    padding: 14px 16px 12px; display: flex; gap: 12px; align-items: flex-start;
    transform: translateX(120%); opacity: 0;
    animation: toast-in .38s cubic-bezier(.2,.9,.3,1.25) forwards;
}
.toast.leaving { animation: toast-out .22s ease forwards; }
@keyframes toast-in { to { transform: translateX(0); opacity: 1; } }
@keyframes toast-out { to { transform: translateX(120%); opacity: 0; } }
.toast-icon {
    width: 38px; height: 38px; border-radius: 10px; flex: none;
    display: flex; align-items: center; justify-content: center; font-size: 1.15rem;
}
.toast-icon img { width: 100%; height: 100%; object-fit: contain; padding: 3px; }
.toast-body { flex: 1; min-width: 0; }
.toast-title { font-weight: 700; font-size: .89rem; margin: 0 0 2px; line-height: 1.3; }
.toast-title b { color: var(--text); }
.toast-desc { font-size: .78rem; color: var(--muted); margin: 0; }
.toast-actions { display: flex; gap: 8px; margin-top: 10px; }
.toast-actions button {
    font-family: inherit; font-size: .78rem; font-weight: 700; cursor: pointer;
    border-radius: 999px; padding: 6px 13px; border: 1px solid transparent;
    transition: background .15s, border-color .15s, transform .1s;
}
.toast-actions button:active { transform: translateY(1px); }
.toast-actions button:disabled { opacity: .5; cursor: default; }
.toast-actions .toast-btn-primary { background: var(--accent-strong); color: #04121f; }
.toast-actions .toast-btn-primary:hover:not(:disabled) { background: var(--accent); }
.toast-actions .toast-btn-ghost { background: transparent; border-color: var(--border); color: var(--text); }
.toast-actions .toast-btn-ghost:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.toast-close {
    position: absolute; top: 8px; right: 8px; background: none; border: none; color: var(--muted);
    cursor: pointer; width: 22px; height: 22px; border-radius: 50%; font-size: .7rem;
    display: flex; align-items: center; justify-content: center; transition: background .12s, color .12s;
}
.toast-close:hover { background: var(--bg-soft); color: var(--text); }
.toast-progress { position: absolute; bottom: 0; left: 0; height: 3px; }
.toast-progress.animate { animation: toast-progress-shrink linear forwards; }
@keyframes toast-progress-shrink { from { width: 100%; } to { width: 0%; } }

.flash-stack { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
.flash-msg { padding: 10px 14px; border-radius: 10px; font-size: 0.9rem; border: 1px solid var(--border); }
.flash-msg.flash-error { border-color: var(--hit); color: #fecaca; background: rgba(239,68,68,.12); }
.flash-msg.flash-success { border-color: #22c55e; color: #bbf7d0; background: rgba(34,197,94,.12); }

/* Modal d'invitation (voir static/js/notifications.js) : overlay global,
   au-dessus de tout le reste (même le sélecteur de couleur Uno à z-index
   120 ou les écrans de fin de jeu à 150). */
.invite-modal-backdrop {
    position: fixed; inset: 0; z-index: 200; padding: 24px;
    background: rgba(4, 8, 16, .78); backdrop-filter: blur(3px);
    display: flex; align-items: center; justify-content: center;
    opacity: 0; pointer-events: none; transition: opacity .15s ease;
}
.invite-modal-backdrop.open { opacity: 1; pointer-events: auto; }
.invite-modal {
    width: min(420px, 100%); max-height: 82vh; display: flex; flex-direction: column;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: 0 24px 70px rgba(0,0,0,.6); overflow: hidden;
    transform: translateY(10px) scale(.97); transition: transform .18s ease;
}
.invite-modal-backdrop.open .invite-modal { transform: translateY(0) scale(1); }
.invite-modal-head {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 18px 20px 14px; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.invite-modal-head h3 { margin: 0; font-size: 1.05rem; }
.invite-modal-head p { margin: 2px 0 0; font-size: .78rem; color: var(--muted); }
.invite-modal-close {
    background: none; border: none; color: var(--muted); cursor: pointer;
    font-size: 0.95rem; width: 30px; height: 30px; border-radius: 8px; flex-shrink: 0;
}
.invite-modal-close:hover { background: var(--bg-soft); color: var(--text); }
.invite-modal-body { padding: 14px 18px 4px; display: flex; flex-direction: column; gap: 0; min-height: 0; }
.invite-modal-search-wrap { position: relative; margin-bottom: 6px; }
.invite-modal-search-icon {
    position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
    color: var(--muted); font-size: .82rem; pointer-events: none;
}
.invite-modal-filter {
    width: 100%; background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
    padding: 10px 14px 10px 34px; border-radius: 999px; font-size: .9rem; font-family: inherit;
}
.invite-modal-filter:focus { outline: none; border-color: var(--accent); }
.invite-modal-list { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; padding: 8px 0 4px; }
.invite-modal-item {
    display: flex; align-items: center; gap: 11px;
    padding: 8px 8px; border-radius: 10px; transition: background .12s;
}
.invite-modal-item:hover { background: var(--bg-soft); }
.invite-modal-avatar {
    width: 34px; height: 34px; border-radius: 50%; flex: none;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800; font-size: .88rem; color: #04121f;
}
.invite-modal-item-name {
    flex: 1; min-width: 0; font-weight: 600; font-size: .89rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.invite-modal-guest-tag {
    margin-left: 6px; font-size: .68rem; font-weight: 700; color: var(--muted);
    background: var(--bg-soft); border-radius: 999px; padding: 1px 8px;
}
.invite-modal-send-btn {
    flex: none; font-family: inherit; font-size: .78rem; font-weight: 700; cursor: pointer;
    background: transparent; border: 1px solid var(--border); color: var(--text);
    padding: 6px 12px; border-radius: 999px; transition: border-color .15s, color .15s, background .15s;
}
.invite-modal-send-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.invite-modal-send-btn:disabled { cursor: default; border-color: rgba(52,211,153,.4); color: var(--good); background: rgba(52,211,153,.08); }
.invite-modal-empty { padding: 26px 0; text-align: center; color: var(--muted); font-size: 0.85rem; }
/* Sélection (admin/notify.html) : même liste .invite-modal-item que le
   panneau squad, mais le bouton reste cliquable (toggle) plutôt que
   désactivé une fois l'action faite — d'où une classe .is-selected dédiée
   plutôt que réutiliser l'état :disabled ci-dessus. */
.invite-modal-item.is-selected { background: var(--bg-soft); }
.invite-modal-item.is-selected .invite-modal-send-btn {
    border-color: rgba(52,211,153,.4); color: var(--good); background: rgba(52,211,153,.08);
}
.notify-user-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.notify-user-chip {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: .8rem; font-weight: 600; color: var(--text);
    background: var(--bg-soft); border: 1px solid var(--border);
    padding: 4px 6px 4px 12px; border-radius: 999px;
}
.notify-user-chip-remove {
    font-family: inherit; font-size: .95rem; line-height: 1; cursor: pointer;
    background: transparent; border: none; color: var(--muted);
    width: 18px; height: 18px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
}
.notify-user-chip-remove:hover { background: var(--panel); color: var(--hit); }
.invite-modal-footer {
    padding: 12px 20px 16px; border-top: 1px solid var(--border); flex-shrink: 0;
    font-size: .78rem; color: var(--muted); text-align: center;
}
.invite-modal-footer b { color: var(--good); }
.invite-modal-msg { font-size: 0.8rem; min-height: 1em; }
.invite-modal-msg.error { color: var(--hit); }
.invite-modal-msg.success { color: var(--good); }

/* Icône "Ma squad" (topbar) — même gabarit que .notif-bell-wrap/.notif-bell-btn. */
.squad-wrap { position: relative; }
.squad-btn {
    position: relative; display: flex; align-items: center; justify-content: center;
    background: none; border: none; cursor: pointer;
    padding: 8px; border-radius: 8px; color: var(--muted);
}
.squad-btn:hover { background: var(--bg-soft); color: var(--text); }

/* Icône "Rechercher un joueur" (topbar, voir static/js/user_search.js) —
   même gabarit que .notif-bell-wrap/.notif-bell-btn/.notif-dropdown, avec
   un champ de recherche + une liste façon .invite-modal-item en plus. */
.search-wrap { position: relative; }
.search-btn {
    position: relative; display: flex; align-items: center; justify-content: center;
    background: none; border: none; cursor: pointer;
    padding: 8px; border-radius: 8px; color: var(--muted);
}
.search-btn:hover { background: var(--bg-soft); color: var(--text); }
.search-dropdown {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 120; width: 280px; max-height: 380px;
    background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
    box-shadow: 0 14px 34px rgba(0,0,0,.45); padding: 10px;
    display: flex; flex-direction: column; gap: 8px;
}
.search-dropdown.hidden { display: none; }
.search-input {
    width: 100%; background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
    padding: 9px 12px; border-radius: 999px; font-size: .86rem; font-family: inherit;
}
.search-input:focus { outline: none; border-color: var(--accent); }
.search-results { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; max-height: 280px; }
.search-result-item {
    display: flex; align-items: center; gap: 11px; padding: 8px; border-radius: 10px;
    color: var(--text); text-decoration: none; transition: background .12s;
}
.search-result-item:hover { background: var(--bg-soft); }
.search-result-avatar {
    width: 30px; height: 30px; border-radius: 50%; flex: none;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800; font-size: .82rem; color: #04121f;
}
.search-empty { padding: 14px 4px; text-align: center; font-size: .85rem; color: var(--muted); }

/* Panneau "Ma squad" (voir static/js/notifications.js:openSquadModal) —
   réutilise la coquille .invite-modal-* ci-dessus, ces règles ne couvrent
   que le contenu propre à la squad. Liste groupée En ligne/Hors ligne
   (site_presence.is_online, voir squads.py:squad_summary_for) avec pastille
   de présence par avatar et lignes hors ligne estompées ; actions
   promouvoir/expulser révélées au survol uniquement sur les appareils qui
   le permettent (@media (hover: hover), même garde-fou que
   .room-kick-btn/.room-spectate-btn dans le salon — sinon injoignables au
   toucher). Prototypé dans static/preview.html (4 variantes de structure,
   puis 4 styles de boutons Inviter/Quitter) avant d'atterrir ici. */
.squad-member-list { display: flex; flex-direction: column; gap: 2px; margin-bottom: 10px; }
.squad-group-label { margin: 14px 0 6px; }
.squad-group-label:first-child { margin-top: 0; }
.squad-member-row {
    display: flex; align-items: center; gap: 10px; padding: 7px 4px; border-radius: 8px;
    transition: background .12s, opacity .12s;
}
.squad-member-row:hover { background: var(--bg-soft); }
.squad-member-row.is-offline { opacity: .55; }
.squad-member-avatar {
    position: relative; width: 30px; height: 30px; border-radius: 50%; flex: none;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800; font-size: .8rem; color: #04121f;
}
.squad-presence-dot {
    position: absolute; right: -2px; bottom: -2px; width: 9px; height: 9px;
    border-radius: 50%; border: 2px solid var(--panel);
}
.squad-presence-dot.online { background: var(--good); }
.squad-presence-dot.offline { background: var(--muted); }
.squad-member-name {
    flex: 1; min-width: 0; font-weight: 600; font-size: .87rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.squad-role-badge {
    flex: none; font-size: .68rem; font-weight: 800; color: #04121f;
    background: var(--accent); padding: 3px 9px; border-radius: 999px;
}
.squad-member-actions { flex: none; display: flex; gap: 6px; }
@media (hover: hover) {
    .squad-member-actions { opacity: 0; pointer-events: none; transition: opacity .12s; }
    .squad-member-row:hover .squad-member-actions { opacity: 1; pointer-events: auto; }
}
.squad-action-btn {
    font-family: inherit; font-size: .72rem; font-weight: 600; cursor: pointer;
    background: transparent; border: 1px solid var(--border); color: var(--muted);
    padding: 4px 9px; border-radius: 999px; transition: border-color .15s, color .15s;
}
.squad-action-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.squad-action-btn.squad-action-danger:hover:not(:disabled) { border-color: var(--hit); color: var(--hit); }
.squad-invite-picker { margin: 4px 0 10px; }
.squad-invite-picker.hidden { display: none; }
/* Barre d'actions Inviter/Quitter côte à côte (chef uniquement — un membre
   non-chef n'a que "Quitter", seule en pleine largeur via .squad-leave-btn
   ci-dessous). Couleurs adoucies : au repos le texte reste neutre et le
   contour n'est qu'une teinte à faible opacité ; la couleur pleine
   n'apparaît qu'au survol, pour garder le code couleur sans l'imposer en
   permanence (retour : un contour plein accent/rouge "criait" trop). */
.squad-action-row { display: flex; gap: 8px; margin: 10px 0 6px; }
.squad-action-row .btn { flex: 1; margin: 0; }
/* Espace sous "Quitter la squad" quand il est seul (non-chef) — sans ça il
   touche presque le bord bas du modal (.invite-modal-body ne garde que 4px
   de padding bas). */
.squad-leave-btn { width: 100%; margin-top: 14px; margin-bottom: 6px; }

/* "Créer une partie" + "Lancer avec ma squad" (injecté par
   syncSquadLaunchButton dans notifications.js) partagent la même ligne —
   voir templates/_lobby.html#create-actions. */
.create-actions { display: flex; gap: 8px; }
.create-actions .btn { flex: 1; min-width: 0; }
.squad-launch-btn { white-space: nowrap; }

/* Modal de signalement (voir static/js/reports.js) : même gabarit que la
   modale d'invitation ci-dessus (backdrop plein écran, z-index 200). */
.report-modal-backdrop {
    position: fixed; inset: 0; z-index: 200; padding: 24px;
    background: rgba(4, 8, 16, .78); backdrop-filter: blur(3px);
    display: flex; align-items: center; justify-content: center;
    opacity: 0; pointer-events: none; transition: opacity .15s ease;
}
.report-modal-backdrop.open { opacity: 1; pointer-events: auto; }
.report-modal {
    width: min(440px, 100%); max-height: 82vh; display: flex; flex-direction: column;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: 0 24px 70px rgba(0,0,0,.6); overflow: hidden;
    transform: translateY(10px) scale(.97); transition: transform .18s ease;
}
.report-modal-backdrop.open .report-modal { transform: translateY(0) scale(1); }
.report-modal-head {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 18px 20px 14px; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.report-modal-head h3 { margin: 0; font-size: 1.05rem; }
.report-modal-head p { margin: 2px 0 0; font-size: .78rem; color: var(--muted); }
.report-modal-close {
    background: none; border: none; color: var(--muted); cursor: pointer;
    font-size: 0.95rem; width: 30px; height: 30px; border-radius: 8px; flex-shrink: 0;
}
.report-modal-close:hover { background: var(--bg-soft); color: var(--text); }
.report-modal-body { padding: 14px 20px 4px; display: flex; flex-direction: column; gap: 12px; }
.report-modal-kind-tabs { display: flex; gap: 8px; }
.report-modal-kind-btn {
    flex: 1; font-family: inherit; font-size: .85rem; font-weight: 700; cursor: pointer;
    background: var(--bg-soft); border: 1px solid var(--border); color: var(--muted);
    padding: 9px 10px; border-radius: 10px; transition: border-color .15s, color .15s, background .15s;
}
.report-modal-kind-btn.is-active { color: var(--text); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.report-modal-subtype-tabs { display: flex; gap: 8px; }
.report-modal-subtype-btn { font-size: .8rem; padding: 7px 10px; }
.report-modal-game-select {
    width: 100%; background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
    padding: 9px 12px; border-radius: 10px; font-size: .87rem; font-family: inherit;
}
.report-modal-game-select:focus { outline: none; border-color: var(--accent); }
.report-modal-scope { display: flex; align-items: center; gap: 8px; font-size: .85rem; color: var(--muted); cursor: pointer; }
.report-modal-scope b { color: var(--text); }
.report-modal-textarea {
    width: 100%; min-height: 110px; resize: vertical; background: var(--bg-soft);
    border: 1px solid var(--border); color: var(--text); padding: 10px 12px;
    border-radius: 10px; font-size: .9rem; font-family: inherit;
}
.report-modal-textarea:focus { outline: none; border-color: var(--accent); }
.report-modal-msg { font-size: 0.8rem; min-height: 1em; }
.report-modal-msg.error { color: var(--hit); }
.report-modal-msg.success { color: var(--good); }
.report-modal-footer { padding: 14px 20px 18px; flex-shrink: 0; display: flex; justify-content: flex-end; }

.auth-wrap { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 20px; min-height: 100%; padding: 40px 0; }
/* Page de connexion : centrée au milieu de la FENÊTRE via position:fixed
   plutôt que de dépendre de la chaîne flex/pourcentage des ancêtres (topbar
   masquée à hauteur 0, footer, padding de .container...) — la façon la plus
   fiable de garantir un centrage exact peu importe ce qu'il y a autour.
   Classe séparée (pas juste .auth-wrap) pour ne pas changer le comportement
   des pages "changer de pseudo/mot de passe", qui restent dans le flux
   normal de la page avec la topbar visible. */
.auth-wrap-login { position: fixed; inset: 0; margin: 0; padding: 20px; overflow-y: auto; }
.auth-panel { max-width: 380px; width: 100%; }
.auth-logo { display: block; height: 120px; width: auto; margin: 0 auto 18px; border-radius: 16px; }
.auth-guest-panel { border-style: dashed; }
.auth-guest-toggle {
    display: block;
    margin: 14px auto 0;
    padding: 4px 8px;
    background: none;
    border: none;
    color: var(--muted);
    font-size: 0.82rem;
    text-decoration: underline;
    cursor: pointer;
}
.auth-guest-toggle:hover { color: var(--text); }

/* ---------- Administration ---------- */
svg.ic { stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.admin-layout { display: flex; gap: 28px; align-items: flex-start; }
.admin-nav {
    display: flex; flex-direction: column; gap: 2px; min-width: 220px; flex-shrink: 0;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px;
    position: sticky; top: 20px;
}
.admin-nav-group {
    font-size: 0.68rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted);
    padding: 12px 12px 6px; opacity: .75;
}
.admin-nav a {
    display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 10px;
    color: var(--muted); text-decoration: none; font-size: 0.87rem; font-weight: 500; position: relative;
}
.admin-nav-icon {
    width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-soft); color: var(--muted); font-size: .85rem;
}
.admin-nav-icon .ic { width: 15px; height: 15px; }
.admin-nav-badge {
    margin-left: auto; font-size: 0.68rem; font-weight: 800; padding: 1px 7px; border-radius: 999px; flex-shrink: 0;
}
.admin-nav a:hover { background: var(--bg-soft); color: var(--text); }
.admin-nav a.is-active {
    background: linear-gradient(90deg, color-mix(in srgb, var(--acc, var(--accent)) 16%, transparent), transparent);
    color: var(--text); font-weight: 700;
}
.admin-nav a.is-active::before {
    content: ""; position: absolute; left: -12px; top: 6px; bottom: 6px; width: 3px; border-radius: 3px;
    background: var(--acc, var(--accent));
}
.admin-nav a.is-active .admin-nav-icon { background: var(--acc, var(--accent)); color: #04121f; }
.admin-body { flex: 1; min-width: 0; }
.admin-subtitle { margin-top: 32px; font-size: 1rem; color: var(--muted); }
.admin-dash-head { margin-bottom: 22px; }
.admin-dash-head h1 { margin: 0 0 4px; font-size: 1.5rem; }
.admin-dash-head p { margin: 0; color: var(--muted); font-size: 0.88rem; }
.effects-refresh-form { max-width: 360px; }

.admin-tabs {
    display: flex; flex-wrap: wrap; gap: 6px; margin: 18px 0 20px;
    border-bottom: 1px solid var(--border); padding-bottom: 12px;
}
.admin-tabs a {
    padding: 7px 13px; border-radius: 999px; color: var(--muted); text-decoration: none; font-size: 0.85rem;
    border: 1px solid transparent;
}
.admin-tabs a:hover { color: var(--text); border-color: var(--border); }
.admin-tabs a.is-active { background: var(--bg-soft); color: var(--accent); border-color: var(--border); font-weight: 600; }

.admin-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; }
.admin-card {
    display: flex; flex-direction: column; gap: 4px; min-width: 0;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 18px 20px; text-decoration: none; position: relative; overflow: hidden;
}
.admin-card::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; background: var(--acc, var(--accent)); }
.admin-card:hover { border-color: var(--acc, var(--accent)); }
.admin-card-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.admin-card-icon {
    width: 34px; height: 34px; border-radius: 9px; flex-shrink: 0;
    background: color-mix(in srgb, var(--acc, var(--accent)) 18%, transparent); color: var(--acc, var(--accent));
    display: flex; align-items: center; justify-content: center;
}
.admin-card-icon .ic { width: 17px; height: 17px; }
.admin-card-trend { font-size: 0.72rem; font-weight: 700; color: var(--muted); }
.admin-card-trend--live { color: #22c55e; }
.admin-card-num { font-size: 2rem; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text); line-height: 1; margin-bottom: 2px; }
.admin-card-label { color: var(--muted); font-size: 0.85rem; }

.guest-queue { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.guest-row {
    display: flex; align-items: center; gap: 14px; padding: 12px 16px;
    background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
}
.guest-name { font-weight: 600; }
.guest-time { font-size: 0.8rem; }
.guest-actions { margin-left: auto; display: flex; gap: 8px; }
.guest-actions form { margin: 0; }
.guest-queue--history .guest-row { opacity: 0.8; }
.guest-status { margin-left: auto; font-size: 0.8rem; font-weight: 600; }
.guest-status--approved { color: #22c55e; }
.guest-status--rejected { color: var(--hit); }

.admin-head-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 4px; flex-wrap: wrap; }
.admin-head-row form { margin: 0; }
.table-wrap { overflow-x: auto; }
.admin-table { width: 100%; border-collapse: collapse; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.admin-table th, .admin-table td { padding: 11px 16px; text-align: left; border-bottom: 1px solid var(--border); font-size: 0.9rem; position: relative; }
.admin-table th { color: var(--muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: .4px; }
.admin-table tr:last-child td { border-bottom: none; }
.admin-table tr:hover td { background: var(--bg-soft); }
.admin-table tr:hover td:first-child::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--accent); }
.admin-table form { margin: 0; }
.table-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

.admin-filter-tabs { display: flex; gap: 4px; flex-wrap: wrap; }
.admin-filter-tabs a {
    padding: 6px 12px; border-radius: 999px; font-size: 0.82rem; font-weight: 600;
    color: var(--muted); border: 1px solid var(--border);
}
.admin-filter-tabs a:hover { color: var(--text); }
.admin-filter-tabs a.is-active { color: var(--bg); background: var(--accent); border-color: var(--accent); }

.session-status {
    display: inline-flex; align-items: center; gap: 6px; font-size: 0.78rem; font-weight: 700;
    padding: 3px 10px 3px 8px; border-radius: 999px;
}
.session-status::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.session-status--waiting { color: var(--muted); background: color-mix(in srgb, var(--muted) 14%, transparent); }
.session-status--setup { color: #f59e0b; background: rgba(245, 158, 11, .14); }
.session-status--playing { color: #22c55e; background: rgba(34, 197, 94, .14); }
.session-status--finished { color: var(--muted); background: color-mix(in srgb, var(--muted) 8%, transparent); }

.report-kind { font-size: 0.82rem; font-weight: 600; white-space: nowrap; display: block; }
.report-kind--bug { color: var(--hit); }
.report-kind--suggestion { color: #f59e0b; }
.report-subtype { font-size: 0.76rem; white-space: nowrap; }
/* table-layout: fixed + <colgroup> (voir templates/admin/reports.html) :
   sans ça, la colonne Message (texte non contraint) forçait le tableau à
   dépasser son conteneur et déclenchait le scroll horizontal de .table-wrap
   au lieu de tronquer proprement. */
.admin-table--reports { table-layout: fixed; }
.admin-table--reports .report-action-cell { padding-left: 8px; padding-right: 8px; }
.admin-table--reports .report-action-cell .btn { padding-left: 8px; padding-right: 8px; }
.report-open-btn {
    display: block; width: 100%; text-align: left; cursor: pointer;
    background: none; border: none; color: var(--text); font-family: inherit; font-size: 0.85rem;
    padding: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.report-open-btn:hover { color: var(--accent); text-decoration: underline; }

/* Modale de détail d'un signalement (voir static/js/admin-reports.js) —
   même gabarit que .report-modal-* (modale d'envoi, static/js/reports.js). */
.report-detail-modal .report-modal-head p { text-transform: none; }
.report-detail-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 4px; }
.report-detail-tag {
    font-size: 0.78rem; color: var(--muted); background: var(--bg-soft);
    border: 1px solid var(--border); border-radius: 999px; padding: 4px 12px;
}
.report-detail-message {
    font-size: 0.9rem; white-space: pre-wrap; word-break: break-word; color: var(--text);
    max-height: 50vh; overflow-y: auto; padding-bottom: 4px;
}
.report-status-select {
    background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
    padding: 5px 8px; border-radius: 8px; font-size: 0.8rem; font-family: inherit; font-weight: 600;
}
.report-status-select--new { color: var(--accent); border-color: var(--accent); }
.report-status-select--closed { color: var(--muted); }

textarea {
    width: 100%; background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
    padding: 11px 13px; border-radius: 10px; font-size: 0.85rem; font-family: "Space Grotesk", monospace;
    resize: vertical;
}
textarea:focus { outline: none; border-color: var(--accent); }
.bot-profile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; margin-bottom: 8px; }
.bot-profile-card { padding: 18px; }
.bot-profile-head { margin-bottom: 6px; display: flex; align-items: center; justify-content: space-between; }
.bot-delete-btn {
    background: none; border: 1px solid var(--border); color: var(--muted);
    width: 24px; height: 24px; border-radius: 50%; cursor: pointer; font-size: 0.75rem; line-height: 1;
}
.bot-delete-btn:hover { border-color: var(--hit); color: var(--hit); }
.bot-profile-card-new { border-style: dashed; display: flex; flex-direction: column; justify-content: flex-start; }
.bot-delete-form { display: none; }
.bot-new-hint { font-size: 0.78rem; margin-top: 8px; }

.bot-avatar-row { display: flex; align-items: center; gap: 10px; margin: 8px 0 4px; }
.bot-avatar-preview {
    width: 46px; height: 46px; border-radius: 50%; flex: none; overflow: hidden;
    display: flex; align-items: center; justify-content: center; font-size: 1.3rem;
    background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--border);
}
.bot-avatar-preview img { width: 100%; height: 100%; object-fit: cover; }
.bot-avatar-field { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.bot-avatar-field input[type=file] { font-size: 0.78rem; }
.bot-avatar-field label { font-size: 0.78rem; display: flex; align-items: center; gap: 5px; }

.bot-config-fields { display: flex; flex-direction: column; gap: 2px; }
.bot-config-field label { margin: 12px 0 5px; }
.bot-config-field input[type=number], .bot-config-field select { font-size: 0.88rem; padding: 8px 11px; }

.param-help {
    position: relative;
    display: inline-flex; align-items: center; justify-content: center;
    width: 15px; height: 15px; border-radius: 50%;
    border: 1px solid var(--muted); color: var(--muted);
    font-size: 0.62rem; font-weight: 700; line-height: 1;
    cursor: help; vertical-align: middle;
}
.param-help:hover, .param-help:focus { border-color: var(--accent); color: var(--accent); outline: none; }

.param-help::after {
    content: attr(data-tip);
    position: absolute;
    z-index: 40;
    bottom: calc(100% + 10px);
    left: 50%;
    transform: translate(-50%, 4px);
    width: max-content;
    max-width: 230px;
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: var(--shadow);
    color: var(--text);
    font-size: 0.78rem;
    font-weight: 400;
    line-height: 1.45;
    text-align: left;
    white-space: normal;
    padding: 10px 12px;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .15s ease, transform .15s ease;
}
.param-help::before {
    content: "";
    position: absolute;
    z-index: 40;
    bottom: calc(100% + 4px);
    left: 50%;
    transform: translate(-50%, 4px);
    border: 6px solid transparent;
    border-top-color: var(--panel-2);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .15s ease, transform .15s ease;
}
.param-help:hover::after, .param-help:focus::after,
.param-help:hover::before, .param-help:focus::before {
    opacity: 1;
    visibility: visible;
    transform: translate(-50%, 0);
}
.bot-config-checkbox {
    display: flex; align-items: center; gap: 8px;
    margin: 14px 0 6px; font-size: 0.85rem; color: var(--text); cursor: pointer;
}
.bot-config-checkbox input[type=checkbox] { width: 16px; height: 16px; accent-color: var(--accent-strong); cursor: pointer; }

/* .content-shift relaie le flex:1 de body vers .container (footer collé en
   bas sur les pages courtes) : nécessaire depuis que .container n'est plus
   l'enfant direct de body (voir .content-shift dans base.html / le
   commentaire sur le décalage du chat plus bas). */
.content-shift { flex: 1; display: flex; flex-direction: column; transition: padding-right .3s cubic-bezier(.16, 1, .3, 1); }
.container { width: min(1160px, 92vw); margin: 0 auto; padding: 32px 0 60px; flex: 1; transition: max-width .3s cubic-bezier(.16, 1, .3, 1); }
/* Le Yams a besoin de plus de large que les autres pages : le terrain garde
   sa taille (760px) ET la feuille de score doit pouvoir tenir au moins 4
   joueurs à côté sans scroll ni passage automatique sous le terrain (voir
   maybeAutoStack dans yams.js) — d'où un conteneur élargi rien que pour
   cette page plutôt que d'agrandir .container partout. */
.container:has(#game[data-slug="yams"]) { width: min(1600px, 94vw); }
/* Idem pour le Saboteur : le terrain élargi (+1.5 carte de chaque côté de la
   grille 11 colonnes, voir .sb-board-wrap) laisse trop peu de place pour que
   le bandeau manche/rôle + la liste des joueurs (.sb-side) restent à côté
   sans repasser sous le terrain à chaque chargement sur un écran normal. */
.container:has(#game[data-slug="saboteur"]) { width: min(1320px, 94vw); }

.footer {
    text-align: center;
    color: var(--muted);
    font-size: 0.8rem;
    padding: 24px;
    border-top: 1px solid var(--border);
}

/* ---------- Hero / index ---------- */
.hero { text-align: center; margin: 20px 0 34px; }
.hero h1 { font-size: 2.4rem; margin: 0 0 10px; letter-spacing: -0.5px; }
.hero p { color: var(--muted); font-size: 1.05rem; margin: 0 auto; max-width: 560px; }

.section-title { font-size: 1.1rem; text-transform: uppercase; letter-spacing: 2px; color: var(--muted); margin: 36px 0 16px; }

.join-strip { display: flex; justify-content: center; margin-bottom: 20px; }
.join-toggle {
    display: inline-flex; align-items: center; gap: 8px;
    background: var(--bg-soft); border: 1px solid #244d60; color: #6992a5;
    font-family: inherit; font-size: 0.9rem; font-weight: 600; cursor: pointer;
    padding: 10px 20px; border-radius: 999px;
    transition: background .15s, color .15s, transform .1s;
}
.join-toggle svg { flex: none; }
.join-toggle:hover { background: var(--accent); color: #04121f; }
.join-toggle:active { transform: translateY(1px); }
.join-toggle.hidden { display: none; }
.join-form { display: flex; gap: 8px; align-items: center; }
.join-form.hidden { display: none; }
.join-form input { width: 170px; }
.join-form-submit {
    flex: none; width: 40px; height: 40px; border-radius: 10px;
    background: var(--accent-strong); color: #04121f; border: none; cursor: pointer;
    font-size: 1.1rem; font-weight: 700; line-height: 1;
    display: flex; align-items: center; justify-content: center;
    transition: background .15s, transform .1s;
}
.join-form-submit:hover { background: var(--accent); }
.join-form-submit:active { transform: translateY(1px); }

/* Filtres de la bibliothèque par effectif — voir #game-filters dans
   index.html, purement cosmétique ici (le filtrage lui-même est en JS,
   .game-card.hidden masque une carte sans la retirer du DOM). */
.game-filters { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; margin-bottom: 20px; }
.filter-chip {
    font-family: inherit; font-size: .82rem; font-weight: 600; cursor: pointer;
    background: var(--bg-soft); color: var(--muted); border: 1px solid var(--border);
    padding: 8px 16px; border-radius: 999px; transition: background .15s, color .15s, border-color .15s;
}
.filter-chip:hover { color: var(--text); border-color: var(--accent); }
.filter-chip.is-active { background: var(--accent); color: #04121f; border-color: transparent; }
.game-filters-empty { text-align: center; color: var(--muted); padding: 40px 0; font-size: .92rem; }

.game-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 18px; }
.game-card {
    position: relative;
    display: flex; gap: 16px; align-items: flex-start;
    background: linear-gradient(150deg, var(--panel), var(--bg-soft));
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 20px;
    transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.game-card:hover { transform: translateY(-3px); border-color: var(--accent); box-shadow: var(--shadow); }
/* Banderole en diagonale, plaquée au coin haut-droit — de taille fixe et
   totalement indépendante du texte du titre (contrairement à une pastille
   posée à côté ou au-dessus de lui), elle ne peut donc jamais être poussée
   hors de la tuile par un nom de jeu long, mot unique ou non. Le wrap
   (90x90, overflow hidden) ne fait que découper proprement le coin ; la
   bande elle-même est plus large que ce coin et pivotée à travers. */
.game-card-ribbon-wrap {
    position: absolute; top: 0; right: 0; width: 90px; height: 90px;
    overflow: hidden; pointer-events: none; border-radius: 0 var(--radius) 0 0;
}
.game-card-ribbon {
    position: absolute; top: 15px; right: -30px; width: 120px;
    transform: rotate(45deg);
    text-align: center;
    font-size: .64rem; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
    color: #04121f; background: var(--accent);
    padding: 4px 0;
    box-shadow: 0 2px 8px rgba(0,0,0,.35);
}
/* Marge réservée sous la bande (voir .game-card-ribbon-wrap ci-dessus) :
   sans ça, un titre qui retombe sur une deuxième ligne pleine largeur
   ("Traîtres à bord") peut passer sous la pointe basse de la diagonale. */
.game-card.has-ribbon .game-body { padding-top: 14px; }
.game-illustration {
    width: 76px; height: 76px; flex-shrink: 0; object-fit: contain;
    border-radius: 12px; border: 1px solid var(--border);
    box-shadow: 0 4px 14px rgba(0,0,0,.35);
    background: var(--bg-soft); padding: 4px;
}
.game-body h3 { margin: 0 0 6px; overflow-wrap: anywhere; }
.game-body p { margin: 0 0 12px; color: var(--muted); font-size: 0.9rem; }
.game-meta { font-size: 0.75rem; color: var(--accent); border: 1px solid var(--accent); padding: 3px 8px; border-radius: 999px; }

/* ---------- Panels / forms ---------- */
.panel-duo { display: grid; grid-template-columns: 1.3fr 1fr; gap: 20px; align-items: start; }
/* Lobby : règles (gauche) + création (droite) ; connexion par code (via l'accueil).
   Prototypé dans static/preview.html sur Flip 7 puis généralisé aux 15 jeux
   (voir templates/_lobby.html) : chaque règle porte une pastille-icône, les
   tons bad/gold/frost/good gardent un sens FIXE quel que soit le jeu (danger/
   récompense/mécanique spéciale/positif), le ton par défaut reprend
   `var(--accent)` du jeu via color-mix — jamais de couleur en dur ici, sinon
   chaque nouveau jeu casserait la cohérence. Le filigrane reprend
   l'illustration déjà utilisée par le jeu sur la page d'accueil
   (`game.illustration`, catalog.py) : aucun asset supplémentaire à fournir. */
.panel-rules { position: relative; overflow: hidden; }
.panel-rules-watermark {
    position: absolute; right: -30px; bottom: -30px; width: 190px; height: 190px;
    background-size: cover; background-position: center; opacity: .05;
    transform: rotate(8deg); pointer-events: none;
}
.panel-rules .rules-list { list-style: none; margin: 12px 0 0; padding: 0; position: relative; z-index: 1; }
.panel-rules .rules-list li {
    display: flex; align-items: flex-start; gap: 12px;
    padding: 11px 0; border-bottom: 1px solid rgba(255,255,255,.06);
}
.panel-rules .rules-list li:last-child { border-bottom: none; }
.panel-rules .rules-list li > span:last-child { color: var(--muted); line-height: 1.5; padding-top: 6px; }
.panel-rules .rules-list b { color: var(--text); }
.rule-icon {
    flex: none; width: 36px; height: 36px; border-radius: 10px;
    display: flex; align-items: center; justify-content: center; font-size: 1.05rem;
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
}
.rule-icon-bad { background: rgba(244,63,94,.12); border-color: rgba(244,63,94,.24); }
.rule-icon-gold { background: rgba(246,195,68,.14); border-color: rgba(246,195,68,.26); }
.rule-icon-frost { background: rgba(56,189,248,.12); border-color: rgba(56,189,248,.24); }
.rule-icon-good { background: rgba(52,211,153,.12); border-color: rgba(52,211,153,.24); }
.rules-cards {
    display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; padding-top: 16px;
    border-top: 1px solid rgba(255,255,255,.06); position: relative; z-index: 1;
}
.rules-cards img {
    width: 46px; height: auto; border-radius: 6px; box-shadow: 0 3px 8px rgba(0,0,0,.4);
    background: #fbf7ec;
}
.panel-create { border-left: 3px solid color-mix(in srgb, var(--accent) 40%, transparent); }
.panel-create .create-actions .btn-primary, #join-panel .btn-primary {
    border-radius: 999px; font-weight: 700;
    background: var(--accent); border-color: var(--accent); color: #04121f;
}
.panel-create .create-actions .btn-primary:hover:not(:disabled),
#join-panel .btn-primary:hover:not(:disabled) {
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 20%, transparent);
}
.join-panel { max-width: 440px; margin: 0 auto; }
.join-panel .code-badge, .join-panel b { color: var(--accent); }
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 24px; box-shadow: var(--shadow); }
.panel h2 { margin: 0 0 6px; }
.panel .muted { margin-top: 0; }
.muted { color: var(--muted); }
.center { text-align: center; }

label { display: block; font-size: 0.8rem; color: var(--muted); margin: 14px 0 6px; }
input[type=text], input[type=password], input[type=number], select {
    width: 100%;
    background: var(--bg-soft);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 11px 13px;
    border-radius: 10px;
    font-size: 1rem;
    font-family: inherit;
}
input[type=text]:focus, input[type=password]:focus, input[type=number]:focus, select:focus {
    outline: none; border-color: var(--accent);
}
.code-input, .code-input::placeholder { letter-spacing: 3px; text-transform: uppercase; }

/* ---------- Buttons ---------- */
.btn {
    font-family: inherit; font-size: 0.95rem; font-weight: 600;
    border: 1px solid transparent; border-radius: 10px;
    padding: 11px 18px; cursor: pointer; transition: background .15s, transform .1s, border-color .15s;
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-primary { background: var(--accent-strong); color: #04121f; }
.btn-primary:hover:not(:disabled) { background: var(--accent); }
.btn-ghost { background: transparent; border-color: var(--border); color: var(--text); }
.btn-ghost:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.btn-danger { background: transparent; border-color: var(--hit); color: var(--hit); }
.btn-danger:hover:not(:disabled) { background: var(--hit); color: #1a0508; }
/* Couleurs adoucies (modal "Ma squad", voir static/js/notifications.js)
   au repos le texte reste neutre et le contour n'est qu'une teinte à
   faible opacité ; la couleur pleine n'apparaît qu'au survol. Doivent
   être déclarées après .btn ci-dessus : même spécificité qu'une classe
   simple, donc l'ordre du fichier tranche — plus haut dans le fichier
   (comme avant ce commentaire), le "border: 1px solid transparent" de
   .btn les écrasait silencieusement. */
.squad-btn-accent-soft { background: transparent; border-color: rgba(56,189,248,.35); color: var(--text); }
.squad-btn-accent-soft:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); background: rgba(56,189,248,.08); }
.squad-btn-danger-soft { background: transparent; border-color: rgba(244,63,94,.3); color: var(--muted); }
.squad-btn-danger-soft:hover:not(:disabled) { border-color: var(--hit); color: var(--hit); background: rgba(244,63,94,.08); }
.btn-sm { padding: 6px 12px; font-size: 0.8rem; }
.btn-block { width: 100%; margin-top: 14px; }
.btn-primary.btn-block { margin-top: 18px; }

/* ---------- Game shell ---------- */
.game-head { margin-bottom: 24px; text-align: center; }
.game-head h1 { margin: 0; }
.screen { animation: fade .3s ease; }
.hidden { display: none !important; }
@keyframes fade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

.error-msg { color: var(--hit); min-height: 1.2em; margin-top: 14px; }

/* ---------- Setup ---------- */
/* Le statut adversaire a rejoint .fleet-panel (juste au-dessus de
   #ready-btn) ; il ne reste que le code dans cette barre. */
.setup-bar { display: flex; justify-content: flex-start; align-items: center; margin-bottom: 18px; flex-wrap: wrap; gap: 10px 28px; }
/* .setup-bar et .setup-grid ont chacune leur propre boîte pleine largeur
   (comportement par défaut d'un bloc) à l'intérieur de .container (jusqu'à
   1160px) ; sans centrage explicite, tout le bloc plateau+flotte (748px)
   colle au bord gauche au lieu d'être centré dans la fenêtre. Même largeur
   max sur les deux pour qu'elles restent alignées l'une sous l'autre. */
.setup-bar, .setup-grid { max-width: 748px; margin-left: auto; margin-right: auto; }
.code-badge {
    position: relative;
    font-weight: 700; letter-spacing: 3px; font-size: 1.1rem; color: var(--accent);
    background: var(--panel); border: 1px solid var(--border); padding: 4px 12px; border-radius: 8px; margin: 0 8px;
}
.code-badge.code-badge-copyable { cursor: pointer; user-select: none; }
.code-badge.code-badge-copyable:hover { border-color: var(--accent); }
.mini-toast {
    position: absolute;
    z-index: 20;
    bottom: calc(100% + 9px);
    left: 50%;
    transform: translate(-50%, 4px);
    background: var(--panel-2);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: var(--shadow);
    color: var(--good);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0;
    white-space: nowrap;
    padding: 5px 10px;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .15s ease, transform .15s ease;
}
.mini-toast::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: var(--panel-2);
}
.mini-toast.show { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
.mini-toast.error { color: var(--hit); }
/* La 1re colonne était "auto" : elle se dimensionnait sur le contenu
   max-content du <p> d'instructions (une seule ligne non coupée, bien plus
   large que les 440px du plateau) plutôt que sur le plateau lui-même, d'où
   l'énorme vide avant le panneau de flotte — un max-width sur l'item seul
   ne suffisait pas à corriger le calcul de la piste "auto" elle-même. Piste
   explicitement bornée à la largeur du plateau (minmax(0, 440px)) : plus
   aucune ambiguïté, le texte redevient libre de passer à la ligne dedans. */
.setup-grid { display: grid; grid-template-columns: minmax(0, 440px) 280px; gap: 28px; align-items: stretch; }
.setup-board-col { min-width: 0; }
/* align-items: stretch (ci-dessus) fait que .fleet-panel occupe toute la
   hauteur de la ligne (celle du plateau, plus haute avec son titre+texte) ;
   flex-direction: column + margin-top: auto sur #setup-opponent (premier des
   3 éléments de fin : statut adversaire, bouton, texte d'état) pousse ce
   trio jusqu'en bas du panneau, pour que ce bas s'aligne avec le bas du
   plateau au lieu de flotter juste sous la liste des navires. */
.fleet-panel {
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px;
    display: flex; flex-direction: column;
}
#setup-opponent { margin-top: auto; margin-bottom: 10px; }
.orient-toggle { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; font-size: 0.9rem; }
.fleet-list { list-style: none; padding: 0; margin: 0 0 16px; }
.fleet-list li {
    display: flex; align-items: center; justify-content: space-between;
    padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border);
    margin-bottom: 8px; cursor: pointer; transition: border-color .15s, background .15s;
}
.fleet-list li:hover { border-color: var(--accent); }
.fleet-list li.selected { border-color: var(--accent); background: rgba(56, 189, 248, 0.12); }
.fleet-list li.placed { opacity: 0.55; cursor: default; }
.fleet-list li.placed:hover { border-color: var(--border); }
.ship-dots { display: inline-flex; gap: 3px; }
.ship-dots span { width: 11px; height: 11px; border-radius: 3px; background: var(--ship); }
.fleet-list li.placed .ship-dots span { background: var(--accent); }
.setup-actions { display: flex; gap: 10px; }
.setup-actions .btn { flex: 1; }

/* ---------- Boards ---------- */
.boards { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-top: 10px; }
.board-wrap h3 { margin: 0 0 10px; display: flex; align-items: center; gap: 8px; }
.spectate-peek-btn {
    background: none; border: 1px solid var(--border); color: var(--muted);
    cursor: pointer; width: 26px; height: 26px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
    transition: border-color .12s, color .12s;
}
.spectate-peek-btn:hover { border-color: var(--accent); color: var(--accent); }
.board {
    display: grid;
    grid-template-columns: repeat(10, 1fr);
    grid-template-rows: repeat(10, 1fr);
    gap: 3px;
    background: var(--bg-soft);
    padding: 8px;
    border-radius: 12px;
    border: 1px solid var(--border);
    width: min(440px, 90vw);
    aspect-ratio: 1;
}
.cell {
    position: relative;
    overflow: hidden;
    min-width: 0;
    min-height: 0;
    background: var(--panel-2);
    border-radius: 4px;
    display: flex; align-items: center; justify-content: center;
    font-size: clamp(0.6rem, 2.2vw, 1rem);
    transition: background .12s;
    user-select: none;
}
.board--enemy .cell { cursor: crosshair; }
.board--enemy.locked .cell { cursor: default; }
.board--enemy .cell:hover:not(.shot) { background: #26406b; }
.cell.ship { background: var(--ship); }
.cell.preview { background: rgba(56, 189, 248, 0.4); }
.cell.preview-bad { background: rgba(244, 63, 94, 0.4); }

/* --- Tir dans l'eau : dégradé aqueux + onde qui se propage une fois --- */
.cell.miss {
    background: radial-gradient(circle at 50% 42%, #2a4c78 0%, #183253 55%, #0e2039 100%);
}
.cell.miss::after {
    content: "";
    position: absolute;
    width: 55%;
    height: 55%;
    border: 2px solid rgba(150, 199, 255, 0.7);
    border-radius: 50%;
    animation: ripple 0.7s ease-out;
}
@keyframes ripple {
    0%   { transform: scale(0.2); opacity: 0.9; }
    100% { transform: scale(1.4); opacity: 0; }
}

/* --- Tir qui touche : boule de feu + explosion qui éclate une fois --- */
.cell.hit {
    background: radial-gradient(circle at 50% 45%, #fef08a 0%, #fb923c 32%, #ef4444 62%, #7f1d1d 100%);
    color: #fff;
    box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.35);
}
.cell.hit::after {
    content: "💥";
    line-height: 1;
    animation: boom 0.45s cubic-bezier(0.2, 1.4, 0.4, 1);
}
.cell.hit.sunk {
    background: radial-gradient(circle at 50% 45%, #7f1d1d 0%, #4c0f14 70%, #2a0a0d 100%);
}
.cell.hit.sunk::after { content: "🔥"; }
@keyframes boom {
    0%   { transform: scale(0) rotate(-25deg); opacity: 0; }
    55%  { transform: scale(1.45); opacity: 1; }
    100% { transform: scale(1); opacity: 1; }
}

/* ---------- Play ---------- */
.turn-banner {
    text-align: center; font-weight: 600; font-size: 1.05rem;
    padding: 12px; border-radius: 12px; margin-bottom: 18px; border: 1px solid var(--border);
    background: var(--panel);
}
.turn-banner.your-turn { border-color: var(--good); color: var(--good); background: rgba(52, 211, 153, 0.08); }
.turn-banner.wait-turn { color: var(--muted); }
.play-log { text-align: center; color: var(--muted); min-height: 1.4em; margin-top: 18px; }

/* ---------- Bataille navale : pouvoirs (Sonar / Déplacement / Repositionnement) ---------- */
.powers-panel { margin-bottom: 18px; }
.powers-buttons { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }
.power-card {
    position: relative;
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    width: 88px; padding: 14px 8px 10px;
    border-radius: 18px; cursor: pointer;
    background: linear-gradient(160deg, var(--panel-2), var(--bg-soft));
    border: 1px solid var(--border); color: var(--text);
    font: inherit;
    transition: transform .15s ease, border-color .15s, box-shadow .15s, opacity .15s;
}
.power-card:hover:not(:disabled) {
    transform: translateY(-3px);
    border-color: var(--power-c, var(--accent));
    box-shadow: 0 8px 18px rgba(0, 0, 0, .3);
}
.power-card:active:not(:disabled) { transform: translateY(-1px); }
.power-card:disabled { opacity: .4; cursor: default; }
.power-card-icon {
    width: 42px; height: 42px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.3rem; line-height: 1;
    background: color-mix(in srgb, var(--power-c, var(--accent)) 22%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--power-c, var(--accent)) 45%, transparent);
}
.power-card-label { font-size: .76rem; font-weight: 700; letter-spacing: .02em; }
.power-card-count {
    position: absolute; top: -7px; right: -7px;
    min-width: 21px; height: 21px; padding: 0 5px;
    border-radius: 999px;
    background: var(--power-c, var(--accent)); color: #0a1220;
    font-size: .74rem; font-weight: 800;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .4), 0 0 0 2px var(--panel);
}
.power-card.is-active {
    border-color: var(--power-c, var(--accent));
    background: linear-gradient(160deg, color-mix(in srgb, var(--power-c, var(--accent)) 25%, var(--panel-2)), var(--bg-soft));
    box-shadow: 0 0 0 1px var(--power-c, var(--accent)), 0 6px 16px color-mix(in srgb, var(--power-c, var(--accent)) 40%, transparent);
}
.power-card-sonar { --power-c: var(--accent); }
.power-card-move { --power-c: var(--amber); }
.power-card-reposition { --power-c: var(--violet); }
.power-card-decoy { --power-c: #2dd4bf; }
.power-card-recon { --power-c: #60a5fa; }
.powers-ship-picker {
    margin: 12px auto 0; max-width: 420px; text-align: center;
    padding: 14px; border-radius: 12px; border: 1px solid var(--border); background: var(--panel);
}
.powers-ship-picker > p { margin: 0 0 10px; }
.powers-cancel-btn { display: block; margin: 0 auto; }
/* Mode de ciblage Sonar actif : indicateur visuel distinct du curseur de tir normal.
   allowShot (voir drawBoard) rend aussi les cases déjà tirées cliquables en
   mode Sonar, donc le survol doit s'appliquer à TOUTES les cases ici, pas
   seulement :not(.shot) comme le tir normal. */
.board--enemy.sonar-mode { box-shadow: 0 0 0 2px var(--accent); }
.board--enemy.sonar-mode .cell { cursor: help; }
.board--enemy.sonar-mode .cell:hover { background: color-mix(in srgb, var(--accent) 30%, #26406b); }
/* Même principe pour la Reconnaissance aérienne, en bleu ciel pour la
   distinguer visuellement du Sonar (cyan). */
.board--enemy.recon-mode { box-shadow: 0 0 0 2px #60a5fa; }
.board--enemy.recon-mode .cell { cursor: crosshair; }

/* Marqueur permanent laissé sur la grille ennemie par un scan Sonar passé
   (garde l'info visible tant que la partie dure, voir your_sonar_scans). */
.cell.sonar-scanned::before {
    content: "";
    position: absolute; inset: 4px;
    border-radius: 50%;
    border: 2px solid rgba(148, 163, 184, .6);
    pointer-events: none;
}
.cell.sonar-detected::before { border-color: #34d399; box-shadow: 0 0 6px rgba(52, 211, 153, .55); }

/* Aperçu de la ligne/colonne survolée avant de confirmer une Reconnaissance
   aérienne (voir highlightReconLine, délégué sur #enemy-board). */
.cell.recon-line-hover { background: color-mix(in srgb, #60a5fa 30%, #26406b); }
/* Teinte permanente sur toute la ligne/colonne déjà survolée (garde l'info,
   même principe que .sonar-scanned mais étalé sur 10 cases plutôt qu'une
   zone locale — voir your_recon_scans). */
.cell.recon-scanned { box-shadow: inset 0 0 0 999px rgba(148, 163, 184, .12); }
.cell.recon-detected { box-shadow: inset 0 0 0 999px rgba(52, 211, 153, .16); }

/* Leurre : pose sur SA PROPRE grille (jamais l'ennemie), indicateur de mode
   + marqueur du leurre lui-même (distinct d'un vrai navire — bouée, pas un
   bloc plein). */
.board--own.decoy-mode { box-shadow: 0 0 0 2px #2dd4bf; cursor: crosshair; }
.board--own.decoy-mode .cell:hover { background: color-mix(in srgb, #2dd4bf 30%, var(--panel-2)); }
.cell.decoy {
    box-shadow: inset 0 0 0 2px #2dd4bf;
}
/* ::before (pas ::after) pour ne jamais entrer en conflit avec le contenu
   ::after de .cell.hit/.cell.miss (💥/ondulation) si ce leurre est visé plus
   tard — les deux se superposent proprement au lieu de s'écraser. */
.cell.decoy::before {
    content: "🪤";
    position: absolute; inset: 0; z-index: 1;
    display: flex; align-items: center; justify-content: center;
    font-size: .75em;
}

/* Onde ponctuelle au moment où le Sonar se déclenche (voir playSonarPing) —
   clipée par overflow:hidden de .cell, donc un pulse contenu plutôt qu'une
   vraie zone 5×5, pour rester lisible sans chevaucher les cases voisines. */
.sonar-ping {
    position: absolute; inset: 0; border-radius: 4px; pointer-events: none;
    border: 3px solid var(--accent);
    animation: sonar-ping-anim .9s ease-out forwards;
}
.sonar-ping-hit { border-color: #34d399; }
.sonar-ping-miss { border-color: #94a3b8; }
@keyframes sonar-ping-anim {
    0%   { transform: scale(.2); opacity: 1; }
    100% { transform: scale(2.4); opacity: 0; }
}

/* Sélection d'un navire au clic sur "Votre flotte" (Déplacement / Repositionnement). */
.board--own.pick-mode .cell { cursor: default; }
.board--own.pick-mode .cell.ship-pickable { cursor: pointer; }
.cell.ship-picked {
    outline: 3px solid var(--accent); outline-offset: -3px;
    box-shadow: 0 0 10px color-mix(in srgb, var(--accent) 60%, transparent);
}
/* Survol groupé (JS, voir drawBoard) : TOUTES les cases du navire visé
   sur-brillent ensemble, pas seulement la case sous le curseur — sinon
   impossible de distinguer 2 navires collés bord à bord au survol. */
.cell.ship-hover { background: color-mix(in srgb, var(--accent) 35%, var(--ship)); }

/* Aperçu de la position finale avant de confirmer, au survol d'une flèche
   de Déplacement ou d'une icône de rotation de Repositionnement — seuls les
   sens valides ont une icône (voir renderMoveArrows/renderRepositionIcons),
   donc pas besoin d'un état "invalide" ici. */
.cell.move-ghost, .cell.reposition-ghost {
    background: color-mix(in srgb, var(--accent) 38%, transparent) !important;
    outline: 2px dashed var(--accent); outline-offset: -2px;
}

/* Flèches de déplacement posées directement sur la case d'arrivée (voir
   renderMoveArrows) — un cran "proche" (1 case) et un cran "loin" (2 cases)
   par direction, distincts et non superposés. */
.move-arrow {
    position: absolute; inset: 0; margin: auto;
    width: 60%; height: 60%; min-width: 20px; min-height: 20px;
    border-radius: 50%; border: none; cursor: pointer; padding: 0;
    background: var(--accent); color: #fff; font-size: .85rem; font-weight: 700; line-height: 1;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .35);
    transition: transform .12s;
    z-index: 2;
}
.move-arrow:hover { transform: scale(1.18); }
.move-arrow-far { opacity: .75; }

/* Icônes de rotation (Repositionnement) — même traitement que .move-arrow,
   posées sur la case la plus éloignée de l'ancre pour chaque sens (voir
   renderRepositionIcons). */
.rotate-arrow {
    position: absolute; inset: 0; margin: auto;
    width: 60%; height: 60%; min-width: 20px; min-height: 20px;
    border-radius: 50%; border: none; cursor: pointer; padding: 0;
    background: var(--accent); color: #fff; font-size: 1rem; font-weight: 700; line-height: 1;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .35);
    transition: transform .12s;
    z-index: 2;
}
.rotate-arrow:hover { transform: scale(1.18) rotate(15deg); }

.end-banner {
    text-align: center; font-size: 1.6rem; font-weight: 700; padding: 22px;
    border-radius: 14px; margin-bottom: 20px; border: 1px solid var(--border); background: var(--panel);
}
.end-banner.win { color: var(--good); border-color: var(--good); }
.end-banner.lose { color: var(--hit); border-color: var(--hit); }

/* Fin de partie : fenêtre par-dessus le plateau plutôt qu'un écran séparé,
   pour que la position finale reste visible en arrière-plan. */
#screen-end.screen {
    position: fixed; inset: 0; z-index: 150;
    display: flex; align-items: flex-start; justify-content: center;
    padding: 40px 20px; overflow-y: auto;
    background: rgba(6, 10, 20, .72); backdrop-filter: blur(3px);
}
#screen-end.screen.hidden { display: none !important; }
#screen-end > .end-panel {
    position: relative;
    max-width: 480px; width: 100%; margin: auto;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 28px; box-shadow: 0 20px 60px rgba(0,0,0,.55);
    animation: mp-pop .25s ease-out;
}
#screen-end .end-panel .end-banner,
#screen-end .end-panel .ranking,
#screen-end .end-panel .scorecard-wrap { margin-left: 0; margin-right: 0; }
/* Variante plus large : jeux dont le contenu de fin dépasse une simple liste
   (ex. Bataille navale : deux plateaux 10×10 côte à côte ; Yams : une feuille
   de score qui s'élargit avec le nombre de joueurs). */
#screen-end.end-wide > .end-panel { max-width: min(1000px, 92vw); }

.end-panel-close {
    position: absolute; top: 10px; right: 10px; z-index: 1;
    width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border);
    background: var(--bg-soft); color: var(--muted); font-size: 1.1rem; line-height: 1;
    cursor: pointer; display: flex; align-items: center; justify-content: center;
    transition: background .15s, color .15s;
}
.end-panel-close:hover { background: var(--hit); color: #fff; border-color: var(--hit); }

/* Pause entre les manches de 6 qui prend (résultats avant la manche
   suivante) : même habillage que #screen-end, sous un id distinct puisque
   ce n'est pas une fin de partie (le plateau reste vivant derrière). */
#sp-round-end {
    position: fixed; inset: 0; z-index: 150;
    display: flex; align-items: flex-start; justify-content: center;
    padding: 40px 20px; overflow-y: auto;
    background: rgba(6, 10, 20, .72); backdrop-filter: blur(3px);
}
#sp-round-end.hidden { display: none !important; }
#sp-round-end > .end-panel {
    position: relative;
    max-width: 480px; width: 100%; margin: auto;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 28px; box-shadow: 0 20px 60px rgba(0,0,0,.55);
    animation: mp-pop .25s ease-out;
}

/* ---------- Salon (salle d'attente, tous les jeux multi-joueurs) ----------
   Couleur d'accent fixe (bleu, comme static/preview.html), plutôt que la
   couleur de marque du jeu ({{ game.accent }} sur #game) : le salon est un
   écran générique commun à tous les jeux, indépendant du thème du jeu qui
   va suivre.
   Finition "Anneau dégradé" (static/preview2.html, validée par l'utilisateur
   après plusieurs itérations) : cadre en dégradé cyan -> indigo (astuce
   double background-image, pas de pseudo-élément consommé) + 2 anneaux
   orbitaux en pointillés qui tournent lentement en fond (::before/::after).
   Appliquée ici une seule fois : bénéficie automatiquement aux 14 jeux à
   salon, aucune modification par jeu nécessaire. */
.room-panel {
    max-width: 460px; margin: 0 auto; --accent: #38bdf8; --accent-2: #818cf8;
    position: relative; overflow: hidden; border: 1px solid transparent;
    background-image:
        linear-gradient(var(--panel), var(--panel)),
        linear-gradient(135deg, color-mix(in srgb, var(--accent) 55%, transparent), color-mix(in srgb, var(--accent-2) 55%, transparent), transparent 60%);
    background-origin: border-box; background-clip: padding-box, border-box;
}
.room-panel::before, .room-panel::after {
    content: ""; position: absolute; border: 1px dashed; border-radius: 50%; pointer-events: none; z-index: -1;
}
.room-panel::before { width: 340px; height: 340px; top: -130px; right: -150px; border-color: color-mix(in srgb, var(--accent) 22%, transparent); animation: room-orbit-spin 46s linear infinite; }
.room-panel::after { width: 220px; height: 220px; bottom: -100px; left: -100px; border-color: color-mix(in srgb, var(--accent-2) 20%, transparent); animation: room-orbit-spin 32s linear infinite reverse; }
@keyframes room-orbit-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .room-panel::before, .room-panel::after { animation: none; } }

.room-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.room-panel-head h2 { margin: 0; }
.room-count-pill {
    flex: none;
    font-size: .72rem; font-weight: 700; color: var(--muted);
    background: var(--bg-soft); border: 1px solid var(--border);
    padding: 3px 9px; border-radius: 999px; white-space: nowrap;
}

.room-code {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    background: var(--bg-soft); border: 1px solid var(--border); border-radius: 12px;
    padding: 12px 16px; margin: 10px 0 18px;
}
.room-code-info { display: flex; flex-direction: column; }
.room-code-label { font-size: .74rem; color: var(--muted); margin: 0 0 6px; line-height: 1.2; }
/* Le code lui-même en texte dégradé plutôt qu'en pastille imbriquée dans
   .room-code (qui a déjà son propre cadre) — .code-badge reste inchangé
   partout ailleurs (modales d'invitation, en-têtes...). */
.room-code .code-badge {
    background: none; border: none; padding: 0; margin: 0;
    background-image: linear-gradient(90deg, var(--accent), var(--accent-2));
    -webkit-background-clip: text; background-clip: text; color: transparent;
}
.room-invite-btn {
    flex-shrink: 0;
    font-family: inherit; font-size: .82rem; font-weight: 600; cursor: pointer;
    background: transparent; border: 1px solid var(--border); color: var(--muted);
    padding: 7px 14px 7px 11px; border-radius: 999px; display: inline-flex; align-items: center; gap: 6px;
    transition: border-color .15s, color .15s;
}
.room-invite-btn:hover { border-color: var(--accent); color: var(--accent); }
.room-invite-btn:active { transform: translateY(1px); }
.room-invite-btn svg { flex: none; width: 15px; height: 15px; }

.room-players { list-style: none; padding: 0; margin: 0 0 8px; display: flex; flex-direction: column; gap: 4px; }
.room-players li {
    display: flex; align-items: center; gap: 13px;
    padding: 8px 10px; border-radius: 11px; font-weight: 500;
    transition: background .12s;
}
.room-players li:hover { background: var(--bg-soft); }
.room-players li.is-you { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.rp-avatar {
    width: 62px; height: 62px; border-radius: 50%; flex: none;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800; font-size: 1.4rem; color: #04121f;
    box-shadow: inset 0 0 0 2px rgba(255,255,255,.12);
}
.rp-avatar.is-bot { color: var(--text); background: var(--panel-2) !important; box-shadow: inset 0 0 0 1px var(--border); font-size: 1.5rem; }
/* Photo de profil (badge choisi, voir /profil et static/js/room-ui.js:applyAvatar)
   à la place de la bulle initiale+couleur — pas d'encadré, juste un halo
   teinté par palier (--tc, posé en JS), même traitement que /profil. */
.rp-avatar.has-badge { box-shadow: none; }
.rp-avatar.has-badge img { width: 100%; height: 100%; object-fit: contain; padding: 4px; filter: drop-shadow(0 0 6px color-mix(in srgb, var(--tc) 65%, transparent)); }
.rp-avatar.is-zoomable { cursor: zoom-in; transition: transform .15s ease; }
.rp-avatar.is-zoomable:hover { transform: scale(1.06); }
/* Photo de profil d'un BOT (uploadée par un superadmin, voir /admin/bots et
   applyAvatar dans room-ui.js) — une vraie photo, pas un badge à teinte de
   palier : on la fait juste remplir le cercle, sans halo ni padding. */
.rp-avatar.has-bot-avatar { box-shadow: inset 0 0 0 1px var(--border); background: var(--panel-2) !important; }
.rp-avatar.has-bot-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
.rp-main { flex: 1; min-width: 0; }
.rp-name-row { display: flex; align-items: center; font-size: .93rem; }
.rp-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rp-tags { display: flex; gap: 5px; margin-top: 3px; flex-wrap: wrap; }
.rp-tag {
    font-size: .64rem; font-weight: 700; padding: 1.5px 7px; border-radius: 999px;
    text-transform: uppercase; letter-spacing: .03em; line-height: 1.5;
}
.rp-tag.host { color: #f59e0b; border: 1px solid rgba(245,158,11,.5); background: rgba(245,158,11,.1); }
.rp-tag.you { color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 50%, transparent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.rp-tag.bot { color: var(--muted); border: 1px solid var(--border); background: var(--panel); }
.rp-tag.spectator { color: #a78bfa; border: 1px solid rgba(167,139,250,.5); background: rgba(167,139,250,.1); }
.room-kick-btn, .room-spectate-btn, .room-give-host-btn {
    flex-shrink: 0; background: none; border: 1px solid var(--border); color: var(--muted);
    width: 26px; height: 26px; border-radius: 50%; cursor: pointer; font-size: 0.72rem; line-height: 1;
    display: flex; align-items: center; justify-content: center;
    position: relative; transition: opacity .12s, border-color .12s, color .12s;
}
.room-kick-btn:hover { border-color: var(--hit); color: var(--hit); }
.room-spectate-btn:hover { border-color: var(--accent); color: var(--accent); }
.room-give-host-btn:hover { border-color: var(--amber); color: var(--amber); }
@media (hover: hover) {
    .room-kick-btn, .room-spectate-btn, .room-give-host-btn { opacity: 0; }
    .room-players li:hover .room-kick-btn,
    .room-players li:hover .room-spectate-btn,
    .room-players li:hover .room-give-host-btn { opacity: 1; }
}

/* ---------- Tirage au sort du premier joueur (partagé, tous les jeux) ----------
   Voir GS_ROOM.runStarterDraw (room-ui.js) — le tirage lui-même se fait
   côté serveur (chaque `start()` de moteur de jeu), ceci n'est que la mise
   en scène. Prototypé/validé dans static/preview.html avant généralisation. */
.gs-draw-overlay {
    position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center;
    background: rgba(4,8,16,.8); backdrop-filter: blur(3px);
}
.gs-draw-box { text-align: center; padding: 0 24px; }
.gs-draw-label {
    font-size: .8rem; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; font-weight: 700;
    margin: 0 0 18px;
}
.gs-draw-seats { display: flex; flex-wrap: wrap; gap: 20px; justify-content: center; padding: 4px 0; max-width: 640px; }
.gs-draw-seat { display: flex; flex-direction: column; align-items: center; gap: 9px; }
.gs-draw-seat-avatar {
    width: 52px; height: 52px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800; font-size: 1rem; color: #04121f;
    background: var(--panel-2); /* repli pour un bot (voir applyAvatar dans room-ui.js, qui ne pose pas de couleur pour 🤖) */
    box-shadow: inset 0 0 0 2px rgba(255,255,255,.12);
    transition: transform .12s ease, box-shadow .12s ease;
}
.gs-draw-seat-avatar.has-badge { box-shadow: none; background: transparent; }
.gs-draw-seat-avatar.has-badge img { width: 100%; height: 100%; object-fit: contain; padding: 3px; filter: drop-shadow(0 0 5px color-mix(in srgb, var(--tc) 65%, transparent)); }
.gs-draw-seat-avatar.has-bot-avatar { background: var(--panel-2); }
.gs-draw-seat-avatar.has-bot-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
.gs-draw-seat-name { font-size: .76rem; color: var(--muted); font-weight: 600; transition: color .12s ease; }
.gs-draw-seat.is-active .gs-draw-seat-avatar {
    transform: scale(1.2);
    box-shadow: 0 0 0 4px rgba(56,189,248,.55), 0 0 22px rgba(56,189,248,.55);
}
.gs-draw-seat.is-winner .gs-draw-seat-avatar {
    transform: scale(1.3);
    box-shadow: 0 0 0 4px rgba(250,204,21,.75), 0 0 32px rgba(250,204,21,.65);
}
.gs-draw-seat.is-winner .gs-draw-seat-name { color: #facc15; font-weight: 800; }
.gs-draw-name {
    font-size: clamp(1.6rem, 6vw, 2.8rem); font-weight: 900; margin-top: 18px; color: var(--text);
    text-shadow: 0 4px 24px rgba(0,0,0,.6);
}
.gs-draw-name.landed { color: #facc15; animation: gs-draw-land .5s cubic-bezier(.2,.8,.3,1.4); }
@keyframes gs-draw-land {
    0% { transform: scale(.7); opacity: .4; }
    60% { transform: scale(1.15); }
    100% { transform: scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .gs-draw-name.landed { animation: none; } }

#room-host { border-top: 1px solid var(--border); margin-top: 10px; padding-top: 16px; }
/* Le nom d'un réglage doit se lire comme un titre, pas se fondre avec sa
   propre description — même correction que .set-field-label, étendue ici
   aux jeux qui gardent .mode-toggle (sous-titre par option) au lieu de
   .set-seg. `label` de base (style.css plus haut) est gris/discret partout
   ailleurs sur le site (formulaires génériques) : override scopé, pas de
   changement hors salon. `.sb-check` a déjà son propre style, exclu ici. */
#room-host label:not(.sb-check) { font-weight: 700; color: var(--text); font-size: .84rem; }
#room-host label:not(.sb-check) .muted { font-weight: 500; color: var(--muted); font-size: .78rem; }
.room-settings-label {
    font-size: .74rem; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; font-weight: 700;
    margin: 0 0 12px;
}

/* ---------- Réglages du salon vus par les non-hôtes (lecture seule) ----------
   Rangées façon "carte" (fond léger + valeur en pastille colorée) plutôt que
   du texte brut — et un petit point qui pulse à côté du titre pour signaler
   que ces valeurs suivent l'hôte en direct, pas juste au lancement. */
#room-settings-view { border-top: 1px solid var(--border); margin-top: 10px; padding-top: 16px; }
#room-settings-view .room-settings-label { display: flex; align-items: center; gap: 7px; margin-bottom: 10px; }
#room-settings-view .room-settings-label svg { width: 14px; height: 14px; flex: none; color: var(--accent); }
.settings-live-dot {
    width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex: none;
    animation: settings-pulse 1.8s ease-in-out infinite;
}
@keyframes settings-pulse {
    0%, 100% { opacity: .35; transform: scale(.85); }
    50% { opacity: 1; transform: scale(1.15); }
}
.settings-summary { display: flex; flex-direction: column; gap: 7px; }
.settings-row {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 8px 12px; border-radius: 10px;
    background: var(--bg-soft); border: 1px solid var(--border);
}
.settings-row-label { font-size: .82rem; color: var(--muted); }
.settings-value {
    font-weight: 700; font-size: .8rem; line-height: 1; white-space: nowrap;
    padding: 4px 10px; border-radius: 999px;
    background: color-mix(in srgb, var(--accent) 15%, transparent);
    color: var(--accent);
}

/* ---------- Réglages du salon vus par l'hôte (formulaire) ----------
   Prototypé dans static/preview.html/preview2.html ("Anneau dégradé"),
   validé après plusieurs itérations : ligne titre en gras + contrôle
   arrondi, un composant par type de réglage réel (nombre, booléen à 2
   choix, choix multiple avec description) plutôt qu'un textarea/case à
   cocher brute. Un booléen EST un choix à 2 options : même composant
   `.set-seg` que pour un choix à 3+ options, pas un interrupteur à part
   (retour utilisateur : la case à cocher "ne faisait pas dingue"). */
.set-field { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.set-field-label { font-size: .84rem; font-weight: 700; color: var(--text); flex: none; min-width: 118px; white-space: nowrap; }
.set-field-hint { font-size: .72rem; color: var(--muted); margin: 0 0 12px; line-height: 1.4; }

.set-num-wrap { flex: 1; position: relative; }
.set-num-input {
    width: 100%; background: var(--bg-soft); border: 1px solid var(--border); border-radius: 999px;
    padding: 9px 42px 9px 16px; color: var(--text); font: inherit; font-weight: 700; font-size: .9rem;
}
.set-num-input:focus { outline: none; border-color: var(--accent); }
.set-num-suffix { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: .74rem; pointer-events: none; }

.set-seg { flex: 1; display: flex; flex-wrap: wrap; gap: 2px; background: var(--bg-soft); border: 1px solid var(--border); border-radius: 999px; padding: 3px; margin: 4px 0 12px; }
.set-field .set-seg { margin: 0; }
.set-seg .seg-btn {
    flex: 1; text-align: center; padding: 7px 4px; border-radius: 999px; border: none; background: transparent;
    color: var(--muted); font: inherit; font-size: .78rem; font-weight: 700; cursor: pointer; transition: background .15s, color .15s;
}
.set-seg .seg-btn.is-active { background: linear-gradient(90deg, var(--accent), var(--accent-2)); color: #04121f; }

.set-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 10px 0 4px; }
@media (max-width: 480px) { .set-cards { grid-template-columns: 1fr; } }
.set-card {
    text-align: left; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border);
    background: var(--bg-soft); cursor: pointer; font: inherit; color: inherit;
}
.set-card.is-active {
    border-color: transparent;
    background: linear-gradient(120deg, color-mix(in srgb, var(--accent) 16%, var(--bg-soft)), color-mix(in srgb, var(--accent-2) 12%, var(--bg-soft)));
    box-shadow: 0 0 0 1px var(--accent);
}
.set-card-title { font-size: .78rem; font-weight: 700; color: var(--text); display: flex; align-items: center; gap: 7px; }
.set-card-title::before { content: ""; width: 13px; height: 13px; border-radius: 50%; flex: none; border: 1px solid var(--border); background: var(--panel); }
.set-card.is-active .set-card-title::before { background: linear-gradient(90deg, var(--accent), var(--accent-2)); border-color: transparent; }
.set-card-desc { font-size: .68rem; color: var(--muted); margin: 3px 0 0; line-height: 1.35; }

.set-start {
    width: 100%; padding: 13px; border-radius: 12px; border: none; cursor: pointer;
    background: linear-gradient(120deg, var(--accent), var(--accent-2)); color: #04121f; font-weight: 800; font-size: .92rem; margin-top: 6px;
}
.set-start:disabled { opacity: .45; cursor: not-allowed; }
.set-hint { text-align: center; color: var(--muted); font-size: .78rem; margin: 10px 0 0; }

/* ---------- Chat (un bouton topbar, un volet à onglets) ----------
   Même langage visuel que la cloche de notifications, dans la topbar plutôt
   qu'en onglet flottant sur le bord (l'ancien design cachait un vrai bug :
   le badge non-lu posait `position: relative` sur le bouton, ce qui
   écrasait son `position: fixed` et le faisait retomber dans le flux normal
   du document, en bas de page). Un seul bouton/volet pour les deux
   conversations (Général toujours là, Salon seulement en jeu) — voir
   notifications.js:initChat. */
.topbar-chat-btn {
    position: relative; display: flex; align-items: center; justify-content: center; gap: 5px;
    background: none; border: none; cursor: pointer;
    padding: 8px 10px; border-radius: 8px; color: var(--muted); font-size: 0.85rem; font-family: inherit; font-weight: 600;
}
.topbar-chat-btn:hover { background: var(--bg-soft); color: var(--text); }
.topbar-chat-btn.chat-toggle-btn-active { background: var(--accent-strong); color: #04121f; }
.topbar-chat-badge {
    position: absolute; top: 3px; right: 3px; width: 9px; height: 9px; border-radius: 50%;
    background: var(--hit); border: 2px solid var(--panel);
}
.topbar-chat-badge.hidden { display: none; }

:root { --chat-panel-width: min(340px, 90vw); }

.chat-panel {
    position: fixed; top: var(--topbar-height, 66px); right: 0; bottom: 0; width: var(--chat-panel-width);
    /* z-index modeste : le volet ne doit pas lire comme un calque flottant
       très au-dessus du jeu. Séparation "fusion 1+2" retenue après essais
       dans static/preview.html (10+ variantes comparées) : le trait franc
       (bordure) garde une limite nette, l'ombre portée VERS LA PAGE (pas
       intérieure) donne un peu de profondeur sans faire flotter le volet
       comme une fenêtre indépendante. */
    z-index: 50;
    background: var(--panel); border-left: 1px solid var(--border);
    box-shadow: -18px 0 36px -16px rgba(0, 0, 0, .45);
    display: flex; flex-direction: column;
    padding-top: 14px;
    transform: translateX(100%);
    transition: transform .3s cubic-bezier(.16, 1, .3, 1);
    pointer-events: none;
}
.chat-panel.chat-panel-open { transform: translateX(0); pointer-events: auto; }

/* La zone de jeu se recentre dans l'espace restant plutôt que de se faire
   recouvrir (voir setOpen() dans notifications.js, qui pose
   .chat-panel-open sur <body>). Le padding-right se pose sur .content-shift
   (un conteneur dédié autour de <main>, voir base.html) et non sur <body> :
   body porte aussi la topbar et le footer, qui ne doivent pas bouger — seule
   la zone de jeu se décale. Le padding exclut la largeur du volet du calcul
   de centrage de .container (margin: 0 auto) : un simple margin-right sur
   .container ne suffit pas, tout le "mou" retomberait d'un seul côté et le
   contenu resterait collé contre le volet au lieu d'être centré. */
body.chat-panel-open .content-shift { padding-right: var(--chat-panel-width); }
body.chat-panel-open .container { max-width: calc(100% - 48px); }
/* Onglets répartis sur toute la largeur (flex:1 chacun, jusqu'à 3 : Général/
   Salon/Squad) plutôt que regroupés à gauche avec un espacement fixe — même
   traitement que .chat-tab.hidden pour masquer Salon/Squad indépendamment
   (voir syncTabsVisibility dans notifications.js), sans dépendre d'un seul
   "tout ou rien" sur la barre entière. */
.chat-tabs { display: flex; flex-shrink: 0; border-bottom: 1px solid var(--border); }
.chat-tab {
    flex: 1; display: flex; align-items: center; justify-content: center; gap: 5px;
    background: none; border: none; color: var(--muted); cursor: pointer; font-family: inherit;
    font-size: 0.82rem; font-weight: 600; padding: 10px 2px; border-bottom: 2px solid transparent;
}
.chat-tab.hidden { display: none; }
.chat-tab:hover { color: var(--text); }
.chat-tab.chat-tab-active { color: var(--accent); border-bottom-color: var(--accent); }
.chat-messages { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }
/* Bulles de message (avatar + fond de bulle) au lieu du texte plat — voir
   static/preview.html pour les essais avant intégration ici. Un message
   "propre" (chat-msg-own) n'a pas d'avatar (on sait déjà qui c'est) et
   s'aligne à droite, comme dans la plupart des apps de messagerie. */
.chat-msg { display: flex; gap: 8px; align-items: flex-end; font-size: 0.85rem; }
.chat-msg-own { flex-direction: row-reverse; }
.chat-msg-avatar {
    width: 26px; height: 26px; border-radius: 50%; flex: none;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800; font-size: .68rem; color: #04121f;
}
.chat-msg-col { display: flex; flex-direction: column; max-width: 78%; min-width: 0; }
.chat-msg-own .chat-msg-col { align-items: flex-end; margin-left: auto; }
.chat-msg-head { display: flex; align-items: baseline; gap: 6px; font-size: .72rem; padding: 0 3px; }
.chat-msg-name { font-weight: 700; color: var(--accent); }
.chat-msg-own .chat-msg-name { color: var(--good); }
.chat-msg-time { color: var(--muted); font-size: 0.72rem; }
.chat-msg-bubble {
    background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
    border-radius: 14px 14px 14px 4px; padding: 8px 12px; margin-top: 3px;
    font-size: .85rem; word-break: break-word;
}
.chat-msg-own .chat-msg-bubble {
    background: rgba(52,211,153,.12); border-color: rgba(52,211,153,.3);
    border-radius: 14px 14px 4px 14px;
}
.chat-msg-delete {
    margin-left: auto; background: none; border: none; color: var(--muted); cursor: pointer;
    font-size: 0.7rem; line-height: 1; padding: 2px 4px; border-radius: 6px; flex-shrink: 0;
    opacity: 0; transition: opacity .15s ease;
}
.chat-msg:hover .chat-msg-delete { opacity: 1; }
.chat-msg-delete:hover { background: var(--bg-soft); color: var(--hit); }
.chat-empty { text-align: center; padding: 20px 0; font-size: 0.85rem; }
/* padding vertical calé pour que le trait du haut tombe pile à la même
   hauteur que celui du footer (tous deux collés au bas du viewport quand le
   contenu de page est court) — 66px de hauteur totale des deux côtés.
   Champ pilule pleine largeur avec bouton d'envoi en icône intégré (avion
   en papier) plutôt qu'un bouton "Envoyer" séparé. */
.chat-form { position: relative; padding: 10px 14px; border-top: 1px solid var(--border); flex-shrink: 0; }
.chat-input {
    width: 100%; background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
    padding: 10px 42px 10px 14px; border-radius: 999px; font-size: .86rem; font-family: inherit;
}
.chat-input:focus { outline: none; border-color: var(--accent); }
.chat-send-btn {
    position: absolute; right: 20px; top: 50%; transform: translateY(-50%);
    width: 28px; height: 28px; border-radius: 50%; flex: none;
    display: flex; align-items: center; justify-content: center;
    background: var(--accent-strong); color: #04121f; border: none; cursor: pointer; transition: background .15s;
}
.chat-send-btn:hover { background: var(--accent); }

/* ---------- Yams : dés (dés 3D lancés en vrac sur un fond de terrain) ----------
   Repris et affiné depuis static/preview2.html (voir ce fichier pour l'historique
   des itérations : chevauchement des faces, fond de secours anti-interstice,
   ombrage des bords). Piloté par l'état serveur (turn.dice/turn.held) — voir
   renderDice()/rollAllDice() dans yams.js — pas de valeurs générées côté client
   comme dans la preview : les 5 dés sont des éléments DOM persistants (créés une
   fois, jamais détruits) pour ne pas couper une transition en cours à chaque
   re-rendu du long-poll. */
/* Tableau de score à droite du terrain par défaut ; bouton ⇅ (voir
   YM.toggleScoreLayout) pour repasser en pile verticale, préférence
   mémorisée en localStorage. flex-wrap permet au tableau de retomber sous
   le terrain naturellement si la fenêtre est trop étroite pour les deux
   côte à côte, sans dépendre du bouton. */
/* justify-content: center centre le DUO terrain+tableau comme un bloc dans
   le conteneur (au lieu de le laisser collé à gauche) — nécessaire depuis
   que #scorecard ne s'étire plus pour combler l'espace restant (voir plus
   bas) : sans ça, un tableau étroit (peu de joueurs) laisse tout le vide à
   droite plutôt que réparti des deux côtés. Neutralisé en layout empilé
   (colonne) où chaque bloc occupe déjà toute la largeur. */
/* flex-wrap: nowrap — laisse #scorecard (flex-shrink) se compresser sous la
   largeur de son contenu quand la place manque plutôt que de retomber
   spontanément sur 2 lignes (ce qui produisait un entre-deux non maîtrisé,
   mal aligné). C'est CE rétrécissement (clientWidth < scrollWidth) que
   maybeAutoStack() détecte pour basculer explicitement vers .wide-scoreboard
   (colonne, largeur alignée sur le terrain — voir plus bas) : le passage à
   la ligne reste piloté par JS, jamais par le flex-wrap lui-même. */
.yams-layout { display: flex; gap: 24px; align-items: flex-start; flex-wrap: nowrap; justify-content: center; }
/* Le terrain garde une taille fixe (682px -> 760px) au lieu de se partager
   l'espace à parts égales avec le tableau — sinon agrandir l'un revient à
   rogner l'autre. */
.yams-layout .dice-area { flex: 0 1 760px; min-width: 280px; margin-bottom: 0; }
.yams-layout .dice-stage { width: 100%; max-width: 920px; }
/* .scorecard-wrap ET .scorecard (le <table> lui-même) ont chacun
   `margin: 0 auto` par défaut, pour se centrer seuls hors de ce layout (ex.
   écran de fin). #scorecard prenait auparavant tout l'espace restant
   (flex-grow) et laissait le <table>, plus étroit (peu de joueurs), se
   recentrer dedans — ce qui rouvrait un grand vide entre le terrain et le
   tableau tout en gardant le DUO plaqué à gauche du conteneur. #scorecard
   ne grandit donc plus au-delà de son contenu (flex: 0 1 auto) : c'est
   `justify-content: center` ci-dessus qui centre l'ensemble à la place. En
   layout empilé, .scorecard doit au contraire rester centré — c'est ce
   centrage, combiné à sa min-width alignée sur celle du terrain (voir plus
   bas), qui fait coïncider leurs bords. */
.yams-layout #scorecard { flex: 0 1 auto; min-width: 280px; margin: 0 0 10px; }
.yams-layout #scorecard .scorecard { margin-left: 0; margin-right: 0; }
.yams-layout.stacked #scorecard .scorecard, .yams-layout.wide-scoreboard #scorecard .scorecard {
    margin-left: auto; margin-right: auto;
}
/* .wide-scoreboard : basculement automatique (voir maybeAutoStack dans
   yams.js), quand la feuille de score a plus de joueurs qu'il n'y a de
   place à côté du terrain — sans ça, elle affiche un scroll horizontal
   interne au lieu de passer sous le terrain. Même mise en page que le
   bouton manuel .stacked, mais pilotée par le contenu plutôt que par un
   choix persistant de l'utilisateur. */
.yams-layout.stacked, .yams-layout.wide-scoreboard { flex-direction: column; justify-content: flex-start; }
.yams-layout.stacked .dice-area, .yams-layout.stacked #scorecard,
.yams-layout.wide-scoreboard .dice-area, .yams-layout.wide-scoreboard #scorecard { flex: 1 1 auto; width: 100%; }
/* Empilé, le tableau se centre sur sa propre largeur naturelle (nombre de
   colonnes = nombre de joueurs) plutôt que sur celle du terrain : à 4
   joueurs il est nettement plus étroit que le terrain (bords très en
   retrait), à 6 il devient plus large que lui (déborde des deux côtés) — le
   décalage entre les deux bascule sans cohérence d'une partie à l'autre.
   En alignant sa largeur mini sur celle du terrain (920px), les deux
   partagent les mêmes bords tant que le tableau tient dedans ; au-delà (
   beaucoup de joueurs), il s'élargit naturellement, ce qui est attendu. */
.yams-layout.stacked .scorecard, .yams-layout.wide-scoreboard .scorecard { min-width: min(920px, 100%); }
/* Bouton de bascule du tableau : logé dans la case vide du coin supérieur
   gauche de la feuille de score (voir renderScorecard dans yams.js), au lieu
   d'un bouton séparé flottant à côté du plateau. Discret : pas de fond/bordure
   au repos, aligné à gauche de la case plutôt que centré comme les autres
   en-têtes. */
.scorecard th.scorecard-corner { text-align: left; }
.layout-toggle-btn {
    white-space: nowrap; border: none; background: none; padding: 2px 4px;
    color: var(--muted); font-size: .9rem; line-height: 1; border-radius: 6px;
    cursor: pointer;
}
.layout-toggle-btn:hover { color: var(--text); background: rgba(255,255,255,.08); }

.dice-area { text-align: center; margin-bottom: 26px; }
/* Même gabarit que .table-wrap dans static/preview2.html — c'est la
   référence visuelle demandée, pas un format inventé pour cet écran. */
.dice-stage {
    position: relative;
    width: min(920px, 94vw);
    aspect-ratio: 4 / 3;
    border-radius: 20px;
    border: 1px solid var(--border);
    background-color: var(--bg-soft);
    background-size: cover;
    background-position: center;
    margin: 0 auto 16px;
    overflow: hidden;
}
.dice-stage::after {
    content: "";
    position: absolute; inset: 0;
    background: radial-gradient(ellipse at center, rgba(0,0,0,0) 45%, rgba(0,0,0,.32) 100%);
    pointer-events: none;
}

.die-toss {
    position: absolute;
    left: 50%; top: 50%;
    width: 56px; height: 56px;
    margin: -28px 0 0 -28px;
    --tx: 0px; --ty: 0px; --rot: 0deg;
    transform: translate(var(--tx), var(--ty)) rotate(var(--rot));
    transition: transform .85s cubic-bezier(.2, .85, .25, 1);
    z-index: 1;
}
.die-toss.tossing { animation: ym-toss-arc .85s cubic-bezier(.2, .85, .25, 1); }
@keyframes ym-toss-arc {
    0%   { transform: translate(0px, 0px) scale(.35) rotate(0deg); opacity: 0; }
    12%  { opacity: 1; }
    50%  { transform: translate(calc(var(--tx) * .55), calc(var(--ty) * .55 - 70px)) scale(1.4) rotate(calc(var(--rot) * .6)); }
    78%  { transform: translate(calc(var(--tx) * 1.03), calc(var(--ty) * 1.03 - 8px)) scale(.94) rotate(calc(var(--rot) * 1.05)); }
    100% { transform: translate(var(--tx), var(--ty)) scale(1) rotate(var(--rot)); opacity: 1; }
}

.die3d-wrap { position: relative; width: 56px; height: 56px; }
.die3d-shadow {
    position: absolute; left: 50%; bottom: -6px;
    width: 50px; height: 13px; margin-left: -25px;
    border-radius: 50%;
    background: rgba(0,0,0,.5); filter: blur(3px);
    transition: transform .85s cubic-bezier(.2,.85,.25,1), opacity .85s ease;
}
.die-toss.tossing .die3d-shadow { transform: scale(1.7); opacity: .15; }
.die3d-wrap.held .die3d-shadow { box-shadow: 0 0 0 3px rgba(242,180,92,.55); }

.die3d-scene {
    /* Fond proche de la couleur du dé, à plat derrière le cube (pas de
       preserve-3d ici) : un interstice entre deux faces montre alors une
       fente presque blanche plutôt que le fond de la scène. */
    width: 56px; height: 56px; perspective: 1400px; border-radius: 6px;
    background: #dbe2ef;
    transition: box-shadow .15s ease, opacity .2s ease;
    cursor: default;
}
.die3d-wrap.clickable .die3d-scene { cursor: pointer; }
.die3d-wrap.empty .die3d-scene { opacity: .35; }
.die3d-wrap.held .die3d-scene { box-shadow: 0 0 0 4px rgba(242,180,92,.55), 0 0 14px rgba(242,180,92,.5); }
.die3d-cube {
    position: relative;
    width: 100%; height: 100%;
    transform-style: preserve-3d;
    transition: transform 1.05s cubic-bezier(.22,.9,.3,1.25);
}
.die3d-face {
    position: absolute; inset: -1px;
    background: linear-gradient(150deg, #ffffff, #d7deea);
    border-radius: 6px;
    box-shadow: inset 0 -4px 8px rgba(0,0,0,.12), inset 0 3px 3px rgba(255,255,255,.6), inset 0 0 9px 1px rgba(0,0,0,.22);
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(3, 1fr);
    padding: 10px;
    backface-visibility: hidden;
}
.die3d-pip { width: 10px; height: 10px; border-radius: 50%; background: #1b2333; align-self: center; justify-self: center; box-shadow: inset 0 1px 1px rgba(255,255,255,.5); }
.dp-c  { grid-column: 2; grid-row: 2; }
.dp-tl { grid-column: 1; grid-row: 1; } .dp-tr { grid-column: 3; grid-row: 1; }
.dp-bl { grid-column: 1; grid-row: 3; } .dp-br { grid-column: 3; grid-row: 3; }
.dp-ml { grid-column: 1; grid-row: 2; } .dp-mr { grid-column: 3; grid-row: 2; }

.dface-front  { transform: translateZ(28px); }
.dface-back   { transform: rotateY(180deg) translateZ(28px); }
.dface-right  { transform: rotateY(90deg) translateZ(28px); }
.dface-left   { transform: rotateY(-90deg) translateZ(28px); }
.dface-top    { transform: rotateX(90deg) translateZ(28px); }
.dface-bottom { transform: rotateX(-90deg) translateZ(28px); }

.dice-controls { display: flex; gap: 14px; align-items: center; justify-content: center; }
.hint { font-size: .85rem; margin-top: 12px; }

/* ---------- Yams : feuille de score ---------- */
.scorecard-wrap { overflow-x: auto; margin: 0 auto 10px; }
.scorecard {
    border-collapse: collapse; margin: 0 auto; min-width: 340px;
    background: var(--panel); border-radius: var(--radius); overflow: hidden;
    box-shadow: var(--shadow);
}
.scorecard th, .scorecard td {
    border: 1px solid var(--border); padding: 9px 14px; text-align: center; font-size: .92rem;
}
.scorecard th { background: var(--bg-soft); font-weight: 600; }
.scorecard .cat-label { text-align: left; color: var(--muted); font-weight: 500; white-space: nowrap; }
.scorecard td.filled { font-weight: 700; color: var(--text); }
.scorecard td.col-active, .scorecard th.col-active { background: rgba(167,139,250,.10); }
.scorecard td.ghost { color: var(--accent); opacity: .75; font-style: italic; }
.scorecard td.clickable { cursor: pointer; }
.scorecard td.clickable:hover { background: rgba(167,139,250,.28); opacity: 1; font-style: normal; }
.scorecard .sum-row td { background: var(--bg-soft); font-weight: 600; }
.scorecard .total-row td { background: var(--panel-2); font-size: 1.05rem; font-weight: 800; }

/* ---------- Flip 7 ----------
   Prototypé dans static/preview.html avant ce portage (3 itérations avec
   l'utilisateur : palette carnaval jugée hors-charte -> recalée sur les
   vrais tokens du site ; cartes spéciales tenues à l'écart de la main).
   .pill/.control-panel/.turn-banner/.action-row/.target-row sont des
   classes PARTAGÉES par tous les jeux (voir les fichiers static/js) : leurs
   surcharges ci-dessous sont donc scopées via #game[data-slug="flip7"],
   jamais réécrites globalement. .flip-board/.player-panel/.pp- et .fcard
   n'appartiennent qu'à flip7.js — éditables directement. */
.flip-topbar { display: flex; gap: 14px; justify-content: center; margin-bottom: 22px; flex-wrap: wrap; }
/* Base partagée par tous les jeux — supprimée par erreur lors d'un premier
   portage (elle a fini remplacée au lieu d'être surchargée), ce qui cassait
   les pastilles .pill de tous les AUTRES jeux, pas seulement Flip 7. */
.pill {
    background: var(--panel); border: 1px solid var(--border); border-radius: 999px;
    padding: 7px 16px; font-size: .88rem; color: var(--muted);
}
.pill b { color: var(--text); }
/* Ticket à 2 lignes (petit libellé au-dessus, grande valeur en dessous) —
   le texte brut "Manche " et le <b> du template sont chacun un item flex
   à part entière une fois le parent en colonne, pas besoin de span en plus. */
#game[data-slug="flip7"] .flip-topbar .pill {
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    position: relative; padding: 10px 22px; min-width: 92px; border-radius: 10px;
    font-size: .66rem; text-transform: uppercase; letter-spacing: 1.3px;
}
#game[data-slug="flip7"] .flip-topbar .pill::before,
#game[data-slug="flip7"] .flip-topbar .pill::after {
    content: ""; position: absolute; top: 50%; width: 14px; height: 14px; margin-top: -7px;
    background: var(--bg); border-radius: 50%;
}
#game[data-slug="flip7"] .flip-topbar .pill::before { left: -7px; }
#game[data-slug="flip7"] .flip-topbar .pill::after { right: -7px; }
#game[data-slug="flip7"] .flip-topbar .pill b {
    font-size: 1.25rem; font-weight: 800; letter-spacing: 0; text-transform: none;
    font-variant-numeric: tabular-nums;
}

/* Base partagée par tous les jeux — même erreur que .pill ci-dessus : ces
   4 règles avaient disparu du fichier au lieu d'être surchargées, cassant
   le panneau de contrôle (et .action-row/.target-row) de TOUS les jeux. */
.control-panel {
    max-width: 620px; margin: 0 auto 24px; padding: 16px;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel);
    transition: border-color .2s, box-shadow .2s;
}
.control-panel.active-panel { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(251,113,133,.15); }
.action-row, .target-row { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin-top: 12px; }
.control-panel .turn-banner { margin-bottom: 0; }

#game[data-slug="flip7"] .control-panel.active-panel {
    border-color: rgba(251,113,133,.4);
    animation: fl-pulse 1.8s ease-in-out infinite;
}
@keyframes fl-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(251,113,133,.4); }
    50% { box-shadow: 0 0 0 9px rgba(251,113,133,0); }
}
@media (prefers-reduced-motion: reduce) {
    #game[data-slug="flip7"] .control-panel.active-panel { animation: none; }
}
/* Bannière "à vous"/"en attente" + boutons : .turn-banner/.btn-primary/
   .btn-ghost sont partagés (voir banner()/renderControls dans flip7.js et
   l'équivalent dans les autres jeux) — la bannière générique est verte
   partout ailleurs sur le site, ici on la remet à plat (plus de boîte dans
   la boîte : .control-panel porte déjà son propre cadre) et on la teinte
   avec l'accent rose du jeu plutôt que le vert générique. */
#game[data-slug="flip7"] .control-panel .turn-banner {
    border: none; background: transparent; padding: 0; margin-bottom: 14px;
    font-weight: 700; font-size: 1.05rem;
}
#game[data-slug="flip7"] .control-panel .turn-banner.your-turn { color: var(--accent); }
#game[data-slug="flip7"] .control-panel .turn-banner.wait-turn { color: var(--muted); }
#game[data-slug="flip7"] .control-panel .btn-primary {
    border-radius: 999px; font-weight: 700;
    background: var(--accent); border-color: var(--accent); color: #2a0a10;
}
#game[data-slug="flip7"] .control-panel .btn-primary:hover:not(:disabled) {
    background: var(--accent); box-shadow: 0 0 0 4px rgba(251,113,133,.22);
}
#game[data-slug="flip7"] .control-panel .btn-ghost { border-radius: 999px; font-weight: 700; }
#game[data-slug="flip7"] .control-panel .btn-ghost:hover:not(:disabled) {
    border-color: var(--accent); color: var(--accent);
}

.flip-board {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(264px, 1fr));
    gap: 16px; margin-bottom: 26px;
}
.player-panel {
    position: relative; overflow: hidden;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 16px; transition: border-color .2s, transform .15s, box-shadow .2s;
}
.player-panel::before {
    content: ""; position: absolute; top: -40px; left: -40px; width: 90px; height: 90px;
    border-radius: 50%; background: var(--accent); opacity: .08; filter: blur(6px);
}
.player-panel.current { border-color: rgba(251,113,133,.45); box-shadow: var(--shadow); transform: translateY(-2px); }
.player-panel.status-busted { filter: grayscale(.6) brightness(.82); border-style: dashed; }
.player-panel.status-frozen { border-color: rgba(56,189,248,.4); }
.player-panel.status-frozen::after {
    content: "❄️"; position: absolute; top: 10px; right: 12px; opacity: .85; font-size: 1rem;
}
.player-panel.status-flip7 {
    border-color: rgba(246,195,68,.45);
    background:
        repeating-conic-gradient(from 0deg, rgba(246,195,68,.14) 0deg 6deg, transparent 6deg 18deg),
        var(--panel);
    box-shadow: 0 0 0 1px rgba(246,195,68,.3), 0 16px 34px rgba(246,195,68,.22);
}
.pp-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 2px; }
.pp-name { font-weight: 600; }
.pp-total { font-weight: 800; font-size: 1.1rem; color: var(--accent); font-variant-numeric: tabular-nums; }
.pp-meta { display: flex; align-items: center; margin-bottom: 10px; min-height: 20px; }
.pp-status {
    display: inline-block; font-size: .68rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 1.2px; color: var(--muted);
    padding: 2px 9px; border-radius: 999px; background: var(--panel-2);
}
.player-panel.current .pp-status { background: var(--accent); color: #2a0a10; }
.player-panel.status-flip7 .pp-status { background: #f6c344; color: #3a2c04; }
.player-panel.status-busted .pp-status { background: var(--hit); color: #fff; }
.player-panel.status-frozen .pp-status { background: #38bdf8; color: #04212f; }
.pp-cards { display: flex; flex-wrap: wrap; gap: 8px; min-height: 90px; align-content: flex-start; }
/* Cartes spéciales (modificateurs + Second Chance) : tenues à l'écart de la
   main de nombres plutôt que mêlées dedans (retour utilisateur), séparées
   par un filet pointillé et plus petites pour marquer la hiérarchie. */
.pp-specials {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border);
}
.pp-specials-label {
    font-size: .64rem; text-transform: uppercase; letter-spacing: 1.2px;
    color: var(--muted); white-space: nowrap;
}
.pp-specials-row { display: flex; flex-wrap: wrap; gap: 6px; }
.pp-specials-row .fcard { width: 38px; }
.pp-foot { display: flex; justify-content: space-between; margin-top: 12px; font-size: .8rem; color: var(--muted); }
.pp-round b { color: var(--text); }

/* Cartes illustrées (images de static/img/games/flip7) */
.fcard {
    width: 70px; height: auto; border-radius: 8px; display: block;
    box-shadow: 0 2px 6px rgba(0,0,0,.35); background: #fbf7ec;
    transition: transform .15s, box-shadow .15s;
}
.fcard-num, .fcard-mod, .fcard-action { cursor: default; }
.player-panel.current .fcard:hover,
.pp-specials-row .fcard:hover {
    transform: translateY(-4px) scale(1.06); box-shadow: 0 8px 18px rgba(0,0,0,.45);
}
.fcard-sc { width: 34px; border-radius: 5px; box-shadow: 0 1px 3px rgba(0,0,0,.4); }
.fcard-bust {
    outline: 2px solid var(--bad, #f87171); outline-offset: 1px;
    filter: grayscale(.35) brightness(.92);
}
@keyframes fcard-draw { from { opacity: 0; transform: translateY(-10px) scale(.85); } to { opacity: 1; transform: none; } }
.fcard-new, .tcard-new { animation: fcard-draw .35s ease-out backwards; }
@media (prefers-reduced-motion: reduce) { .fcard-new, .tcard-new { animation: none; } }

.log-panel { max-width: 620px; margin: 0 auto; }
.log-panel h3 { font-size: .85rem; text-transform: uppercase; letter-spacing: 2px; color: var(--muted); margin-bottom: 8px; }
.event-log { list-style: none; padding: 12px 16px; margin: 0; background: var(--bg-soft); border: 1px solid var(--border); border-radius: 12px; max-height: 190px; overflow-y: auto; }
.event-log li { padding: 4px 0; font-size: .88rem; color: var(--muted); border-bottom: 1px solid rgba(255,255,255,.04); }
.event-log li:first-child { color: var(--text); }
.event-log li:last-child { border-bottom: none; }

.ranking { max-width: 420px; margin: 0 auto 24px; }
.rank-row { display: flex; align-items: center; gap: 14px; padding: 12px 16px; border: 1px solid var(--border); border-radius: 10px; margin-bottom: 8px; background: var(--panel); }
.rank-row.rank-win { border-color: var(--good); background: rgba(52,211,153,.08); }
.rank-pos { font-weight: 700; color: var(--muted); width: 20px; }
.rank-name { flex: 1; font-weight: 600; }
.rank-total { font-weight: 700; color: var(--accent); }

/* ---------- Trio ---------- */
.trio-section-title {
    font-size: .8rem; text-transform: uppercase; letter-spacing: 2px;
    color: var(--muted); margin: 0 0 10px;
}
.trio-middle-wrap { margin-bottom: 24px; }
.trio-middle {
    display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
    padding: 14px; background: var(--bg-soft); border: 1px solid var(--border);
    border-radius: var(--radius); min-height: 90px; align-content: flex-start;
}
.trio-players {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 16px; margin-bottom: 26px;
}
.trio-player {
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 14px; transition: border-color .2s, box-shadow .2s, transform .15s;
}
.trio-player.current { border-color: var(--accent); box-shadow: var(--shadow); transform: translateY(-2px); }
.tp-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 10px; }
.tp-name { font-weight: 600; }
.tp-trios-count { font-weight: 700; color: var(--accent); font-size: .82rem; }
.tp-trios { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 10px; }
.tp-hand { display: flex; flex-wrap: wrap; gap: 5px; min-height: 62px; align-content: flex-start; }
.tp-actions { display: flex; gap: 8px; margin-top: 10px; }

/* Cartes illustrées (images de static/img/games/trio) */
.tcard {
    width: 62px; height: auto; border-radius: 7px; display: block;
    box-shadow: 0 2px 6px rgba(0,0,0,.4); background: #10151f;
    transition: transform .15s, box-shadow .15s, outline-color .15s;
    outline: 2px solid transparent; outline-offset: 1px;
}
.tcard-sm { width: 46px; border-radius: 5px; }
.tcard-mini { width: 30px; border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,.45); }
.tcard-back { filter: brightness(.92); }
.tcard.clickable { cursor: pointer; }
.tcard.clickable:hover { transform: translateY(-4px) scale(1.05); box-shadow: 0 8px 16px rgba(0,0,0,.5); }
.tcard-group { outline-color: var(--accent); }
.tcard-busted { outline-color: #f87171; filter: saturate(.85) brightness(.96); }
.tcard-own { opacity: .96; outline-color: rgba(255,255,255,.12); }

/* ---------- Président : cartes illustrées réelles (static/img/games/cartes) ----------
   `.pres-hand` (grille simple, mise en place uniquement) vs le plateau de
   jeu qui reprend le gabarit terrain d'Uno/5 Alive : `.pres-center` +
   `.hand-row`/`.fan-card` (génériques, voir plus bas dans ce fichier) pour
   la main du joueur, `.opp-stack` (générique) pour les adversaires. */
.pres-hand { display: flex; flex-wrap: wrap; gap: 6px; align-items: flex-end; min-height: 76px; }
.pcard {
    width: 58px; height: auto; border-radius: 6px; display: block;
    box-shadow: 0 2px 6px rgba(0,0,0,.4); background: #10151f;
    transition: transform .15s, box-shadow .15s, outline-color .15s;
    outline: 2px solid transparent; outline-offset: 1px;
}
.pcard.clickable { cursor: pointer; }
.pcard.clickable:hover { transform: translateY(-6px) scale(1.06); box-shadow: 0 8px 16px rgba(0,0,0,.5); }
.pcard-selected { outline-color: var(--accent); transform: translateY(-8px); }
.pres-declare { margin: 10px 0; text-align: center; }
.pres-declare select { margin-left: 6px; }
.pres-actions { display: flex; gap: 10px; margin-top: 12px; justify-content: center; }

/* Fond de terrain (voir dev_set_terrain dans president.py) : même gabarit
   que .uno-center/.fa-center — la main et les adversaires vivent À
   L'INTÉRIEUR du terrain (voir .hand-row/.fan-card/.opp-stack, génériques,
   partagés avec Uno et 5 Alive). */
.pres-center {
    position: relative;
    width: min(900px, 96vw); aspect-ratio: 4 / 3; margin: 6px auto 16px;
    border-radius: 20px; background-color: var(--bg-soft);
    background-size: cover; background-position: center; overflow: hidden;
}
.pres-stage { position: absolute; inset: 0; z-index: 2; display: flex; align-items: center; justify-content: center; }
/* Pile de défausse (comme Uno) plutôt qu'une rangée à plat : chaque carte du
   pli en cours (une combi peut en compter jusqu'à 4, + les complétions/la
   chaîne qui s'accumulent dessus) est empilée en position absolue avec un
   léger décalage/rotation façon "posée négligemment" — la dernière posée
   (z-index le plus haut, cf. président.js) apparaît au-dessus de la pile. */
.pres-pli-stack { position: relative; width: 160px; height: 170px; margin: 0 auto; }
.pres-pli-card {
    position: absolute; left: 50%; top: 50%; width: 82px;
    border-radius: 9px; box-shadow: 0 3px 8px rgba(0,0,0,.45); display: block;
    transform: translate(-50%, -50%) translate(var(--tx, 0px), var(--ty, 0px)) rotate(var(--rot, 0deg));
}
.pres-pli-card.pres-pli-new { animation: pres-pli-drop .28s cubic-bezier(.2,.8,.3,1.15) backwards; }
@keyframes pres-pli-drop {
    from { transform: translate(-50%, -50%) translate(var(--tx, 0px), calc(var(--ty, 0px) - 40px)) rotate(var(--rot, 0deg)); opacity: 0; }
    to { transform: translate(-50%, -50%) translate(var(--tx, 0px), var(--ty, 0px)) rotate(var(--rot, 0deg)); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .pres-pli-card.pres-pli-new { animation: none; } }

/* Flash plein écran au déclenchement/à l'annulation d'une révolution (voir
   variante `revolution` dans president.py) — même principe que .uno-flash
   (UNO!/CONTRE-UNO!), sa propre classe pour garder la couleur/le texte
   propres à Président plutôt que de réutiliser les modificateurs Uno. */
.pres-flash {
    position: fixed; inset: 0; z-index: 140; display: grid; place-items: center;
    pointer-events: none; font-weight: 900; letter-spacing: 2px;
    font-size: clamp(2rem, 9vw, 4.6rem); text-align: center; padding: 0 24px;
    animation: uno-flash-anim 1.5s cubic-bezier(.2,.8,.3,1) forwards;
    text-shadow: 0 4px 24px rgba(0,0,0,.65);
    color: #a78bfa;
}
.pres-flash.pres-flash-off { color: #94a3b8; }
.pres-center.pres-revolution-shake { animation: pres-revolution-shake .5s ease-in-out; }
@keyframes pres-revolution-shake {
    0%, 100% { transform: rotate(0); }
    20% { transform: rotate(-1.2deg); }
    40% { transform: rotate(1deg); }
    60% { transform: rotate(-0.7deg); }
    80% { transform: rotate(0.5deg); }
}
@media (prefers-reduced-motion: reduce) {
    .pres-flash { animation: none; }
    .pres-center.pres-revolution-shake { animation: none; }
}

/* Tirage au sort du premier joueur : voir les classes partagées .gs-draw-*
   (style.css, section "Tirage au sort du premier joueur (partagé, tous les
   jeux)") + GS_ROOM.runStarterDraw (room-ui.js) — plus de duplicata ici. */

.pres-opponents { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.pres-my-tag {
    position: absolute; left: 3%; bottom: 3%; z-index: 4;
    display: inline-flex; align-items: center; gap: 8px;
    font-size: .78rem; font-weight: 700; color: rgba(255,255,255,.92);
    text-shadow: 0 1px 4px rgba(0,0,0,.85);
    background: rgba(10,14,24,.5); padding: 5px 12px; border-radius: 999px;
}
/* Carte "nue" dans un .fan-card (pas de cadre propre, le conteneur gère déjà
   l'ombre/le survol) — voir .hand-row .fan-card img.uno-card / .fa-card-img,
   même principe pour Président. */
.hand-row .fan-card img.pres-card-img { width: 100%; box-shadow: none; border-radius: 0; }
/* Carte sélectionnée dans la main (Président joue plusieurs cartes à la fois,
   contrairement à Uno/5 Alive : un simple survol ne suffit pas à indiquer
   l'état — added AFTER .fan-card:hover dans la cascade pour rester visible
   même carte survolée. */
.fan-card.selected .fan-card-lift {
    outline: 3px solid var(--accent); outline-offset: -3px;
    transform: translateY(-14px);
}

/* ---------- Président : mise en place (échange / main cachée) ---------- */
.pres-setup-panel { max-width: 520px; margin: 0 auto; }
.pres-setup-block { margin-bottom: 20px; }
.pres-setup-block > p { margin-bottom: 8px; }

.trio-link-hint {
    margin: 10px 0 0; text-align: center; font-size: .9rem; font-weight: 600;
    color: var(--accent);
}
.mode-toggle { display: flex; gap: 10px; margin: 6px 0 4px; }
.mode-btn {
    flex: 1; padding: 12px 10px; border-radius: 12px; cursor: pointer;
    background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
    font: inherit; font-weight: 600; text-align: center;
    transition: border-color .15s, background .15s, transform .1s;
}
.mode-btn span { display: block; font-size: .74rem; font-weight: 500; color: var(--muted); margin-top: 3px; }
.mode-btn:hover { border-color: var(--accent); }
.mode-btn.is-active {
    border-color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, transparent);
}
.mode-btn.is-active span { color: var(--text); }

/* Zoom sur une carte visible (lecture des combinaisons liées) */
.card-zoom {
    position: fixed; inset: 0; z-index: 100; padding: 24px;
    background: rgba(4, 8, 16, .82); backdrop-filter: blur(3px);
    display: none; align-items: center; justify-content: center; cursor: zoom-out;
}
.card-zoom.show { display: flex; animation: card-zoom-fade .15s ease-out; }
.card-zoom-img {
    max-height: 86vh; max-width: 92vw; border-radius: 16px;
    box-shadow: 0 24px 70px rgba(0, 0, 0, .65);
    animation: card-zoom-in .18s cubic-bezier(.2, .8, .3, 1);
}
@keyframes card-zoom-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes card-zoom-in {
    from { opacity: 0; transform: scale(.88); }
    to   { opacity: 1; transform: scale(1); }
}

/* ---------- Présence (déconnexion) ---------- */
.offline-badge {
    display: inline-flex; align-items: center; gap: 4px;
    color: #f87171; font-size: .72rem; font-weight: 600; margin-left: 6px;
    vertical-align: middle; letter-spacing: .3px;
}
.offline-badge::before { content: "●"; font-size: .7em; }

/* Pin "en ligne" (liste des utilisateurs, admin — voir gamespace/site_presence.py) :
   contrairement à .offline-badge (signale l'exception dans un salon où
   tout le monde est normalement connecté), ici l'inverse est vrai — la
   plupart des comptes ne sont pas en train de naviguer, donc c'est la
   présence qui mérite d'être signalée plutôt que l'absence. */
.online-pin {
    display: inline-block; width: 8px; height: 8px; border-radius: 50%;
    background: #34d399; box-shadow: 0 0 5px #34d399; margin-right: 6px;
    vertical-align: middle;
}

/* ---------- Puissance 4 ---------- */
.c4-board {
    display: flex; gap: 6px; justify-content: center;
    background: #1e40af; padding: 12px; border-radius: 18px;
    max-width: 520px; margin: 0 auto 18px;
    box-shadow: 0 10px 30px rgba(30, 64, 175, .35), inset 0 2px 6px rgba(255,255,255,.12);
}
.c4-col { display: flex; flex-direction: column; gap: 6px; flex: 1; border-radius: 10px; }
.c4-col.playable { cursor: pointer; }
.c4-col.playable:hover .c4-cell { background: #2563eb; }
.c4-cell {
    position: relative; aspect-ratio: 1; background: #1e40af; border-radius: 4px;
    transition: background .12s;
}
.c4-cell::before {
    content: ""; position: absolute; inset: 11%; border-radius: 50%;
    background: var(--bg); box-shadow: inset 0 3px 5px rgba(0,0,0,.55);
}
.c4-disc {
    position: absolute; inset: 11%; border-radius: 50%; z-index: 1;
    box-shadow: inset 0 -3px 6px rgba(0,0,0,.3), 0 1px 3px rgba(0,0,0,.45);
}
.c4-p1 { background: radial-gradient(circle at 34% 30%, #f87171, #dc2626); }
.c4-p2 { background: radial-gradient(circle at 34% 30%, #fde047, #eab308); }
.c4-disc.just-dropped { animation: c4-drop .32s cubic-bezier(.4, .05, .6, 1); }
@keyframes c4-drop { from { transform: translateY(-620%); } to { transform: translateY(0); } }
.c4-disc.c4-win { outline: 3px solid #fff; outline-offset: -3px; animation: c4-pulse 1s ease-in-out infinite; }
@keyframes c4-pulse { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.4); } }

.c4-legend { display: flex; gap: 26px; justify-content: center; }
.c4-legend-item { display: flex; align-items: center; gap: 8px; color: var(--muted); }
.c4-legend-item b { color: var(--text); font-weight: 600; }
.c4-chip { width: 18px; height: 18px; border-radius: 50%; display: inline-block; }
.c4-board-end { pointer-events: none; }
.c4-end-actions { display: flex; justify-content: center; gap: 10px; margin-top: 4px; flex-wrap: wrap; }

/* ---------- 6 qui prend ! ---------- */
.sp-rows-wrap { margin-bottom: 20px; }
/* Fond de terrain (voir dev_set_terrain dans sixquiprend.py) : .sp-row garde
   un fond translucide (rgba, pas --bg-soft opaque) pour qu'il reste
   visible derrière les rangées plutôt que de les cacher entièrement. */
.sp-rows {
    display: flex; flex-direction: column; gap: 10px;
    padding: 64px; border-radius: 14px; background-color: rgba(0,0,0,.22);
    background-size: cover; background-position: center;
}
.sp-row {
    display: flex; align-items: center; gap: 6px; padding: 8px 10px;
    background: rgba(17,26,46,.55); border: 1px solid var(--border); border-radius: 12px;
    transition: border-color .15s, background .15s;
}
.sp-row.choosable { cursor: pointer; }
.sp-row.choosable:hover { border-color: var(--accent); background: rgba(245, 158, 11, .1); }
.sp-row.forced {
    cursor: pointer; border-color: var(--accent);
    background: rgba(245, 158, 11, .14); box-shadow: 0 0 0 2px rgba(245, 158, 11, .35);
}
.sp-slot {
    width: 64px; aspect-ratio: 5 / 7; border-radius: 7px; flex: none;
    border: 1px dashed var(--border);
}
.sp-row-heads {
    margin-left: auto; font-size: .85rem; font-weight: 700; color: var(--muted);
    white-space: nowrap; padding-left: 8px;
}
.sp-card {
    width: 64px; height: auto; border-radius: 7px; display: block; flex: none;
    box-shadow: 0 2px 6px rgba(0,0,0,.4); background: #fff;
}
/* Animation de pose d'une carte */
.sp-card-placed { z-index: 5; }              /* marqueur : la carte qui vient d'être posée */
.sp-card-drop { animation: sp-place-in .4s cubic-bezier(.2, .85, .3, 1); }  /* repli (pas de source) */
@keyframes sp-place-in {
    0%   { transform: translateY(-46px) scale(.82) rotate(-4deg); opacity: 0; }
    60%  { transform: translateY(4px) scale(1.04); opacity: 1; }
    100% { transform: none; }
}
.sp-row-taken { animation: sp-row-flash .55s ease-out; }
@keyframes sp-row-flash {
    0%   { background: rgba(248, 113, 113, .4); box-shadow: 0 0 0 2px rgba(248, 113, 113, .5); }
    100% { }
}

/* Bande de révélation (cartes jouées ce tour) */
.sp-reveal {
    display: flex; flex-wrap: wrap; gap: 12px; justify-content: center;
    margin-bottom: 18px; padding: 12px 14px;
    background: var(--bg-soft); border: 1px solid var(--border); border-radius: 14px;
}
.sp-reveal-item { display: flex; flex-direction: column; align-items: center; gap: 5px; transition: opacity .2s; }
.sp-reveal-item.placed { opacity: .38; }
.sp-reveal-item.current .sp-reveal-card { outline: 3px solid var(--accent); outline-offset: 2px; }
.sp-reveal-card { width: 60px; }
.sp-reveal-name { font-size: .74rem; color: var(--muted); max-width: 72px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Invite de sélection : discrète (le survol des cartes indique déjà l'action). */
.sp-select-hint {
    color: var(--muted); background: var(--bg-soft);
    border-color: var(--border); font-weight: 500; font-size: .95rem;
}

.sp-hand-wrap { margin-bottom: 22px; }
.sp-hand { display: flex; flex-wrap: wrap; gap: 10px; min-height: 116px; align-content: flex-start; }
.sp-hand-card { width: 84px; transition: transform .12s, box-shadow .12s; }
.sp-hand-card.clickable { cursor: pointer; }
.sp-hand-card.clickable:hover { transform: translateY(-8px); box-shadow: 0 12px 20px rgba(0,0,0,.5); }

.sp-players {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 10px; margin-bottom: 22px;
}
.sp-player {
    display: flex; justify-content: space-between; align-items: center; gap: 8px;
    background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
    padding: 10px 12px;
}
.sp-player.chosen { border-color: var(--accent); }
.sp-pname { font-weight: 600; }
.sp-pmeta { display: flex; align-items: center; gap: 10px; }
.sp-penalty { font-weight: 700; color: var(--accent); white-space: nowrap; }
.sp-chosen { font-size: .74rem; color: var(--good); font-weight: 600; }
.sp-thinking { font-size: .74rem; color: var(--muted); font-style: italic; }

/* ---------- Morpion ---------- */
.mp-board {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
    max-width: 340px; margin: 0 auto 20px;
}
.mp-cell {
    aspect-ratio: 1; background: var(--panel); border: 1px solid var(--border);
    border-radius: 14px; display: grid; place-items: center;
    transition: background .12s, border-color .12s;
}
.mp-cell.playable { cursor: pointer; }
.mp-cell.playable:hover { border-color: var(--accent); background: var(--bg-soft); }
.mp-mark {
    font-size: 3.2rem; font-weight: 700; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
}
.mp-x { color: #f472b6; }
.mp-o { color: #38bdf8; }
.mp-mark.mp-last { animation: mp-pop .2s ease-out; }
@keyframes mp-pop { from { transform: scale(.4); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.mp-mark.mp-oldest { opacity: .3; }        /* en infini : disparaîtra au prochain remplissage */
.mp-mark.mp-win { text-shadow: 0 0 16px currentColor; animation: mp-win-pulse 1s ease-in-out infinite; }
@keyframes mp-win-pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.15); } }
.mp-mark.mp-removed { animation: mp-fade-out .5s ease-in forwards; }
@keyframes mp-fade-out { from { opacity: .5; transform: scale(1); } to { opacity: 0; transform: scale(.5); } }

.mp-legend { display: flex; gap: 22px; justify-content: center; align-items: center; flex-wrap: wrap; }
.mp-legend-item { display: flex; align-items: center; gap: 8px; color: var(--muted); }
.mp-legend-item b { color: var(--text); font-weight: 600; }
.mp-legend .mp-mark { font-size: 1.35rem; }
.mp-board-end { pointer-events: none; }

/* Compteur de victoires (Morpion, Puissance 4) */
.mp-wins {
    min-width: 22px; padding: 1px 7px; border-radius: 999px;
    background: var(--bg-soft); border: 1px solid var(--border);
    font-size: .8rem; font-weight: 700; color: var(--accent); text-align: center;
}
.mp-score {
    text-align: center; font-weight: 700; font-size: 1.1rem;
    color: var(--muted); margin-bottom: 16px;
}

/* ---------- Uno ---------- */
.uno-topbar { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-bottom: 16px; }
.mode-toggle-3 { flex-wrap: wrap; }
.mode-toggle-3 .mode-btn { min-width: 92px; }
.uno-badge { background: #ef4444; color: #fff; font-size: .64rem; font-weight: 800; padding: 1px 6px; border-radius: 6px; letter-spacing: 1px; }

/* Fond de terrain (voir dev_set_terrain dans uno.py) : agrandi (624px ->
   900px) par rapport à la version qui ne contenait que les 2 piles — la
   main et les adversaires vivent maintenant À L'INTÉRIEUR du terrain (voir
   .hand-row / .opp-stack plus bas) plutôt qu'en dehors, donc tout l'espace
   auparavant occupé par .uno-players/.uno-hand-wrap revient au terrain. */
.uno-center {
    position: relative;
    width: min(900px, 96vw); aspect-ratio: 4 / 3; margin: 6px auto 16px;
    border-radius: 20px; background-color: var(--bg-soft);
    background-size: cover; background-position: center; overflow: hidden;
}
.uno-center .stage {
    position: absolute; inset: 0; z-index: 2;
    display: flex; gap: 26px; align-items: center; justify-content: center;
}
.uno-pile { position: relative; }
.uno-card { width: 90px; height: auto; border-radius: 9px; display: block; box-shadow: 0 3px 8px rgba(0,0,0,.4); }
.uno-draw.clickable { cursor: pointer; }
.uno-draw.clickable:hover .uno-card { transform: translateY(-3px); }
.uno-deck-count { position: absolute; inset: 0; display: grid; place-items: center; font-weight: 800; font-size: 1.35rem; color: #fff; text-shadow: 0 1px 5px #000; pointer-events: none; }
.uno-discard { padding: 3px; perspective: 600px; }
.uno-discard .uno-card.uno-card-drop { animation: uno-card-drop .32s cubic-bezier(.2,.8,.3,1.15); }
@keyframes uno-card-drop {
    from { transform: scale(.5) rotate(-14deg); opacity: 0; }
    to { transform: scale(1) rotate(0); opacity: 1; }
}
/* Joker classique -> version teintée une fois la couleur choisie : un vrai
   flip (la carte passe "de profil", son src change à ce moment précis dans
   le JS, puis elle se retourne côté couleur), pas juste un zoom/fondu sur
   l'image finale — sinon on dirait que la carte disparaît/réapparaît. */
.uno-discard .uno-card.uno-card-tint { animation: uno-card-tint .5s ease-in-out; }
@keyframes uno-card-tint {
    0% { transform: rotateY(0deg); filter: brightness(1); }
    40%, 50% { transform: rotateY(90deg); filter: brightness(1.6); }
    100% { transform: rotateY(0deg); filter: brightness(1); }
}

/* Positionné en absolu, EN DEHORS de .stage : y mettre les boutons comme 3e
   élément du flex centré décalait le centrage de la pioche/défausse (leur
   centre à elles deux glissait pour laisser de la place aux boutons). Calé
   sur le bord droit du duo pioche+défausse (206px à elles deux : 2 × 90px
   de carte + 26px de gap), sans toucher à leur centrage. Empile "Passer" /
   "Uno !" / cibles d'échange ET la barre de contestation, l'un sous
   l'autre plutôt que sur 2 rangées horizontales séparées au-dessus du
   plateau. */
.uno-side-actions {
    position: absolute; top: 50%; left: calc(50% + 123px);
    transform: translateY(-50%); z-index: 6;
    display: flex; flex-direction: column; gap: 10px; align-items: flex-start;
}
.uno-actions { display: flex; flex-direction: column; gap: 10px; }
.btn-uno { background: linear-gradient(160deg, #facc15, #ef4444); color: #fff; font-weight: 800; letter-spacing: 1px; border: none; white-space: nowrap; }

/* Passer / Dénoncer : anciennement de simples .btn-ghost, quasi invisibles
   à côté de "Uno !"/la barre de contestation — Passer reste sobre mais
   bien contourée (action fréquente, pas besoin de clignoter) ; Dénoncer
   reprend le traitement pulsant de .uno-catch-btn ci-dessous (même idée :
   une décision ponctuelle à ne pas manquer). */
.uno-pass-btn {
    background: var(--panel-2); border: 1.5px solid var(--accent); color: var(--accent);
    font-weight: 700; white-space: nowrap;
}
.uno-pass-btn:hover { background: rgba(56,189,248,.14); }
.uno-denounce-btn {
    background: linear-gradient(160deg, #f59e0b, #ef4444); color: #fff; font-weight: 700; border: none;
    box-shadow: 0 4px 14px rgba(239,68,68,.4); animation: uno-catch-pulse 1s ease-in-out infinite;
    white-space: nowrap;
}

/* Barre de contestation Uno (bien visible) */
.uno-catch-bar { display: flex; flex-direction: column; gap: 10px; }
.uno-catch-btn {
    background: linear-gradient(160deg, #f59e0b, #ef4444); color: #fff; font-weight: 700; border: none;
    box-shadow: 0 4px 14px rgba(239,68,68,.4); animation: uno-catch-pulse 1s ease-in-out infinite;
    white-space: nowrap;
}
@keyframes uno-catch-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.04); box-shadow: 0 6px 20px rgba(239,68,68,.6); } }

/* Pop au clic (UNO ! / Contre-UNO !) : .uno-catch-btn a déjà une animation
   permanente (la pulsation ci-dessus, sur transform + box-shadow) — le pop
   ne doit PAS toucher ces deux propriétés, sinon `animation` (raccourci,
   une seule valeur par élément) écraserait la pulsation au lieu de s'y
   ajouter. Le flash (filter) tourne donc en parallèle, sans conflit. */
.uno-side-actions button { position: relative; }
.uno-side-actions button::after {
    content: ""; position: absolute; inset: -3px; border-radius: inherit;
    border: 2px solid rgba(255,255,255,.85); opacity: 0; pointer-events: none;
}
@keyframes uno-btn-ring {
    0% { transform: scale(1); opacity: .85; }
    100% { transform: scale(1.45); opacity: 0; }
}
.uno-side-actions button.pop::after { animation: uno-btn-ring .5s ease-out; }
@keyframes uno-btn-pop {
    0% { transform: scale(1); filter: brightness(1); }
    35% { transform: scale(1.16); filter: brightness(1.5); }
    60% { transform: scale(.96); }
    100% { transform: scale(1); filter: brightness(1); }
}
.btn-uno.pop { animation: uno-btn-pop .4s cubic-bezier(.25,.8,.3,1.3); }
@keyframes uno-btn-flash {
    0% { filter: brightness(1); }
    35% { filter: brightness(1.7); }
    100% { filter: brightness(1); }
}
.uno-catch-btn.pop { animation: uno-catch-pulse 1s ease-in-out infinite, uno-btn-flash .4s ease-out; }
.uno-denounce-btn.pop { animation: uno-catch-pulse 1s ease-in-out infinite, uno-btn-flash .4s ease-out; }
.uno-pass-btn.pop { animation: uno-btn-pop .4s cubic-bezier(.25,.8,.3,1.3); }

/* Message qui s'affiche au clic (UNO ! / Contre-UNO !), par-dessus toute la
   table (pointer-events:none) : plus lisible pour TOUS les joueurs autour
   de la table que juste un agrandissement du bouton. */
.uno-toast {
    position: absolute; top: 50%; left: 50%; z-index: 30;
    pointer-events: none; opacity: 0;
    font-weight: 900; font-size: 2.6rem; letter-spacing: 1px;
    text-shadow: 0 4px 18px rgba(0,0,0,.65); white-space: nowrap;
}
.uno-toast.kind-uno {
    background: linear-gradient(160deg, #fde047, #ef4444);
    -webkit-background-clip: text; background-clip: text; color: transparent;
    -webkit-text-stroke: 2px rgba(255,255,255,.5);
}
.uno-toast.kind-catch { color: #fff; text-shadow: 0 0 16px rgba(239,68,68,.9), 0 4px 18px rgba(0,0,0,.65); }
.uno-toast.kind-challenge-win {
    background: linear-gradient(160deg, #38bdf8, #34d399);
    -webkit-background-clip: text; background-clip: text; color: transparent;
    -webkit-text-stroke: 2px rgba(255,255,255,.5);
}
.uno-toast.kind-challenge-fail { color: #fff; text-shadow: 0 0 16px rgba(239,68,68,.9), 0 4px 18px rgba(0,0,0,.65); }
.uno-toast.show.kind-uno { animation: uno-toast-pop 1.3s cubic-bezier(.2,.8,.2,1) forwards; }
.uno-toast.show.kind-catch { animation: uno-toast-shake 1.3s cubic-bezier(.2,.8,.2,1) forwards; }
.uno-toast.show.kind-challenge-win { animation: uno-toast-pop 1.3s cubic-bezier(.2,.8,.2,1) forwards; }
.uno-toast.show.kind-challenge-fail { animation: uno-toast-shake 1.3s cubic-bezier(.2,.8,.2,1) forwards; }
@keyframes uno-toast-pop {
    0% { transform: translate(-50%, -50%) scale(.3) rotate(-8deg); opacity: 0; }
    20% { transform: translate(-50%, -50%) scale(1.15) rotate(4deg); opacity: 1; }
    32% { transform: translate(-50%, -50%) scale(1) rotate(0deg); opacity: 1; }
    75% { transform: translate(-50%, -50%) scale(1) rotate(0deg); opacity: 1; }
    100% { transform: translate(-50%, -60%) scale(.92) rotate(0deg); opacity: 0; }
}
@keyframes uno-toast-shake {
    0% { transform: translate(-50%, -50%) scale(.4); opacity: 0; }
    18% { transform: translate(-50%, -50%) scale(1.08); opacity: 1; }
    26% { transform: translate(-53%, -50%) scale(1); }
    34% { transform: translate(-47%, -50%) scale(1); }
    42% { transform: translate(-51%, -50%) scale(1); }
    50% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
    80% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
    100% { transform: translate(-50%, -58%) scale(.92); opacity: 0; }
}

/* ---------- Adversaires : petit paquet groupé + badge, autour du terrain
   ----------
   Remplace les anciennes .uno-players/.uno-player (rangée de cases
   au-dessus du plateau) : juste quelques dos de carte serrés en petit
   paquet à côté du nom (inspiré d'Uno officiel — capture de référence),
   positionnés SUR le terrain plutôt qu'au-dessus. */
.uno-opponents { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.opp-stack { position: absolute; z-index: 3; text-align: left; transition: opacity .2s; pointer-events: auto; }
.opp-stack.center { transform: translateX(-50%); }
.opp-stack.align-right { text-align: right; }
.opp-stack.align-right .opp-tag { flex-direction: row-reverse; padding: 4px 4px 4px 10px; }
.opp-stack.eliminated { opacity: .45; }
.opp-tag {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: .76rem; font-weight: 700; color: rgba(255,255,255,.92);
    text-shadow: 0 1px 4px rgba(0,0,0,.85);
    background: rgba(10,14,24,.5); padding: 4px 10px 4px 4px; border-radius: 999px;
    margin-bottom: 10px; white-space: nowrap;
}
.opp-avatar {
    width: 22px; height: 22px; border-radius: 50%; background: var(--accent);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .7rem; color: #1a1206; font-weight: 800; flex: none;
}
.opp-avatar.has-badge { background: transparent; }
.opp-avatar.has-badge img { width: 100%; height: 100%; object-fit: contain; filter: drop-shadow(0 0 4px color-mix(in srgb, var(--tc) 65%, transparent)); }
.opp-avatar.is-zoomable { cursor: zoom-in; }
/* Vies ET pile de cartes toujours centrées sous le pseudo (.opp-stack se
   dimensionne sur son enfant le plus large, en général .opp-tag) quel que
   soit le siège — plutôt que collées à gauche/droite comme le pseudo
   lui-même, ce qui les décale visiblement dès que le pseudo est plus large
   qu'elles (le cas le plus courant, pas seulement en 1v1 centré). */
.opp-cards { position: relative; width: 60px; height: 78px; margin-left: auto; margin-right: auto; }
.opp-card {
    position: absolute; left: 0; top: 0; width: 46px;
    transform: translate(var(--tx), var(--ty)) rotate(var(--rot));
    border-radius: 6px; overflow: hidden; box-shadow: 0 4px 10px rgba(0,0,0,.5);
}
.opp-card img { width: 100%; display: block; }
.opp-count {
    position: absolute; right: -8px; bottom: -8px; z-index: 10;
    background: var(--accent); color: #1a1206; font-weight: 800; font-size: .72rem;
    border-radius: 50%; width: 24px; height: 24px;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 3px 6px rgba(0,0,0,.4); border: 2px solid var(--bg);
}
.opp-count.danger { background: #ef4444; color: #fff; }
.opp-count.warn { background: #f97316; color: #fff; }
.opp-stack.align-right .opp-count { right: auto; left: -8px; }

/* ---------- Mode spectateur : révéler la main d'un joueur (Uno) ---------- */
.opp-stack.spectator-peek { cursor: pointer; }
.opp-stack.spectator-peek:hover .opp-tag { background: rgba(56,189,248,.25); }
.opp-stack.spectator-peek.is-revealed .opp-tag { background: rgba(56,189,248,.3); }
.opp-peek-hint { opacity: .85; font-size: .8rem; }
.opp-cards-revealed {
    position: static; width: auto; height: auto; max-width: 230px;
    display: flex; flex-wrap: wrap; justify-content: center; gap: 3px;
}
.opp-card-real { width: 30px; border-radius: 4px; display: block; box-shadow: 0 2px 6px rgba(0,0,0,.5); }
.opp-meta {
    display: block; margin-top: 10px; font-size: .68rem; font-weight: 600;
    color: rgba(255,255,255,.85); text-shadow: 0 1px 4px rgba(0,0,0,.85);
    background: rgba(10,14,24,.4); padding: 2px 8px; border-radius: 999px;
    white-space: nowrap;
}

/* ---------- Halo du joueur actif ----------
   Appliqué à CHAQUE carte (ni .hand-row ni .opp-cards n'ont de boîte
   visible propre : leurs enfants sont tous en position absolute) — l'effet
   cumulé sur toutes les cartes d'une main donne un halo autour de "la
   main" dans son ensemble. Pulsation légère, volontairement discrète. */
@keyframes turn-glow-pulse {
    0%, 100% { filter: drop-shadow(0 0 1px rgba(255,214,90,.45)) drop-shadow(0 0 4px rgba(255,190,40,.28)); }
    50% { filter: drop-shadow(0 0 2px rgba(255,224,130,.55)) drop-shadow(0 0 6px rgba(255,190,40,.4)); }
}
.hand-row.is-turn .fan-card-lift,
.opp-stack.is-turn .opp-card {
    animation: turn-glow-pulse 2.2s ease-in-out infinite;
}
.opp-stack.is-turn .opp-tag {
    box-shadow: 0 0 0 1px rgba(255,200,60,.6), 0 0 6px rgba(255,190,40,.4);
}

/* ---------- Ma main : rangée qui se chevauche, À L'INTÉRIEUR du terrain
   ----------
   Inspiré d'Uno officiel : pas un grand éventail courbé — les cartes sont
   presque à plat, à peine tournées, serrées les unes contre les autres
   (translateX), avec une très légère courbe (translateY). Chaque carte
   garde sa propre valeur --tx/--ty/--rot/--spread posée en JS. */
.hand-row { position: absolute; left: 50%; bottom: 3%; z-index: 4; }
/* Message de remplacement (main vide / mode spectateur) : contrairement aux
   cartes (chacune positionnée via --tx autour de l'ancre gauche à 50%),
   c'est un enfant normal du flux — sans ceci il démarre pile À l'ancre au
   lieu d'être centré dessus, et déborde visiblement à droite. */
.hand-row > .muted { position: absolute; left: 0; bottom: 0; transform: translateX(-50%); white-space: nowrap; }
.uno-my-tag {
    position: absolute; left: 3%; bottom: 3%; z-index: 4;
    display: inline-flex; align-items: center; gap: 8px;
    font-size: .78rem; font-weight: 700; color: rgba(255,255,255,.92);
    text-shadow: 0 1px 4px rgba(0,0,0,.85);
    background: rgba(10,14,24,.5); padding: 5px 12px; border-radius: 999px;
}

/* .fan-card (externe, position dans la rangée, jamais transformé au
   survol) et .fan-card-lift (interne, translateY pur) sont volontairement
   deux éléments séparés :
   1) transformer l'élément qui positionne la carte dans la rangée (via
      --tx/--rot) risquait, avec un scale ou une translation supplémentaire
      au même endroit, de faire sauter la carte hors du curseur au survol
      (aller-retour hover in/out en boucle observé lors des essais).
   2) le survol ne doit JAMAIS changer le z-index (elle ne doit pas passer
      au premier plan, seulement se soulever un peu) — seules les DEUX
      voisines immédiates s'écartent (--spread, JS) pour lui faire un peu
      de place, sans effet qui se propage plus loin dans la main. */
.fan-card {
    position: absolute; left: 0; bottom: 0;
    transform: translate(calc(var(--tx) + var(--spread, 0px)), var(--ty)) rotate(var(--rot));
    transform-origin: 50% 100%;
    transition: transform .18s ease;
}
.fan-card-lift {
    display: block; border-radius: 8px; overflow: hidden;
    box-shadow: 0 6px 14px rgba(0,0,0,.55);
    transition: transform .15s ease, box-shadow .15s ease, filter .15s ease;
}
.hand-row .fan-card img.uno-card { width: 100%; box-shadow: none; border-radius: 0; }
.fan-card.playable { cursor: pointer; }
/* Le survol soulève TOUTES les cartes (jouables ou non) — seule la carte
   jouable gagne en plus un léger boost de luminosité ; une carte grisée
   (.dim) garde son filtre grayscale/brightness réduit même survolée, ces
   deux règles ne touchant pas la même chose (transform/box-shadow ici,
   filter là-bas). */
.fan-card:hover .fan-card-lift {
    transform: translateY(-22px);
    box-shadow: 0 14px 24px rgba(0,0,0,.6);
}
.fan-card.playable:hover .fan-card-lift { filter: brightness(1.08); }
/* filter (pas opacity) : les cartes non jouables se chevauchent dans
   l'éventail — une opacité réduite laisse voir la carte du dessous À
   TRAVERS, ce qui rendait le paquet trouble/illisible dès que 2 cartes
   grisées se touchaient. Un filtre garde chaque carte pleinement opaque
   (donc elle masque toujours proprement celle qu'elle recouvre), juste
   assombrie/désaturée. */
.fan-card.dim .fan-card-lift { filter: grayscale(.55) brightness(.55); }

.uno-colorpick { position: fixed; inset: 0; z-index: 120; background: rgba(4,8,16,.72); backdrop-filter: blur(2px); display: flex; align-items: flex-start; justify-content: center; padding-top: 7vh; }
.uno-colorpick.hidden { display: none; }
.uno-colorpick-inner { background: var(--panel); border: 1px solid var(--border); border-radius: 18px; padding: 24px 28px; text-align: center; }
.uno-colorpick-inner p { font-weight: 600; margin-bottom: 16px; }
.uno-color-btns { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.uno-color-btn { width: 78px; height: 78px; border-radius: 14px; border: 3px solid rgba(255,255,255,.25); cursor: pointer; transition: transform .1s; }
.uno-color-btn:hover { transform: scale(1.08); }
.c-red { background: #ef4444; } .c-yellow { background: #facc15; } .c-green { background: #22c55e; } .c-blue { background: #3b82f6; }

/* Barre de contestation Uno (bien visible) */
.uno-catch-bar { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-bottom: 14px; }
.uno-catch-btn {
    background: linear-gradient(160deg, #f59e0b, #ef4444); color: #fff; font-weight: 700; border: none;
    box-shadow: 0 4px 14px rgba(239,68,68,.4); animation: uno-catch-pulse 1s ease-in-out infinite;
}
@keyframes uno-catch-pulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.04); box-shadow: 0 6px 20px rgba(239,68,68,.6); } }

/* Carte volante (pioche) */
.uno-fly {
    position: fixed; z-index: 130; border-radius: 9px; pointer-events: none;
    box-shadow: 0 6px 16px rgba(0,0,0,.5); will-change: transform, opacity;
}

/* Flash « UNO ! » / « CONTRE-UNO ! » */
.uno-flash {
    position: fixed; inset: 0; z-index: 140; display: grid; place-items: center;
    pointer-events: none; font-weight: 900; letter-spacing: 3px;
    font-size: clamp(2.4rem, 12vw, 6rem); text-align: center;
    animation: uno-flash-anim 1.4s cubic-bezier(.2,.8,.3,1) forwards;
    text-shadow: 0 4px 24px rgba(0,0,0,.6);
}
.uno-flash.flash-uno { color: #facc15; }
.uno-flash.flash-catch { color: #f87171; }
.uno-flash.flash-flip { color: #f97316; }
.uno-flash.flash-freeze { color: #38bdf8; }
.uno-flash.flash-flip3 { color: #fb7185; }

/* ---------- Uno Flip ---------- */
#game[data-side="dark"] .uno-topbar .pill { background: #0f131b; border-color: #2a3444; }
.uf-flipping { animation: uf-flip .65s ease-in-out; }
@keyframes uf-flip {
    0% { transform: perspective(1200px) rotateY(0); }
    50% { transform: perspective(1200px) rotateY(90deg); opacity: .55; }
    100% { transform: perspective(1200px) rotateY(0); }
}
@keyframes uno-flash-anim {
    0%   { opacity: 0; transform: scale(.4) rotate(-8deg); }
    18%  { opacity: 1; transform: scale(1.1) rotate(-3deg); }
    35%  { transform: scale(1) rotate(0); }
    80%  { opacity: 1; transform: scale(1); }
    100% { opacity: 0; transform: scale(1.15); }
}

/* ---------- 5 Alive ---------- */
.fa-meta { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }

/* Fond de terrain (voir dev_set_terrain dans fivealive.py) : même gabarit
   que .uno-center — la main et les adversaires vivent À L'INTÉRIEUR du
   terrain (voir .hand-row / .opp-stack, génériques, partagés avec Uno). */
.fa-center {
    position: relative;
    width: min(900px, 96vw); aspect-ratio: 4 / 3; margin: 6px auto 16px;
    border-radius: 20px; background-color: var(--bg-soft);
    background-size: cover; background-position: center; overflow: hidden;
}
.fa-opponents { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.fa-my-tag {
    position: absolute; left: 3%; bottom: 3%; z-index: 4;
    display: inline-flex; align-items: center; gap: 8px;
    font-size: .78rem; font-weight: 700; color: rgba(255,255,255,.92);
    text-shadow: 0 1px 4px rgba(0,0,0,.85);
    background: rgba(10,14,24,.5); padding: 5px 12px; border-radius: 999px;
}

/* Centre : compteur au-dessus, pioche + défausse dessous. Seul
   .fa-piles-row est centré dans .fa-stage (flex à un seul enfant visible) :
   .fa-total est sorti du flux (position absolute, ancré au-dessus de
   .fa-piles-row) pour ne PAS peser dans le calcul du centrage — sinon c'est
   le duo total+piles qui se centre comme un bloc, et les piles se
   retrouvent décalées sous le vrai centre du terrain. */
.fa-stage { position: absolute; inset: 0; z-index: 2; display: flex; align-items: center; justify-content: center; }
.fa-piles-row { position: relative; display: flex; align-items: center; justify-content: center; gap: 18px; }
/* Largeur/ratio fixes (pas seulement sur .fa-draw-img/.fa-last-img) : sans
   ça, #fa-last vide (le temps de l'animation de pose, voir animatePlay dans
   fivealive.js — la vraie carte n'y est posée qu'à l'arrivée du vol) fait
   0×0, .fa-piles-row (flex, centré) rétrécit puis se re-centre d'un coup
   dès que la carte arrive : la pioche visible à côté sautait sur le côté. */
.fa-pile { position: relative; text-align: center; width: 83px; aspect-ratio: 244 / 380; }
.fa-draw-img, .fa-last-img { width: 83px; border-radius: 10px; box-shadow: 0 3px 8px rgba(0,0,0,.4); display: block; margin: 0 auto; }
.fa-draw-count { position: absolute; inset: 0; display: grid; place-items: center; font-weight: 800; font-size: 1.21rem; color: #fff; text-shadow: 0 1px 5px #000; pointer-events: none; }

.fa-total {
    position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: 10px;
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    background: rgba(10,14,24,.55); border: 1px solid rgba(255,255,255,.14);
    border-radius: 12px; padding: 7px 14px; white-space: nowrap;
    transition: border-color .2s, box-shadow .2s;
}
.fa-total.hot { border-color: #f59e0b; box-shadow: 0 0 0 3px rgba(245,158,11,.18); }
.fa-total.danger { border-color: #ef4444; box-shadow: 0 0 0 3px rgba(239,68,68,.22); }
.fa-total-num { font-weight: 900; line-height: 1; display: flex; align-items: baseline; gap: 5px; color: #fff; text-shadow: 0 1px 6px rgba(0,0,0,.6); }
.fa-total-num b { font-size: 1.5rem; font-variant-numeric: tabular-nums; }
.fa-total-num span { font-size: .68rem; color: rgba(255,255,255,.65); font-weight: 600; }
.fa-gauge { width: 80px; height: 5px; border-radius: 99px; background: rgba(255,255,255,.15); overflow: hidden; }
.fa-gauge-fill { display: block; height: 100%; width: 0; border-radius: inherit; background: linear-gradient(90deg, #4ade80, #facc15, #ef4444); transition: width .3s; }

/* Vies (voir livesEl) : ma propre étiquette ET les piles adverses réutilisent
   les mêmes classes, juste réduites dans ces contextes compacts. */
.fa-lives { display: flex; gap: 3px; justify-content: center; min-height: 24px; flex-wrap: wrap; }
.fa-life-img { width: 20px; height: auto; border-radius: 3px; }
.fa-life-img.lost { filter: grayscale(1); opacity: .32; }
.fa-life-img.flip { animation: fa-life-flip .55s ease; }
@keyframes fa-life-flip {
    0% { transform: rotateY(0) scale(1); }
    45% { transform: rotateY(90deg) scale(1.35); }
    100% { transform: rotateY(0) scale(1); }
}
.opp-stack .fa-lives { min-height: 0; gap: 2px; margin-bottom: 6px; }
.fa-my-tag .fa-lives { min-height: 0; gap: 2px; margin-bottom: 0; }
.opp-stack .fa-life-img, .fa-my-tag .fa-life-img { width: 12px; border-radius: 2px; box-shadow: 0 1px 3px rgba(0,0,0,.5); }

/* Carte volante (pioche / redistribution) : dos de carte cloné en position
   fixed, largeur fixée dynamiquement en JS (voir flyCard, comme .uno-fly)
   pour matcher la source plutôt qu'une taille figée ici. */
.fa-fly { position: fixed; border-radius: 8px; z-index: 130; pointer-events: none; box-shadow: 0 6px 16px rgba(0,0,0,.5); will-change: transform, opacity; }

/* Ma main : .hand-row/.fan-card/.fan-card-lift sont génériques (voir Uno) —
   seul le style propre à l'image 5 Alive reste ici. */
.hand-row .fan-card img.fa-card-img { width: 100%; box-shadow: none; border-radius: 0; }
/* Révélation carte par carte lors d'une redistribution (voir revealTimes
   dans renderPlay) : la carte apparaît (pop) au lieu d'être visible
   d'emblée — sur .fan-card-lift (pas .fan-card, dont le transform est déjà
   pris par le positionnement --tx/--ty/--rot). */
.fan-card.fa-card-hidden .fan-card-lift {
    opacity: 0; transform: scale(.3) translateY(-40px);
    transition: transform .22s cubic-bezier(.2,.9,.3,1.25), opacity .22s;
}

@media (max-width: 820px) {
    .panel-duo, .boards, .setup-grid { grid-template-columns: 1fr; }
    .setup-grid { gap: 20px; }
    .die-toss, .die3d-wrap, .die3d-scene { width: 46px; height: 46px; }
    .die-toss { margin: -23px 0 0 -23px; }
    .dface-front, .dface-back, .dface-right, .dface-left, .dface-top, .dface-bottom { transform: none; }
    .dface-front  { transform: translateZ(23px); }
    .dface-back   { transform: rotateY(180deg) translateZ(23px); }
    .dface-right  { transform: rotateY(90deg) translateZ(23px); }
    .dface-left   { transform: rotateY(-90deg) translateZ(23px); }
    .dface-top    { transform: rotateX(90deg) translateZ(23px); }
    .dface-bottom { transform: rotateX(-90deg) translateZ(23px); }
    .tcard { width: 52px; }
    .tcard-sm { width: 42px; }
    .sp-card { width: 52px; }
    .sp-slot { width: 52px; }
    .sp-hand-card { width: 66px; }
    .sp-reveal-card { width: 52px; }
    .mp-board { max-width: 280px; }
    .mp-mark { font-size: 2.6rem; }
    .uno-card { width: 72px; }
    .uno-hand .uno-card { width: 58px; }
}

/* ============ Piña Pirata ============ */
.pp-meta { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-bottom: 14px; }
/* Infos joueurs à droite du terrain (flex-wrap : retombe naturellement sous
   le terrain si la fenêtre est trop étroite pour les deux côte à côte). */
.pp-layout { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
.pp-layout .pp-table { flex: 1 1 460px; min-width: 280px; margin-bottom: 0; }
.pp-players { display: flex; flex-direction: column; gap: 12px; flex: 0 0 168px; margin-bottom: 12px; }
.pp-player { background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08);
    border-radius: 14px; padding: 10px 12px; width: 100%; text-align: center; transition: .2s; box-sizing: border-box; }
.pp-player.current { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(249,115,22,.25); }
.pp-player.you { background: rgba(249,115,22,.08); }
.pp-pname { font-weight: 600; font-size: .9rem; margin-bottom: 8px; }
.pp-pmeta { display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 8px; font-size: .85rem; }
.pp-wins { opacity: .85; }
.pp-front { display: flex; gap: 3px; justify-content: center; flex-wrap: wrap; margin-top: 8px;
    padding-top: 6px; border-top: 1px dashed rgba(255,255,255,.12); }
.pp-front-card { width: 30px; border-radius: 4px; cursor: zoom-in; box-shadow: 0 1px 4px rgba(0,0,0,.5); }
.pp-revealed { border-top-color: rgba(249,115,22,.4); }
.pp-draw-img.faceup { outline: 2px solid var(--accent); }

/* Carte au trésor (2×2) */
.pp-treasure { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr;
    gap: 2px; width: 72px; height: 72px; margin: 0 auto; background: rgba(0,0,0,.25);
    padding: 3px; border-radius: 8px; }
.pp-treasure-cell { display: flex; align-items: center; justify-content: center; overflow: hidden;
    border-radius: 4px; background: rgba(255,255,255,.05); color: rgba(255,255,255,.28);
    font-size: .7rem; font-weight: 700; border: 1px dashed rgba(255,255,255,.14); }
.pp-treasure-cell.got { border: none; }
.pp-treasure-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Éventail de dos de cartes (compteur de main) */
.pp-fan { position: relative; display: inline-flex; align-items: center; height: 34px; overflow: visible; }
.pp-fan-back { position: absolute; width: 16px; height: auto; border-radius: 3px;
    box-shadow: 0 1px 3px rgba(0,0,0,.5); top: 50%; transform: translateY(-50%); }
.pp-fan-count { position: relative; margin-left: 48px; font-weight: 700; line-height: 34px; }

/* Fond de terrain (voir applyTerrain()/renderTerrainPicker() dans
   pina_pirata.js) : englobe les tuiles Aventure (en haut) et la
   pioche/défausse (en dessous) sur une même image, plutôt que chacune sa
   propre vignette — hauteur non contrainte (nombre de tuiles Aventure
   variable), contrairement au damier de Bataille navale ou au terrain de
   dés du Yams qui ont un contenu de taille fixe. */
.pp-table {
    position: relative;
    border-radius: 18px;
    border: 1px solid var(--border);
    background-color: var(--bg-soft);
    background-size: cover;
    background-position: center;
    padding: 42px 16px 48px;
    margin-bottom: 18px;
}

/* Tuiles Aventure */
.pp-adv-wrap { text-align: center; margin: 6px 0 14px; }
.pp-adventures { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 6px; }
.pp-adv-cell { position: relative; display: inline-flex; }
.pp-adv { width: 66px; height: 66px; object-fit: cover; border-radius: 10px; cursor: zoom-in;
    border: 1px solid rgba(255,255,255,.12); box-shadow: 0 2px 6px rgba(0,0,0,.4); transition: .15s; }
.pp-adv:hover { transform: translateY(-3px); box-shadow: 0 8px 16px rgba(0,0,0,.5); }
.pp-adv-cell.pending .pp-adv { filter: grayscale(.6) brightness(.85); }
.pp-adv-tag { position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
    background: rgba(0,0,0,.72); color: #f5cba7; font-size: .58rem; font-weight: 700;
    padding: 1px 6px; border-radius: 999px; letter-spacing: .03em; pointer-events: none; white-space: nowrap; }

/* Pile / pioche */
.pp-stage { display: flex; gap: 26px; justify-content: center; align-items: flex-end; margin: 8px 0 18px; }
.pp-draw { position: relative; width: 110px; cursor: default; }
.pp-draw.clickable { cursor: pointer; }
.pp-draw.clickable:hover .pp-draw-img { box-shadow: 0 10px 20px rgba(0,0,0,.55); transform: translateY(-4px); }
.pp-draw-img { width: 100%; display: block; border-radius: 10px; box-shadow: 0 3px 8px rgba(0,0,0,.45); transition: .15s; }
.pp-draw-count { position: absolute; bottom: -8px; right: -8px; background: var(--accent); color: #1a1206;
    font-weight: 800; font-size: .8rem; border-radius: 999px; padding: 2px 8px; box-shadow: 0 2px 5px rgba(0,0,0,.4); }
.pp-pile { text-align: center; }
.pp-pile-lbl { display: block; font-size: .72rem; text-transform: uppercase; letter-spacing: .08em;
    opacity: .55; margin-bottom: 6px; }
.pp-top { width: 138px; }
.pp-top.pp-two { width: auto; display: flex; gap: 14px; justify-content: center; }
.pp-top.pp-two .pp-pile-slot { width: 120px; }
.pp-pile-tag { display: block; font-size: .68rem; font-weight: 700; opacity: .6; margin-bottom: 4px; }
.pp-pile-target { cursor: pointer; outline: 3px solid var(--accent); outline-offset: 2px;
    border-radius: 10px; animation: pp-pulse 1s infinite; }
@keyframes pp-pulse { 0%,100% { outline-color: var(--accent); } 50% { outline-color: rgba(249,115,22,.4); } }

/* Cartes */
.pp-card { width: 100%; border-radius: 10px; transition: transform .18s; }
.pp-card-img { width: 100%; height: auto; display: block; border-radius: 10px; box-shadow: 0 3px 8px rgba(0,0,0,.4); }
.pp-card.playable { cursor: pointer; }
.pp-card.playable:hover { transform: translateY(-12px); }
.pp-card.playable:hover .pp-card-img { box-shadow: 0 14px 22px rgba(0,0,0,.5); outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 10px; }
.pp-card.dim { opacity: .5; cursor: zoom-in; }

/* Main */
.pp-hand-wrap { margin-top: 6px; }
.pp-hand-head { display: flex; align-items: center; justify-content: center; gap: 14px; }
.pp-hand { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 10px; }
.pp-hand .pp-card { width: 118px; }

.pp-take-lbl { width: 100%; text-align: center; margin-bottom: 6px; }
.pp-joker-btn { margin: 3px; }
.pp-adv-pick { display: inline-flex; flex-direction: column; align-items: center; gap: 6px; margin: 4px; }
.pp-confirm-row { display: flex; gap: 8px; }
.pp-decline-btn { align-self: center; }
.pp-adv-choice { width: 112px; height: 112px; cursor: zoom-in; }
.pp-adv-choice:hover { transform: translateY(-4px); box-shadow: 0 12px 22px rgba(0,0,0,.55); outline: 2px solid var(--accent); }

/* Vol de carte */
.pp-fly { position: fixed; width: 52px; border-radius: 8px; z-index: 60; pointer-events: none;
    box-shadow: 0 8px 18px rgba(0,0,0,.5); }

/* Menu développeur */
.pp-dev-panel { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 95; display: flex;
    align-items: flex-start; justify-content: center; padding: 60px 16px 16px; overflow-y: auto; }
.pp-dev-box { background: #0f172a; color: #e2e8f0; border: 1px solid #334155; border-radius: 14px;
    padding: 18px 20px; width: min(680px, 96vw); box-shadow: 0 20px 50px rgba(0,0,0,.6); }
.pp-dev-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.pp-dev-head h3 { margin: 0; }
.pp-dev-sec { margin-top: 14px; }
.pp-dev-sec h4 { margin: 0 0 8px; font-size: .95rem; }
/* Sélecteur de terrain générique du menu dev — utilisé par plusieurs jeux
   (voir applyTerrain()/renderTerrainPicker() dans yams.js, pina_pirata.js). */
.terrain-picker-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
.terrain-picker-thumb {
    aspect-ratio: 4 / 3; border-radius: 8px; border: 2px solid transparent; cursor: pointer;
    background-size: cover; background-position: center; opacity: .65; transition: opacity .12s, border-color .12s;
    padding: 0;
}
.terrain-picker-thumb:hover { opacity: .9; }
.terrain-picker-thumb-active { opacity: 1; border-color: var(--accent); }
.pp-dev-players { display: flex; gap: 8px; flex-wrap: wrap; }
.pp-dev-advs { display: grid; grid-template-columns: repeat(auto-fill, minmax(58px, 1fr)); gap: 6px;
    max-height: 46vh; overflow-y: auto; padding: 4px; background: rgba(0,0,0,.25); border-radius: 8px; }
.pp-dev-adv { position: relative; display: flex; flex-direction: column; border-radius: 6px; overflow: hidden;
    border: 2px solid transparent; opacity: .6; transition: .12s; }
.pp-dev-adv img { width: 100%; display: block; cursor: zoom-in; }
.pp-dev-toggle { width: 100%; border: none; background: rgba(0,0,0,.6); color: #cbd5e1; font-size: .6rem;
    font-weight: 700; padding: 2px 0; cursor: pointer; }
.pp-dev-adv.sel { opacity: 1; border-color: #f97316; }
.pp-dev-adv.sel .pp-dev-toggle { background: #f97316; color: #1a1206; }
.pp-dev-adv.na::after { content: "à venir"; position: absolute; top: 2px; left: 2px; font-size: .5rem;
    background: rgba(0,0,0,.7); color: #f5cba7; padding: 0 3px; border-radius: 3px; }
.pp-dev-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 10px; }

/* Galerie des cartes règles (accessible depuis "Comment jouer") */
.pp-rules-gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 8px;
    max-height: 70vh; overflow-y: auto; padding: 4px; }
.pp-rules-gallery-grid img { width: 100%; border-radius: 8px; cursor: zoom-in; display: block; }

/* Zoom */
.pp-zoom { position: fixed; inset: 0; background: rgba(0,0,0,.82); display: flex; align-items: center;
    justify-content: center; z-index: 120; cursor: zoom-out; padding: 24px; }
.pp-zoom img { max-width: min(88vw, 460px); max-height: 88vh; border-radius: 14px; box-shadow: 0 20px 50px rgba(0,0,0,.7); }

@media (max-width: 820px) {
    .pp-top { width: 116px; }
    .pp-draw { width: 86px; }
    .pp-hand .pp-card { width: 88px; }
    .pp-adv { width: 54px; height: 54px; }
}

/* ============ Saboteur ============ */
/* Manche/deck/rôle + liste des joueurs à côté du terrain plutôt qu'au-dessus
   (seule la banderole de tour reste au-dessus) : sur une petite partie, ça
   évitait un empilement vertical bien plus haut que nécessaire. Repasse en
   colonne (flex-wrap) si la fenêtre est trop étroite pour les deux côte à
   côte, comme le tableau de score du Yams. */
.sb-layout { display: flex; gap: 20px; align-items: stretch; flex-wrap: wrap; }
.sb-layout .sb-board-wrap { flex: 0 1 auto; margin-bottom: 0; }
.sb-layout .sb-side { flex: 1 1 240px; min-width: 220px; display: flex; flex-direction: column; gap: 12px; }
.sb-layout .sb-side .sb-topbar,
.sb-layout .sb-side .sb-players,
.sb-layout .sb-side .sb-controls,
.sb-layout .sb-side .sb-hand { justify-content: flex-start; margin-bottom: 0; }
/* Toolbar (carte sélectionnée) + main du joueur : groupées tout en bas de la
   colonne (sous les joueurs) plutôt que sous le terrain sur toute la
   largeur — margin-top:auto sur le groupe entier (pas sur .sb-hand-wrap
   seul) pour que la toolbar reste collée juste au-dessus de la main au lieu
   de rester coincée en haut, juste sous les joueurs, quand elle apparaît. */
.sb-layout .sb-side .sb-side-bottom { margin-top: auto; display: flex; flex-direction: column; gap: 12px; }

.sb-topbar { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-bottom: 12px; }
.sb-role-min { background: rgba(180,131,9,.18); border: 1px solid #b45309; }
.sb-role-sab { background: rgba(220,38,38,.18); border: 1px solid #dc2626; }

.sb-players { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-bottom: 10px; }
.sb-player { background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.09); border-radius: 12px;
    padding: 7px 10px; min-width: 104px; text-align: center; transition: .15s; }
.sb-player.current { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(180,83,9,.3); }
.sb-player.you { background: rgba(180,83,9,.08); }
.sb-player.targetable { cursor: pointer; border-color: #f59e0b; box-shadow: 0 0 0 2px rgba(245,158,11,.35); }
.sb-player.targetable:hover { transform: translateY(-2px); }
.sb-pname { font-weight: 600; font-size: .84rem; margin-bottom: 5px; }
.sb-pmeta { display: flex; gap: 10px; justify-content: center; font-size: .8rem; }
.sb-tools { display: flex; gap: 4px; justify-content: center; align-items: center; margin-top: 5px; }
/* Outil cassé : la carte malus (rouge) elle-même plutôt qu'une simple icône
   grisée + croix — bien plus visible d'un coup d'œil, et cohérent avec le
   jeu physique où la carte cassée se pose devant le joueur visé. Les outils
   INTACTS ne montrent plus rien du tout (rien à signaler). */
.sb-malus-card {
    width: 26px; border-radius: 4px; display: block; cursor: pointer;
    box-shadow: 0 0 0 1px #ef4444, 0 2px 8px rgba(239,68,68,.5);
    animation: mp-pop .25s ease-out;
}

/* Le plateau s'adapte toujours à la largeur dispo (cases en 1fr, plafonnées à --sb-cell) :
   aucune scrollbar quand on réduit la fenêtre. */
/* padding-top/bottom : une demi-hauteur de carte de terrain visible au-delà
   de la grille (11 colonnes fixes, ratio carte 1636/2336) — en % car un
   padding vertical se calcule toujours sur la LARGEUR de la boîte en CSS.
   padding-left/right : une carte et demie de terrain visible de chaque côté
   (--sb-cell déplacé ici depuis .sb-board pour que les deux s'en servent). */
.sb-board-wrap { --sb-cell: 66px; --sb-gap: 3px; --sb-cols: 11; max-width: 100%;
    padding: calc(8px + (100% / var(--sb-cols) * 2336 / 1636 / 2)) calc(8px + 1.5 * var(--sb-cell));
    background-color: rgba(0,0,0,.22);
    background-size: cover; background-position: center;
    border-radius: 12px; margin-bottom: 12px; overflow: hidden; }
.sb-board { --sb-ratio: 1636 / 2336;
    display: grid; gap: var(--sb-gap); margin: 0 auto; width: 100%;
    grid-template-columns: repeat(var(--sb-cols), minmax(0, 1fr));
    max-width: calc(var(--sb-cols) * var(--sb-cell) + (var(--sb-cols) - 1) * var(--sb-gap)); }
.sb-cell { width: 100%; aspect-ratio: var(--sb-ratio); border-radius: 4px; position: relative; }
/* Sans ça, la grille se distingue à peine du fond de terrain une fois
   l'image posée derrière — même teinte uniforme sur toutes les cases. */
.sb-cell.empty { background: rgba(0,0,0,.16); border: 1px dashed rgba(255,255,255,.16); }
.sb-cell.valid { border: 2px solid #22c55e; background: rgba(34,197,94,.15); cursor: pointer; }
.sb-cell.valid:hover { background: rgba(34,197,94,.3); }
.sb-cell.targetable { outline: 2px solid #f59e0b; outline-offset: -2px; cursor: pointer; }
.sb-cell.targetable:hover { outline-color: #fbbf24; }
.sb-card-img { width: 100%; height: 100%; object-fit: contain; display: block; border-radius: 4px; }
.sb-cellbox { position: relative; width: 100%; height: 100%; }
.sb-goal-badge { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-size: 1.5rem; background: rgba(0,0,0,.35); border-radius: 4px; }

.sb-controls { display: flex; gap: 10px; align-items: center; justify-content: center; flex-wrap: wrap;
    margin-bottom: 10px; padding: 8px; background: rgba(255,255,255,.03); border-radius: 10px; }

.sb-hand-wrap { margin-top: 4px; }
.sb-hand { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 8px; }
.sb-card { width: 82px; aspect-ratio: 1636 / 2336; border-radius: 6px; cursor: pointer; transition: transform .12s;
    border: 2px solid transparent; }
.sb-card:hover { transform: translateY(-6px); }
.sb-card.selected { border-color: var(--accent); box-shadow: 0 8px 18px rgba(0,0,0,.5); transform: translateY(-8px); }
.sb-gain { color: #22c55e; font-weight: 700; }

@media (max-width: 820px) {
    .sb-card { width: 60px; }
}

/* Saboteur — carte de rôle + révélation animée */
.sb-rolecard { height: 46px; width: auto; border-radius: 5px; cursor: zoom-in; box-shadow: 0 2px 6px rgba(0,0,0,.5);
    border: 1px solid rgba(255,255,255,.15); }
.sb-role-mini { height: 40px; width: auto; border-radius: 4px; margin-right: 8px; cursor: zoom-in;
    box-shadow: 0 1px 4px rgba(0,0,0,.5); vertical-align: middle; }

.sb-reveal { position: fixed; inset: 0; z-index: 130; display: flex; flex-direction: column; gap: 14px;
    align-items: center; justify-content: center; background: rgba(4,8,16,.9); cursor: pointer; padding: 20px;
    animation: fade .25s ease; }
.sb-flip { width: min(60vw, 260px); aspect-ratio: 1636 / 2344; perspective: 1200px; }
.sb-flip-inner { position: relative; width: 100%; height: 100%; transition: transform .8s cubic-bezier(.4,.15,.2,1);
    transform-style: preserve-3d; }
.sb-flip.flipped .sb-flip-inner { transform: rotateY(180deg); }
.sb-flip-face { position: absolute; inset: 0; backface-visibility: hidden; border-radius: 12px; overflow: hidden;
    box-shadow: 0 20px 60px rgba(0,0,0,.7); }
.sb-flip-face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sb-flip-back { transform: rotateY(0deg); }
.sb-flip-front { transform: rotateY(180deg); }
.sb-flip-badge { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-size: 4rem; text-shadow: 0 3px 12px rgba(0,0,0,.6); }
.sb-reveal-label { font-size: 1.15rem; font-weight: 700; text-align: center; margin: 0; }
.sb-reveal-hint { margin: 0; font-size: .85rem; }

/* Saboteur — option "forcer un saboteur" en salon */
.sb-check { display: block; font-size: .88rem; color: var(--text); margin: 6px 0 14px; cursor: pointer; }
.sb-check input { margin-right: 8px; vertical-align: middle; }
.sb-check-hint { display: block; margin: 3px 0 0 24px; font-size: .76rem; }

/* ============ Traîtres à bord ============ */
.tb-meta { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; align-items: center; margin-bottom: 10px; }

/* Fond de terrain (voir dev_set_terrain dans traitres_a_bord.py) — même
   gabarit que .fa-center (5 Alive), qui n'a lui non plus pas de grille à
   afficher. Coffre ET pioche sont positionnés en absolu dedans (comme les
   sièges) plutôt que centrés en flex, pour se placer l'un À CÔTÉ de l'autre
   (retour utilisateur : la pioche doit être identifiable au premier coup
   d'œil, à gauche du Coffre) — voir CHEST_X/CHEST_Y dans traitres_a_bord.js,
   qui doivent rester synchronisés avec le "left" ci-dessous. */
.tb-stage {
    position: relative;
    width: min(700px, 96vw); aspect-ratio: 4 / 3; margin: 6px auto 16px;
    border-radius: 20px; background-color: var(--bg-soft);
    background-size: cover; background-position: center; overflow: hidden;
}
.tb-chest-wrap { position: absolute; z-index: 2; left: 58%; top: 50%; width: 20%; transform: translate(-50%, -50%); }
.tb-chest {
    display: block; width: 100%; height: auto;
    filter: drop-shadow(0 8px 16px rgba(0,0,0,.6)); transition: filter .2s;
}
/* Nombre de cartes DANS le Coffre, directement dessus (plus de chip en haut
   de page — retour utilisateur) : sa vraie valeur n'est jamais montrée, ce
   compteur est public (n'importe qui peut voir des cartes s'empiler). */
.tb-chest-count {
    position: absolute; left: 50%; bottom: -8%; transform: translateX(-50%);
    background: rgba(0,0,0,.75); color: #fff; font-weight: 800; font-size: .72rem;
    border-radius: 999px; padding: 2px 10px; border: 1px solid rgba(255,255,255,.3);
    white-space: nowrap; pointer-events: none;
}
.tb-chest.pop, .tb-seat.pop { animation: tb-pop .35s ease; }
@keyframes tb-pop {
    0% { transform: scale(1); }
    40% { transform: scale(1.16); }
    100% { transform: scale(1); }
}
/* Coffre cliquable (Pirate, à son tour) : lueur dorée pulsée à la place du
   bouton "Ouvrir le Coffre" retiré — voir askOpenCoffre/#tb-confirm-open
   dans traitres_a_bord.js, qui demande toujours confirmation avant d'agir
   (action irréversible, termine la partie). */
.tb-chest-wrap.openable { cursor: pointer; }
.tb-chest-wrap.openable .tb-chest { animation: tb-chest-glow 1.6s ease-in-out infinite; }
@keyframes tb-chest-glow {
    0%, 100% { filter: drop-shadow(0 8px 16px rgba(0,0,0,.6)) drop-shadow(0 0 8px rgba(245,158,11,.55)); }
    50% { filter: drop-shadow(0 8px 16px rgba(0,0,0,.6)) drop-shadow(0 0 20px rgba(245,158,11,.9)); }
}

/* Pioche : le vrai dos de carte fourni (face.png, dossier des cartes) —
   même compteur superposé que le Coffre (.tb-chest-count). */
.tb-deck {
    /* left = 41.5% : écart au Coffre (58%) réduit de moitié (était 25%, un
       écart de 33 points ramené à 16.5) — retour utilisateur. */
    position: absolute; z-index: 2; left: 41.5%; top: 50%; width: 9%; transform: translate(-50%, -50%);
}
.tb-deck-img { display: block; width: 100%; height: auto; filter: drop-shadow(0 5px 12px rgba(0,0,0,.5)); }

/* Sièges des adversaires, DANS le terrain (façon Uno — voir SEAT/SEAT_LAYOUTS
   dans traitres_a_bord.js) plutôt qu'en liste à côté. "Vous" n'a pas de
   siège : votre statut vit dans .tb-meta + votre main, en dessous. */
.tb-seats { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.tb-seat {
    position: absolute; display: flex; flex-direction: column; align-items: center; gap: 3px;
    background: rgba(10,14,24,.62); backdrop-filter: blur(3px);
    border: 1px solid rgba(255,255,255,.12); border-radius: 12px;
    padding: 5px 9px; min-width: 84px; text-align: center; pointer-events: auto;
    transition: box-shadow .2s, border-color .2s, opacity .2s;
}
.tb-seat.center { transform: translateX(-50%); }
.tb-seat.current { border-color: #f59e0b; box-shadow: 0 0 0 2px rgba(245,158,11,.4), 0 0 16px rgba(245,158,11,.3); }
.tb-seat.eliminated { opacity: .4; }
.tb-seat.targetable { cursor: pointer; border-color: #f59e0b; box-shadow: 0 0 0 2px rgba(245,158,11,.35); }
.tb-seat.targetable:hover { border-color: #fbbf24; }
.tb-seat-name { font-weight: 700; font-size: .72rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 110px; }
.tb-seat-meta { display: flex; gap: 7px; justify-content: center; font-size: .66rem; color: var(--muted); }
.tb-seat .sb-role-mini { height: 26px; margin: 2px 0 0; }

/* Carte volante : position posée en JS via left/top (%, relatifs au
   terrain) — transitionnés directement (contrairement à transform, ils
   acceptent n'importe quel point d'arrivée sans calcul de décalage) — plus
   transform pour l'échelle/rotation et opacity pour l'effacement. Réutilisée
   pour pose dans le Coffre / Bon débarras / Longue-vue / Planche (voir
   spawnCard/moveCard dans traitres_a_bord.js). */
.tb-flying-card {
    position: absolute; z-index: 6; width: 15%; border-radius: 6px;
    box-shadow: 0 6px 14px rgba(0,0,0,.55); pointer-events: none;
    transform: translate(-50%, -50%) scale(1) rotate(0deg); opacity: 1;
    transition: left .5s cubic-bezier(.3,.6,.3,1), top .5s cubic-bezier(.3,.6,.3,1),
                transform .5s cubic-bezier(.3,.6,.3,1), opacity .45s ease;
}

/* Modale de décision différée (Longue-vue / Pêche miraculeuse) : même
   habillage que le panneau dev (.pp-dev-panel/.pp-dev-box), sans le bouton
   de fermeture — la décision est obligatoire, pas annulable. */
.tb-reorder-row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin: 12px 0; }
.tb-reorder-cell { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.tb-reorder-cell .sb-card-img { width: 72px; height: auto; cursor: zoom-in; }
.tb-reorder-btns { display: flex; gap: 4px; }

/* Loupe superposée sur une carte de la main (coin haut-droit) : zoome sans
   déclencher la sélection/le ciblage porté par le reste de la carte (voir
   addZoomBadge dans traitres_a_bord.js). */
.tb-card-slot { position: relative; }
.tb-card-zoom-btn {
    position: absolute; top: 3px; right: 3px; z-index: 2;
    width: 22px; height: 22px; padding: 0; display: flex; align-items: center; justify-content: center;
    font-size: .8rem; line-height: 1; border-radius: 50%; cursor: zoom-in;
    background: rgba(0,0,0,.55); border: 1px solid rgba(255,255,255,.25); color: #fff;
}
.tb-card-zoom-btn:hover { background: rgba(0,0,0,.8); }

/* Annonce, ANCRÉE dans le terrain près du Coffre (pas un bandeau fixe en
   haut de page) — voir showEventToast dans traitres_a_bord.js. Valeur
   ANNONCÉE, pas forcément vraie : habillage neutre, pas de vert/rouge qui
   laisserait croire à une confirmation de la vraie valeur de la carte. */
.tb-announce-toast {
    position: absolute; z-index: 5; left: 50%; bottom: 8%; transform: translate(-50%, 10px);
    display: flex; align-items: center; gap: 8px;
    background: rgba(15,23,42,.94); color: #fff; border: 1px solid rgba(255,255,255,.2);
    border-radius: 999px; padding: 6px 16px 6px 6px; box-shadow: 0 8px 22px rgba(0,0,0,.5);
    opacity: 0; pointer-events: none; max-width: 88%;
    transition: opacity .25s ease, transform .25s ease;
}
.tb-announce-toast.show { opacity: 1; transform: translate(-50%, 0); }
.tb-announce-toast img { width: 30px; height: auto; border-radius: 4px; flex: none; }
.tb-announce-toast span { font-size: .8rem; line-height: 1.2; }
.tb-announce-toast b { color: #facc15; }

/* Morpion — panneau dev (réglage des bots) */
.mp-dev-bots { display: flex; flex-direction: column; gap: 14px; margin-top: 8px; }
.mp-dev-field { display: block; font-size: .8rem; color: var(--muted); margin: 10px 0 4px; }
.mp-dev-field input, .mp-dev-field select {
    display: block; width: 100%; margin-top: 4px; background: var(--bg-soft);
    border: 1px solid var(--border); color: var(--text); padding: 8px 10px;
    border-radius: 8px; font-size: .9rem; font-family: inherit;
}

/* Morpion — bouton discret "+ Bot" et sa liste flottante */
.mp-addbot-wrap { position: relative; display: flex; justify-content: center; margin: -6px 0 14px; }
.mp-addbot-btn {
    font-family: inherit; font-size: .75rem; font-weight: 600; color: var(--muted);
    background: transparent; border: 1px dashed var(--border); border-radius: 999px;
    padding: 3px 12px; cursor: pointer; transition: .15s;
}
.mp-addbot-btn:hover { color: var(--accent); border-color: var(--accent); border-style: solid; }
.mp-bot-menu {
    position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
    min-width: 160px; background: var(--panel); border: 1px solid var(--border);
    border-radius: 10px; box-shadow: var(--shadow); padding: 5px; z-index: 40;
    display: flex; flex-direction: column; gap: 2px;
}
.mp-bot-menu-item {
    display: block; width: 100%; text-align: left; font-family: inherit; font-size: .85rem;
    background: transparent; border: none; color: var(--text); padding: 7px 10px;
    border-radius: 7px; cursor: pointer;
}
.mp-bot-menu-item:hover { background: var(--panel-2); color: var(--accent); }

/* Salon (liste de joueurs) : le bouton "+ Bot" prend l'apparence d'une
   ligne en pointillés dans le prolongement de `.room-players`, plutôt que
   la pastille flottante ci-dessus — celle-ci reste inchangée là où il n'y a
   pas de liste de joueurs à côté (Bataille navale, Puissance 4). */
.room-players + .mp-addbot-wrap { display: block; justify-content: initial; margin: 2px 0 6px; }
.room-players + .mp-addbot-wrap .mp-addbot-btn {
    width: 100%; display: flex; align-items: center; gap: 11px; text-align: left;
    padding: 7px 10px; border-radius: 11px; font-size: .88rem; font-weight: 600;
}
.room-players + .mp-addbot-wrap .mp-addbot-btn::before {
    content: "🤖"; flex: none; width: 34px; height: 34px; border-radius: 50%;
    border: 1px dashed var(--border); display: flex; align-items: center; justify-content: center;
    font-size: .95rem; transition: border-color .15s; background: none;
}
.room-players + .mp-addbot-wrap .mp-addbot-btn:hover::before { border-color: var(--accent); }
.room-players + .mp-addbot-wrap .mp-bot-menu { left: 0; transform: none; min-width: 100%; }

/* Bataille navale — dev panel : cases à cocher alignées avec leur libellé */
.mp-dev-field:has(input[type="checkbox"]) { display: flex; align-items: center; gap: 8px; }
.mp-dev-field input[type="checkbox"] { width: auto; margin: 0; }

/* ============ Codenames ============ */
:root {
    --cn-red: #b91c1c; --cn-blue: #1d4ed8; --cn-neutral: #a8895f; --cn-assassin: #111827;
}

/* Bandeau d'équipe/tour ("HUD" d'espionnage) — bannières asymétriques
   (clip-path) plutôt que des pilules plates, chiffre géant, liseré/scanline
   seulement sur l'équipe active. Transposé tel quel depuis le prototype
   validé (static/preview.html), voir gamespace_codenames_game.md. */
.cn-topbar { display: flex; align-items: stretch; justify-content: center; gap: 0; margin: 18px auto 24px; max-width: 640px; }
.cn-score {
    position: relative; flex: 1; min-width: 110px; text-align: center;
    padding: 18px 14px 16px; overflow: hidden;
    background: linear-gradient(165deg, rgba(255,255,255,.05), rgba(255,255,255,0) 60%), var(--panel);
    transition: filter .25s ease;
}
.cn-score.red { clip-path: polygon(0 0, 100% 0, 88% 100%, 0% 100%); }
.cn-score.blue { clip-path: polygon(12% 0, 100% 0, 100% 100%, 0% 100%); }
.cn-score.red::before, .cn-score.blue::before { content: ""; position: absolute; inset: 0; opacity: .5; }
.cn-score.red::before { background: linear-gradient(100deg, rgba(239,68,68,.28), transparent 65%); }
.cn-score.blue::before { background: linear-gradient(260deg, rgba(59,130,246,.28), transparent 65%); }
.cn-score-label {
    position: relative; z-index: 1; display: block; font-size: .62rem; font-weight: 800;
    text-transform: uppercase; letter-spacing: .2em; color: var(--muted); margin-bottom: 6px;
}
.cn-score-num {
    position: relative; z-index: 1; display: block; font-size: 2.7rem; font-weight: 900;
    line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: -.02em;
    filter: drop-shadow(0 0 2px currentColor);
}
.cn-score.red .cn-score-num { color: #f87171; }
.cn-score.blue .cn-score-num { color: #60a5fa; }
.cn-score:not(.active) { filter: saturate(.55) brightness(.8); }
.cn-score.active .cn-score-num { animation: cn-num-glow 1.7s ease-in-out infinite; }
@keyframes cn-num-glow {
    0%, 100% { filter: drop-shadow(0 0 3px currentColor); }
    50% { filter: drop-shadow(0 0 14px currentColor); }
}
.cn-score.active::after {
    content: ""; position: absolute; z-index: 2; left: 18%; right: 18%; bottom: 8px; height: 2px;
    background: currentColor; color: inherit; box-shadow: 0 0 6px currentColor, 0 0 14px currentColor;
    animation: cn-scan 1.7s ease-in-out infinite;
}
.cn-score.red.active::after { color: #f87171; }
.cn-score.blue.active::after { color: #60a5fa; }
@keyframes cn-scan { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }

.cn-turn-banner {
    flex: none; align-self: center; text-align: center; font-weight: 800; padding: 8px 22px;
    font-size: .74rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted);
    min-width: 150px; transition: color .25s ease;
}
.cn-turn-banner b { display: block; font-size: .95rem; letter-spacing: .01em; text-transform: none; margin-top: 2px; }
.cn-turn-banner.team-red { color: #f87171; }
.cn-turn-banner.team-blue { color: #60a5fa; }

/* Indice courant, au-dessus de la grille */
.cn-clue-display {
    max-width: 420px; margin: 0 auto 18px; padding: 12px 20px; border-radius: 14px;
    background: var(--panel); border: 1px solid var(--border); text-align: center;
    display: flex; align-items: baseline; justify-content: center; gap: 10px;
    transition: border-color .25s ease, box-shadow .25s ease;
}
.cn-clue-display.team-red { border-color: rgba(239,68,68,.55); box-shadow: 0 0 18px -8px rgba(239,68,68,.6); }
.cn-clue-display.team-blue { border-color: rgba(59,130,246,.55); box-shadow: 0 0 18px -8px rgba(59,130,246,.6); }
.cn-clue-display-label { font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); }
.cn-clue-display-word { font-size: 1.3rem; font-weight: 900; letter-spacing: .02em; }
.cn-clue-display.team-red .cn-clue-display-word { color: #f87171; }
.cn-clue-display.team-blue .cn-clue-display-word { color: #60a5fa; }
.cn-clue-display-num {
    font-size: 1rem; font-weight: 800; color: var(--text); background: var(--panel-2);
    border-radius: 999px; padding: 2px 11px;
}

/* Grille 5x5 */
.cn-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; max-width: 820px; margin: 0 auto 20px; }
/* Carte à retourner : .cn-card = conteneur perspective (taille/position
   seulement) ; .cn-card-inner pivote de 180° quand .revealed est posé sur
   .cn-card ; faces avant/arrière superposées, chacune cachée quand elle
   regarde "vers l'intérieur" (backface-visibility). Le lift au survol est
   posé sur .cn-card-inner, JAMAIS sur .cn-card-face : la face arrière porte
   sa PROPRE base `transform: rotateY(180deg)`, un hover qui écrirait
   `transform` directement dessus l'écraserait et démasquerait la couleur
   avant l'heure (bug constaté et corrigé sur le prototype). */
.cn-card { position: relative; aspect-ratio: 755 / 493; perspective: 1000px; }
.cn-card.clickable { cursor: pointer; }
.cn-card-inner {
    position: relative; width: 100%; height: 100%; transform-style: preserve-3d;
    transition: transform .55s cubic-bezier(.3,.6,.3,1);
}
.cn-card.revealed .cn-card-inner { transform: rotateY(180deg); }
.cn-card.clickable:hover .cn-card-inner { transform: translateY(-3px); }
.cn-card-face {
    position: absolute; inset: 0; backface-visibility: hidden; border-radius: 10px; overflow: hidden;
    background-size: cover; background-position: center;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 4px 10px rgba(0,0,0,.4); transition: box-shadow .12s ease;
}
.cn-card.clickable:hover .cn-card-face { box-shadow: 0 7px 16px rgba(0,0,0,.5); }
.cn-card-back { transform: rotateY(180deg); }
.cn-card-word {
    position: relative; z-index: 2; font-weight: 800; font-size: clamp(.62rem, 2.6vw, .92rem);
    text-align: center; padding: 4px 6px; color: #1a1208; text-transform: uppercase;
    letter-spacing: .02em; text-shadow: 0 1px 2px rgba(255,255,255,.25);
}
.cn-card-back .cn-card-word { color: #fff; text-shadow: 0 1px 4px rgba(0,0,0,.6); opacity: .85; }
/* Vue Maître-espion : liseré + pastille de couleur, sur la face AVANT
   normale (jamais le plein fond coloré, réservé à la face arrière une fois
   vraiment révélée) — distingue "je sais" de "c'est joué". */
.cn-card.peek .cn-card-front { box-shadow: 0 0 0 3px var(--peek-color), 0 4px 10px rgba(0,0,0,.4); }
.cn-card-peek-dot {
    position: absolute; top: 5px; right: 5px; z-index: 3; width: 14px; height: 14px;
    border-radius: 50%; background: var(--peek-color); border: 2px solid rgba(255,255,255,.6);
    box-shadow: 0 1px 4px rgba(0,0,0,.5); display: none;
}
.cn-card.peek .cn-card-peek-dot { display: block; }

/* Pastille "je pense que c'est ça" : cycle au clic sans déclencher la
   révélation (stopPropagation côté JS), privée à chaque joueur. */
.cn-card-pin {
    position: absolute; top: 5px; left: 5px; z-index: 4; width: 20px; height: 20px;
    border-radius: 50%; background: rgba(0,0,0,.45); border: 1px solid rgba(255,255,255,.35);
    display: flex; align-items: center; justify-content: center; font-size: .7rem; cursor: pointer;
    opacity: .4; transition: opacity .15s, transform .15s;
}
.cn-card-pin:hover { opacity: .9; transform: scale(1.12); }
.cn-card-pin.active { opacity: 1; background: #facc15; border-color: #fff; }

/* Zone Maître-espion : indice */
.cn-clue-panel {
    max-width: 520px; margin: 0 auto 20px; padding: 16px 20px; border-radius: 14px;
    background: var(--panel); border: 1px solid var(--border); text-align: center;
}
.cn-clue-row { display: flex; gap: 8px; margin-top: 10px; justify-content: center; flex-wrap: wrap; }
.cn-clue-row input {
    font-family: inherit; font-size: .9rem; padding: 8px 12px; border-radius: 8px;
    border: 1px solid var(--border); background: var(--panel-2); color: var(--text);
}
.cn-clue-row input#cn-clue-word { width: 160px; }
.cn-clue-row input#cn-clue-num { width: 60px; text-align: center; }
.cn-clue-row button {
    font-family: inherit; font-weight: 700; font-size: .85rem; padding: 8px 18px;
    border-radius: 8px; border: none; background: var(--accent); color: #fff; cursor: pointer;
}

/* Choix d'équipe/Maître-espion en salon — ouvert à tout joueur (aucune
   restriction hôte, contrairement à toutes les autres actions de salon de
   la plateforme, voir gamespace_codenames_game.md). */
.cn-teams { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 10px 0 16px; }
.cn-team-col {
    border-radius: 12px; padding: 12px 14px; background: var(--panel-2);
    border: 1px solid var(--border); border-top: 3px solid var(--cn-red);
}
.cn-team-col-blue { border-top-color: var(--cn-blue); }
.cn-team-title { margin: 0 0 8px; font-size: .85rem; text-transform: uppercase; letter-spacing: .04em; }
.cn-team-col-red .cn-team-title { color: #f87171; }
.cn-team-col-blue .cn-team-title { color: #60a5fa; }
.cn-team-members { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; min-height: 24px; }
.cn-team-member { font-size: .85rem; }
.cn-team-member.is-you { font-weight: 700; }
.cn-team-empty { font-size: .8rem; }
.cn-team-actions .btn { width: 100%; }

/* ==================== Page Succès ==================== */
.succes-page { max-width: 1180px; margin: 0 auto; }
.succes-notice {
    background: rgba(56, 189, 248, .08); border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 10px; padding: 10px 16px; font-size: .85rem; color: var(--muted);
    margin-bottom: 22px;
}
.succes-notice b { color: var(--text); }

.succ-header {
    display: flex; align-items: flex-end; justify-content: space-between; gap: 24px;
    flex-wrap: wrap; margin-bottom: 20px;
}
.succ-header h1 { font-size: 1.9rem; margin: 0 0 6px; display: flex; align-items: center; gap: 12px; }
.succ-header p { margin: 0; color: var(--muted); max-width: 520px; font-size: .92rem; }

.overview-strip { display: flex; gap: 10px; flex-wrap: wrap; }
.overview-pill {
    display: flex; align-items: center; gap: 10px;
    background: var(--panel); border: 1px solid var(--border);
    border-top: 3px solid var(--tier-c, var(--border));
    border-radius: 10px; padding: 9px 14px 8px; min-width: 88px;
}
.overview-pill .gem {
    width: 13px; height: 13px; flex: none; border-radius: 3px;
    background: var(--tier-c, var(--muted)); transform: rotate(45deg);
    box-shadow: 0 0 9px 0 color-mix(in srgb, var(--tier-c, var(--muted)) 65%, transparent);
}
.overview-pill.tier-cosmique { border-top-color: transparent; position: relative; }
.overview-pill.tier-cosmique::before {
    content: ""; position: absolute; inset: -1px -1px auto -1px; height: 3px;
    border-radius: 10px 10px 0 0;
    background: var(--cosmic-gradient); background-size: 300% 100%;
    animation: cosmique-shift 5s linear infinite;
}
.overview-pill.tier-cosmique .gem {
    background: var(--cosmic-gradient); background-size: 300% 100%;
    animation: cosmique-shift 5s linear infinite;
    box-shadow: 0 0 9px 0 rgba(217, 70, 239, .6);
}
.overview-pill .n { font-size: 1.1rem; font-weight: 700; line-height: 1.1; color: var(--text); }
.overview-pill .l { font-size: .66rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; margin-top: 1px; }
@keyframes cosmique-shift { to { background-position: 300% 0; } }

.overall-bar-wrap { margin-top: 14px; }
.overall-bar {
    height: 10px; border-radius: 999px; background: var(--panel-2);
    border: 1px solid var(--border); overflow: hidden; position: relative;
}
.overall-bar-fill {
    height: 100%; border-radius: 999px;
    background: linear-gradient(90deg, var(--t-bronze), var(--t-or), var(--t-diamant));
}
.overall-bar-label { font-size: .76rem; color: var(--muted); margin-top: 6px; display: flex; justify-content: space-between; }

.succ-layout { display: grid; grid-template-columns: 250px 1fr; gap: 22px; margin-top: 26px; align-items: start; }
@media (max-width: 860px) { .succ-layout { grid-template-columns: 1fr; } }

.game-rail {
    display: flex; flex-direction: column; gap: 6px;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 8px; position: sticky; top: 20px;
}
@media (max-width: 860px) {
    .game-rail { position: static; flex-direction: row; overflow-x: auto; padding: 8px; gap: 8px; }
}
.game-tab {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 12px; border-radius: 10px; cursor: pointer;
    border: 1px solid transparent; background: none; color: var(--text);
    font: inherit; text-align: left; width: 100%;
    transition: background .15s, border-color .15s;
}
@media (max-width: 860px) { .game-tab { width: auto; flex: none; white-space: nowrap; } }
.game-tab:hover { background: var(--panel-2); }
.game-tab.is-active {
    background: color-mix(in srgb, var(--tab-c, var(--accent)) 16%, var(--panel-2));
    border-color: color-mix(in srgb, var(--tab-c, var(--accent)) 50%, transparent);
}
.game-tab-emoji { font-size: 1.2rem; width: 26px; text-align: center; flex: none; }
.game-tab-info { flex: 1; min-width: 0; }
.game-tab-name { font-size: .86rem; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.game-tab-sub { font-size: .68rem; color: var(--muted); }
.game-tab-ring { flex: none; width: 36px; height: 36px; display: block; }
.game-tab-ring-text { font-size: 10px; font-weight: 700; fill: var(--text); font-family: "Space Grotesk", system-ui, sans-serif; }

.succ-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 14px; flex-wrap: wrap; }
.succ-panel-head h2 { margin: 0; font-size: 1.25rem; display: flex; align-items: center; gap: 10px; }
.succ-search {
    background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
    padding: 8px 12px; color: var(--text); font: inherit; font-size: .85rem;
    min-width: 180px;
}
.succ-search:focus { outline: none; border-color: var(--accent); }

.achv-list { display: flex; flex-direction: column; gap: 12px; }

.achv-card {
    display: grid; grid-template-columns: 74px 1fr; gap: 16px;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 14px 16px;
}
.achv-card.is-maxed { box-shadow: 0 0 0 1px color-mix(in srgb, var(--t-cosmique) 45%, transparent) inset; }

.achv-icon-wrap { display: flex; align-items: center; justify-content: center; }
.achv-icon {
    width: 64px; height: 64px; border-radius: 16px;
    display: flex; align-items: center; justify-content: center;
    background: var(--panel-2); border: 1px solid var(--border);
    position: relative; overflow: hidden;
}
.achv-icon img { width: 100%; height: 100%; object-fit: contain; padding: 6px; }
.achv-icon.is-locked { filter: grayscale(1) brightness(.55); }
.achv-icon.is-empty::after { content: "🔒"; font-size: 1.4rem; opacity: .35; }
.achv-icon-glow { box-shadow: 0 0 16px 1px color-mix(in srgb, var(--tier-c, var(--accent)) 55%, transparent); }
.achv-icon.is-zoomable { cursor: zoom-in; }
.achv-icon.is-zoomable:hover { transform: scale(1.04); }

.achv-main { min-width: 0; }
.achv-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.achv-name { font-weight: 700; font-size: 1rem; }
.achv-current-tier {
    font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
    padding: 2px 9px; border-radius: 999px; color: #0a1220;
    background: var(--tier-c, var(--border)); white-space: nowrap;
}
.achv-current-tier.none { background: var(--panel-2); color: var(--muted); }
.achv-current-tier.cosmique {
    background: var(--cosmic-gradient); background-size: 300% 100%;
    color: #0a1220; animation: cosmique-shift 4s linear infinite;
}
.achv-desc { font-size: .82rem; color: var(--muted); margin: 3px 0 10px; }

.tier-ladder { display: flex; align-items: center; gap: 4px; }
.tier-step { display: flex; flex-direction: column; align-items: center; gap: 4px; flex: none; position: relative; }
.tier-dot {
    width: 30px; height: 30px; border-radius: 9px;
    background: var(--panel-2); border: 2px solid var(--border);
    display: flex; align-items: center; justify-content: center;
    position: relative; cursor: default;
}
.tier-dot img { width: 100%; height: 100%; object-fit: contain; border-radius: 7px; }
.tier-dot.is-unlocked { border-color: var(--tc); box-shadow: 0 0 8px 0 color-mix(in srgb, var(--tc) 55%, transparent); }
.tier-dot.is-locked img { filter: grayscale(1) brightness(.5); opacity: .55; }
.tier-dot.is-zoomable { cursor: zoom-in; transition: transform .12s; }
.tier-dot.is-zoomable:hover { transform: scale(1.12); }
.tier-dot.no-badge { display: flex; align-items: center; justify-content: center; font-size: .8rem; }
.tier-dot.no-badge::before { content: "?"; color: var(--muted); font-weight: 700; }
.tier-dot.is-unlocked.no-badge::before { content: "✓"; color: var(--tc); }
.tier-connector { flex: 1; height: 3px; background: var(--border); border-radius: 2px; min-width: 10px; position: relative; top: -12px; }
.tier-connector.is-filled { background: var(--tc); }
.tier-connector.is-partial { background: linear-gradient(90deg, var(--tc) var(--fill, 0%), var(--border) var(--fill, 0%)); }

.achv-progress-row { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.achv-bar {
    flex: 1; height: 7px; border-radius: 999px; background: var(--panel-2);
    border: 1px solid var(--border); overflow: hidden;
}
.achv-bar-fill { height: 100%; border-radius: 999px; background: var(--tier-c, var(--accent)); transition: width .3s; }
.achv-progress-label { font-size: .74rem; color: var(--muted); white-space: nowrap; flex: none; }
.achv-progress-label b { color: var(--text); }

.achv-meta-note {
    font-size: .76rem; color: var(--muted); margin-top: 10px;
    background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px;
    padding: 6px 10px;
}

.tier-tip {
    position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
    background: #05070d; border: 1px solid var(--border); border-radius: 8px;
    padding: 6px 10px; font-size: .7rem; white-space: nowrap; color: var(--text);
    opacity: 0; pointer-events: none; transition: opacity .12s; z-index: 5;
    box-shadow: var(--shadow);
}
.tier-step:hover .tier-tip { opacity: 1; }

.succ-empty-state { text-align: center; color: var(--muted); padding: 50px 20px; font-size: .9rem; }

/* Zoom sur un badge débloqué (même principe que .card-zoom) */
.badge-zoom {
    position: fixed; inset: 0; z-index: 100; padding: 24px;
    background: rgba(4, 8, 16, .82); backdrop-filter: blur(3px);
    display: none; align-items: center; justify-content: center; cursor: zoom-out;
}
.badge-zoom.show { display: flex; animation: card-zoom-fade .15s ease-out; }
.badge-zoom-box {
    position: relative; cursor: default; text-align: center;
    display: flex; flex-direction: column; align-items: center; gap: 14px;
    animation: card-zoom-in .18s cubic-bezier(.2, .8, .3, 1);
    max-width: 92vw;
}
.badge-zoom-img {
    width: min(340px, 68vw); height: min(340px, 68vw); object-fit: contain;
    filter: drop-shadow(0 24px 50px rgba(0, 0, 0, .55));
}
.badge-zoom-close {
    position: absolute; top: -14px; right: -14px; width: 32px; height: 32px;
    border-radius: 50%; border: 1px solid var(--border); background: var(--panel);
    color: var(--text); font-size: 1rem; cursor: pointer;
}
.badge-zoom-tier {
    display: inline-block; font-size: .72rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: .05em; padding: 3px 12px; border-radius: 999px;
    background: var(--zc, var(--accent)); color: #0a1220;
}
.badge-zoom-name { font-size: 1.3rem; font-weight: 700; }
.badge-zoom-desc { color: var(--muted); font-size: .88rem; max-width: 380px; }

/* ===================================================================
   Page Profil (templates/profile.html, static/js/profile.js) — toutes
   les classes sont préfixées `profile-` pour ne jamais entrer en
   collision avec des classes génériques déjà utilisées ailleurs sur le
   site (`.panel`, `.section`, `.avatar`... existent déjà avec un tout
   autre style, voir plus haut dans ce fichier).
   =================================================================== */
.profile-page { max-width: 1000px; margin: 0 auto; }
.profile-viewing-note {
    font-size: .82rem; color: var(--muted); margin-bottom: 20px;
    padding: 10px 14px; background: rgba(56,189,248,.08); border: 1px solid var(--border);
    border-left: 3px solid var(--accent); border-radius: 10px;
}
.profile-viewing-note b { color: var(--text); }
.profile-viewing-note a { color: var(--accent); font-weight: 600; text-decoration: none; }
.profile-viewing-note a:hover { text-decoration: underline; }

@keyframes profile-fade-up { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }
.profile-fade-in { animation: profile-fade-up .6s cubic-bezier(.2,.8,.3,1) backwards; }
@keyframes profile-shift { to { background-position: 300% 0; } }

/* Le flou "verre dépoli" est posé sur un ::before (derrière le contenu,
   z-index:-1) plutôt que directement sur .profile-panel : un
   backdrop-filter posé sur le même conteneur que ses enfants découpe les
   effets qui débordent (filter: drop-shadow des badges, notamment) au
   bord de ce conteneur — bug connu de Chromium/WebKit sur cette
   combinaison. */
.profile-panel { position: relative; isolation: isolate; border-radius: 22px; }
.profile-panel::before {
    content: ""; position: absolute; inset: 0; z-index: -1;
    background: rgba(22, 31, 56, .55); backdrop-filter: blur(14px);
    border: 1px solid rgba(255,255,255,.06); border-radius: 22px;
}

/* ---------- Hero : cover + avatar en débord + barre de stats ---------- */
.profile-hero { border-radius: 26px; overflow: visible; margin-bottom: 28px; position: relative; }

.profile-cover {
    position: relative; height: 190px; border-radius: 26px 26px 0 0; overflow: hidden;
    background: linear-gradient(200deg, #161f42, #0a0e1e 75%);
}
.profile-cover::before {
    content: ""; position: absolute; inset: 0; opacity: .8;
    background-image:
        radial-gradient(1.4px 1.4px at 10% 20%, #fff, transparent),
        radial-gradient(1.2px 1.2px at 30% 65%, #fff, transparent),
        radial-gradient(1px 1px at 50% 15%, #fff, transparent),
        radial-gradient(1.3px 1.3px at 70% 40%, #fff, transparent),
        radial-gradient(1px 1px at 85% 70%, #fff, transparent),
        radial-gradient(1.5px 1.5px at 95% 25%, #fff, transparent),
        radial-gradient(1px 1px at 15% 85%, #fff, transparent),
        radial-gradient(1.2px 1.2px at 60% 85%, #fff, transparent);
}
.profile-cover-planet-big {
    position: absolute; width: 240px; height: 240px; border-radius: 50%; right: -50px; top: -90px;
    background: radial-gradient(circle at 35% 30%, #59689e, #131a30 72%);
    box-shadow: inset -18px -12px 36px rgba(0,0,0,.5), 0 0 60px rgba(99,102,241,.20);
}
.profile-cover-planet-small {
    position: absolute; width: 56px; height: 56px; border-radius: 50%; left: 8%; top: 58%;
    background: radial-gradient(circle at 35% 30%, #fb7185, #7c2d3f 80%);
    box-shadow: 0 0 26px rgba(251,113,133,.35);
}

.profile-hero-center {
    position: absolute; left: 50%; top: 84px; transform: translateX(-50%);
    display: flex; flex-direction: column; align-items: center; z-index: 5; width: max-content;
}
.profile-avatar-wrap { position: relative; }
.profile-avatar {
    width: 154px; height: 154px; border-radius: 33px;
    display: flex; align-items: center; justify-content: center;
    font-size: 3.1rem; font-weight: 700; color: #0a1220;
    background: linear-gradient(135deg, var(--accent), var(--violet));
    border: 4px solid #0a0e1e;
    box-shadow: 0 10px 30px -6px rgba(0,0,0,.5);
}
.profile-avatar.is-zoomable { cursor: zoom-in; transition: transform .15s ease; }
.profile-avatar.is-zoomable:hover { transform: scale(1.03); }
.profile-avatar-tag {
    position: absolute; bottom: -8px; right: -10px;
    background: var(--cosmic-gradient); background-size: 300% 100%; animation: profile-shift 6s linear infinite;
    border: 3px solid #0a0e1e; border-radius: 999px;
    font-size: .72rem; font-weight: 800; color: #0a1220; padding: 3px 9px;
    display: flex; align-items: center; gap: 3px; white-space: nowrap;
}
/* Photo de profil = un succès débloqué (voir User.profile_badge_*) plutôt
   que l'initiale par défaut — pas d'encadré autour de l'image : juste le
   halo teinté par palier (--tc, posé inline, filter:drop-shadow suit la
   silhouette réelle du badge), même traitement que l'étagère de succès. */
.profile-avatar.has-badge { background: transparent; border: none; box-shadow: none; }
.profile-avatar img { width: 100%; height: 100%; object-fit: contain; padding: 8px; }
.profile-avatar.has-badge img { filter: drop-shadow(0 0 12px color-mix(in srgb, var(--tc) 65%, transparent)); }

/* Menu "⋯" (badge / pseudo / mot de passe) — même gabarit que .user-menu-wrap/
   .user-dropdown (topbar, voir base.html) : un petit dropdown à 3 entrées,
   chacune ouvrant une vraie modale (.profile-modal-*, voir plus bas) plutôt
   qu'un panneau qui reste coincé dans ce petit espace. */
.profile-menu-wrap { position: relative; }
.profile-menu-btn {
    width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--border);
    background: var(--panel-2); color: var(--muted); font-size: .95rem; font-weight: 700;
    display: flex; align-items: center; justify-content: center; cursor: pointer; line-height: 1;
    margin-left: 2px;
}
.profile-menu-btn:hover { border-color: var(--accent); color: var(--accent); }
.profile-menu-dropdown {
    position: absolute; top: calc(100% + 8px); left: 0; z-index: 20; width: 220px;
    background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
    box-shadow: 0 14px 34px rgba(0,0,0,.45); padding: 6px;
}
.profile-menu-dropdown.hidden { display: none; }

/* Modales (badge / pseudo / mot de passe), construites en JS — même gabarit
   que .report-modal-* (static/js/reports.js) : backdrop plein écran flou,
   boîte centrée qui zoome légèrement à l'ouverture. */
.profile-modal-backdrop {
    position: fixed; inset: 0; z-index: 200; padding: 24px;
    background: rgba(4, 8, 16, .78); backdrop-filter: blur(3px);
    display: flex; align-items: center; justify-content: center;
    opacity: 0; pointer-events: none; transition: opacity .15s ease;
}
.profile-modal-backdrop.open { opacity: 1; pointer-events: auto; }
.profile-modal {
    width: min(440px, 100%); max-height: 82vh; overflow-y: auto; display: flex; flex-direction: column;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    box-shadow: 0 24px 70px rgba(0,0,0,.6);
    transform: translateY(10px) scale(.97); transition: transform .18s ease;
}
.profile-modal.is-wide { width: min(560px, 100%); }
.profile-modal-backdrop.open .profile-modal { transform: translateY(0) scale(1); }
.profile-modal-head {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 18px 20px 14px; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.profile-modal-head h3 { margin: 0; font-size: 1.05rem; }
.profile-modal-close {
    background: none; border: none; color: var(--muted); cursor: pointer;
    font-size: 0.95rem; width: 30px; height: 30px; border-radius: 8px; flex-shrink: 0;
}
.profile-modal-close:hover { background: var(--bg-soft); color: var(--text); }
.profile-modal-body { padding: 18px 20px 22px; display: flex; flex-direction: column; }
.profile-modal-body form > label:first-of-type { margin-top: 0; }
.profile-modal-body .btn { margin-top: 18px; }

.profile-badge-picker-grid {
    display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px;
}
.profile-badge-tile { display: block; }
.profile-badge-tile-btn {
    width: 100%; aspect-ratio: 1; border-radius: 12px; padding: 6px;
    background: var(--panel-2); border: 2px solid transparent; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    filter: drop-shadow(0 0 6px color-mix(in srgb, var(--tc, var(--accent)) 45%, transparent));
    transition: border-color .15s, transform .15s;
}
.profile-badge-tile-btn:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--tc, var(--accent)) 60%, transparent); }
.profile-badge-tile-btn.is-current { border-color: var(--tc, var(--accent)); }
.profile-badge-tile-btn img { width: 100%; height: 100%; object-fit: contain; }
.profile-badge-tile.is-reset .profile-badge-tile-btn { color: var(--muted); font-size: 1.4rem; }
@media (max-width: 480px) { .profile-badge-picker-grid { grid-template-columns: repeat(3, 1fr); } }
.profile-badge-picker-label {
    font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    color: var(--muted); margin: 18px 0 10px;
}
.profile-name-row { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
.profile-name { font-size: 1.4rem; font-weight: 800; }
.profile-verified {
    width: 19px; height: 19px; border-radius: 50%; background: var(--good);
    color: #06281c; font-size: .68rem; display: flex; align-items: center; justify-content: center; flex: none;
}
/* Couleur unie du palier réel du joueur par défaut (--tc, posé inline
   depuis templates/profile.html) — le traitement arc-en-ciel animé
   (.is-cosmique) reste réservé au VRAI palier Cosmique, pour ne pas
   survendre un badge Bronze/Argent avec le même effet que le sommet. */
.profile-flair {
    margin-top: 6px; font-size: .66rem; font-weight: 700; letter-spacing: .04em;
    color: var(--tc, var(--text));
    border: 1px solid color-mix(in srgb, var(--tc, var(--border)) 45%, transparent);
    border-radius: 999px; padding: 3px 12px;
}
.profile-flair.is-cosmique {
    background: var(--cosmic-gradient); background-size: 300% 100%; animation: profile-shift 6s linear infinite;
    -webkit-background-clip: text; background-clip: text; color: transparent;
}

.profile-stat-bar {
    display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 18px;
    background: rgba(16, 24, 43, .78); backdrop-filter: blur(14px);
    border: 1px solid rgba(255,255,255,.06); border-top: none;
    border-radius: 0 0 26px 26px; padding: 70px 30px 22px;
}
.profile-stat-bar-group { display: flex; gap: 26px; }
.profile-mini-stat { text-align: left; }
.profile-mini-stat .v { font-size: 1.3rem; font-weight: 800; line-height: 1; }
.profile-mini-stat .l { font-size: .68rem; color: var(--muted); margin-top: 4px; text-transform: uppercase; letter-spacing: .04em; }
.profile-mini-stat.is-win .v { color: var(--good); }
.profile-stat-bar-right { text-align: right; font-size: .8rem; color: var(--muted); }
.profile-stat-bar-right .l1 { color: var(--text); font-weight: 600; }
@media (max-width: 640px) {
    .profile-stat-bar { justify-content: center; text-align: center; padding-top: 78px; }
    .profile-stat-bar-group { gap: 18px; }
    .profile-stat-bar-right { text-align: center; }
}

/* ---------- Section shell ---------- */
.profile-section { margin-top: 22px; }
.profile-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
.profile-card-head .profile-title {
    display: flex; align-items: center; gap: 9px; font-weight: 800; font-size: .84rem;
    text-transform: uppercase; letter-spacing: .06em;
}
.profile-card-head .profile-link { font-size: .78rem; color: var(--accent); text-decoration: none; font-weight: 600; }
.profile-card-head .profile-link:hover { text-decoration: underline; }

/* ---------- Widgets (Winrate / Activité) ---------- */
.profile-widget-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 700px) { .profile-widget-row { grid-template-columns: 1fr; } }
.profile-widget { padding: 20px 22px; }

.profile-toggle { display: inline-flex; background: var(--panel-2); border-radius: 999px; padding: 3px; gap: 2px; }
.profile-toggle button {
    border: none; background: none; color: var(--muted); font: inherit; font-size: .74rem; font-weight: 700;
    padding: 5px 13px; border-radius: 999px; cursor: pointer;
}
.profile-toggle button.is-active { background: linear-gradient(135deg, var(--indigo), var(--violet)); color: #fff; }

.profile-widget-big { font-size: 1.7rem; font-weight: 800; margin-top: 14px; display: flex; align-items: center; gap: 10px; }
.profile-widget-unit { font-size: .9rem; color: var(--muted); font-weight: 600; }
.profile-delta { font-size: .68rem; font-weight: 700; padding: 3px 8px; border-radius: 999px; display: inline-flex; align-items: center; gap: 2px; }
.profile-delta.is-up { background: rgba(52,211,153,.15); color: var(--good); }
.profile-delta.is-down { background: rgba(248,113,113,.15); color: var(--hit); }
.profile-delta.is-flat { background: rgba(142,161,194,.15); color: var(--muted); }
.profile-widget-spark { margin-top: 10px; width: 100%; height: 54px; }

/* ---------- Succès : spotlight + étagère ---------- */
.profile-tier-stats { display: flex; flex-wrap: wrap; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); padding: 14px 0; margin-bottom: 26px; }
.profile-tier-stat { padding: 0 20px; border-right: 1px solid var(--border); }
.profile-tier-stat:last-child { border-right: none; }
.profile-tier-stat .num { font-size: 1.3rem; font-weight: 800; }
.profile-tier-stat .lbl { font-size: .64rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; margin-top: 3px; }
@media (max-width: 700px) { .profile-tier-stat { padding: 0 12px 8px; border-right: none; } }

.profile-spotlight { display: flex; align-items: center; gap: 26px; margin-bottom: 26px; flex-wrap: wrap; }
.profile-spotlight-badge {
    width: 148px; height: 148px; flex: none;
    display: flex; align-items: center; justify-content: center;
    animation: profile-pulse-glow 2.8s ease-in-out infinite;
}
.profile-spotlight-badge img { width: 100%; height: 100%; object-fit: contain; }
.profile-spotlight-badge.is-zoomable { cursor: zoom-in; }
@keyframes profile-pulse-glow {
    0%, 100% { filter: drop-shadow(0 0 16px color-mix(in srgb, var(--tc, var(--t-cosmique)) 65%, transparent)); }
    50%      { filter: drop-shadow(0 0 28px color-mix(in srgb, var(--tc, var(--t-cosmique)) 80%, transparent)); }
}
.profile-spotlight-info .kicker { font-size: .66rem; text-transform: uppercase; letter-spacing: .08em; font-weight: 700; color: var(--tc, var(--t-cosmique)); margin-bottom: 5px; }
.profile-spotlight-info h3 { margin: 0 0 4px; font-size: 1.25rem; }
.profile-spotlight-info p { margin: 0; color: var(--muted); font-size: .84rem; max-width: 420px; }

.profile-shelf-wrap { position: relative; }
/* overflow-x: auto force le calcul de overflow-y à "auto" aussi (règle
   CSS : un seul axe ne peut pas rester "visible" si l'autre défile) —
   le halo (drop-shadow) des trophées est donc rogné en haut par la boîte
   du carrousel lui-même si elle n'a pas assez de padding-top. */
.profile-shelf {
    display: flex; align-items: flex-start; gap: 20px; overflow-x: auto;
    padding: 26px 4px 14px; scrollbar-width: thin; scrollbar-color: var(--border) transparent;
    margin-top: -22px;
    mask-image: linear-gradient(90deg, transparent, black 24px, black calc(100% - 24px), transparent);
}
.profile-shelf::-webkit-scrollbar { height: 6px; }
.profile-shelf::-webkit-scrollbar-track { background: transparent; }
.profile-shelf::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; }
.profile-shelf::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.profile-trophy { flex: none; text-align: center; width: 92px; transition: transform .2s ease; }
.profile-trophy:hover { transform: translateY(-6px) scale(1.04); }
.profile-trophy-icon {
    width: 88px; height: 88px; margin: 0 auto 6px; cursor: zoom-in;
    display: flex; align-items: center; justify-content: center;
    filter: drop-shadow(0 0 10px color-mix(in srgb, var(--tc) 55%, transparent));
    transition: filter .2s ease;
}
.profile-trophy:hover .profile-trophy-icon { filter: drop-shadow(0 0 16px color-mix(in srgb, var(--tc) 75%, transparent)); }
.profile-trophy-icon img { width: 100%; height: 100%; object-fit: contain; }
.profile-trophy-name { font-size: .7rem; font-weight: 700; line-height: 1.25; }
.profile-trophy-game { font-size: .6rem; color: var(--muted); margin-top: 2px; }
.profile-empty-state { color: var(--muted); font-size: .85rem; padding: 10px 2px 4px; }

/* ---------- Statistiques par jeu / historique (tables façon "board") ---------- */
.profile-board-panel { padding: 22px 24px 10px; }
.profile-board-sort { font-size: .76rem; color: var(--muted); background: var(--panel-2); border: 1px solid var(--border); border-radius: 999px; padding: 5px 13px; }
.profile-board-header, .profile-history-header {
    display: flex; align-items: center; gap: 14px; padding: 0 4px 10px;
    font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em;
    border-bottom: 1px solid var(--border);
}
.profile-board-header .bh-game, .profile-history-header .hh-game { flex: 1; }
.profile-board-header .bh-col { width: 84px; text-align: right; }
.profile-board-row, .profile-history-row {
    display: flex; align-items: center; gap: 14px; padding: 13px 4px; border-bottom: 1px solid var(--border);
    transition: background .15s ease;
}
.profile-board-row:last-child, .profile-history-row:last-child { border-bottom: none; }
.profile-board-row:hover, .profile-history-row:hover { background: rgba(255,255,255,.02); }
.profile-board-icon, .profile-history-icon {
    width: 36px; height: 36px; border-radius: 10px; flex: none; font-size: 1.05rem;
    display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--gc, var(--accent)) 20%, var(--panel-2));
}
.profile-board-game, .profile-history-game { flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px; }
.profile-board-name, .profile-history-name { font-weight: 700; font-size: .88rem; display: flex; align-items: center; gap: 6px; }
.profile-board-col { width: 84px; text-align: right; font-weight: 700; font-size: .88rem; }
.profile-board-col.is-win.is-above { color: var(--good); }
.profile-board-col.is-win.is-below { color: var(--hit); }
@media (max-width: 620px) {
    .profile-board-header .bh-col:nth-child(2), .profile-board-col:nth-child(2) { display: none; }
    .profile-board-header .bh-col, .profile-board-col { width: 60px; }
}

.profile-history-panel { padding: 22px 24px 10px; }
.profile-history-header .hh-result { width: 92px; }
.profile-history-header .hh-detail { width: 170px; }
.profile-history-header .hh-date { width: 110px; text-align: right; }
.profile-history-result { width: 92px; }
.profile-result-pill { font-size: .68rem; font-weight: 700; padding: 3px 10px; border-radius: 999px; display: inline-block; white-space: nowrap; }
.profile-result-pill.is-win { background: rgba(52,211,153,.15); color: var(--good); }
.profile-result-pill.is-loss { background: rgba(248,113,113,.15); color: var(--hit); }
.profile-result-pill.is-draw { background: rgba(142,161,194,.15); color: var(--muted); }
.profile-history-detail { width: 170px; font-size: .8rem; color: var(--muted); }
.profile-history-date { width: 110px; text-align: right; font-size: .8rem; color: var(--muted); }
@media (max-width: 700px) {
    .profile-history-header .hh-detail, .profile-history-detail { display: none; }
}
@media (max-width: 480px) {
    .profile-history-header .hh-date, .profile-history-date { display: none; }
}

/* ============ FarAway ============ */
/* Prototypé dans static/preview.html puis static/preview2.html (option
   terrain englobant Rivière + aire de jeu + Sanctuaires, validée par
   l'utilisateur) — voir gamespace_faraway_game.md. Réutilise les variables
   globales (--accent surchargé par jeu via style="--accent: {{ game.accent }}",
   --panel/--border/--text/--muted/--good déjà définies en tête de fichier)
   plutôt qu'une palette dédiée comme dans le prototype autonome. */
.fw-topbar {
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    background: linear-gradient(165deg, color-mix(in srgb, var(--accent) 10%, transparent), transparent 70%), var(--panel);
    border: 1px solid var(--border); border-radius: 14px; padding: 14px 20px; margin-bottom: 18px;
}
.fw-round { display: flex; align-items: baseline; gap: 10px; }
.fw-round-label { font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); }
.fw-round-num { font-size: 1.6rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.fw-round-num small { font-size: .85rem; color: var(--muted); font-weight: 600; }
.fw-phase { text-align: right; }
.fw-phase-name { font-size: 1rem; font-weight: 700; }
.fw-phase-hint { font-size: .74rem; color: var(--muted); margin-top: 2px; }
.fw-players { display: flex; gap: 8px; flex-wrap: wrap; }
.fw-player-chip {
    width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
    font-size: .72rem; font-weight: 800; background: var(--panel-2); border: 1px solid var(--border); color: var(--muted);
    cursor: pointer; transition: transform .12s, box-shadow .12s;
}
.fw-player-chip:hover { transform: translateY(-2px); }
.fw-player-chip.me { background: var(--accent); color: #1a1206; border-color: var(--accent); }
/* Chip du terrain actuellement affiché — distinct de .me (qui marque "Moi"
   en permanence) : n'importe quel joueur peut regarder le terrain de
   n'importe qui, .viewing suit le clic, pas l'identité. */
.fw-player-chip.viewing { box-shadow: 0 0 0 3px rgba(238,243,236,.5); }
.fw-player-chip.fw-chip-ready { border-color: var(--good); }
.fw-player-chip.fw-chip-turn { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 55%, transparent); }

/* ---------- Cartes Région (carrées) ----------
   <img> réelle + object-fit plutôt qu'un background-image CSS : avec un
   border-radius, un fond posé en background-image laisse voir un léger
   liseré/seam aux coins sur certains navigateurs. */
.fw-card {
    position: relative; aspect-ratio: 1 / 1; border-radius: 14px; overflow: hidden;
    cursor: pointer; box-shadow: 0 5px 16px rgba(0,0,0,.6);
    transition: transform .15s ease, box-shadow .15s ease; border: 2px solid transparent;
}
.fw-card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fw-card.clickable:hover { transform: translateY(-4px); box-shadow: 0 10px 24px rgba(0,0,0,.65); }
.fw-card.selected { border-color: var(--accent); }

/* Retournement (rivière révélée face cachée -> face visible) : structure à
   double face en 3D plutôt qu'un simple fondu — la carte "tourne" réellement
   sur elle-même. */
.fw-card.fw-flip { perspective: 900px; }
.fw-card.fw-flip:not(.revealed) { pointer-events: none; }
.fw-card.fw-flip:not(.revealed) .fw-zoom-btn { display: none; }
.fw-flip-inner {
    position: relative; width: 100%; height: 100%; transform-style: preserve-3d;
    transition: transform .55s cubic-bezier(.45, .05, .2, 1);
}
.fw-card.fw-flip.revealed .fw-flip-inner { transform: rotateY(180deg); }
.fw-flip-face { position: absolute; inset: 0; backface-visibility: hidden; }
.fw-flip-face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fw-flip-back { transform: rotateY(180deg); }

/* Bouton loupe (zoom) — même coin sur toutes les cartes, Région comme
   Sanctuaire, stopPropagation pour ne jamais déclencher l'action de la
   carte (jouer/piocher/sacrifier) en dessous. */
.fw-zoom-btn {
    position: absolute; bottom: 5px; right: 5px; z-index: 3; width: 22px; height: 22px;
    border-radius: 50%; background: rgba(8,12,10,.62); border: 1px solid rgba(255,255,255,.25);
    display: flex; align-items: center; justify-content: center; font-size: .72rem; cursor: zoom-in;
    opacity: .55; transition: opacity .15s, transform .15s;
}
.fw-zoom-btn:hover { opacity: 1; transform: scale(1.12); }

/* ---------- Main (Phase 1) — reste HORS terrain ---------- */
.fw-stage { margin-bottom: 20px; }
.fw-stage-title { font-size: .8rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); margin: 0 0 10px; }
.fw-hand { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 14px; max-width: 480px; margin: 0 auto; }

/* ================= Terrain englobant Rivière + aire de jeu + Sanctuaires =================
   Même gabarit que .pres-center/.uno-center : ratio 4/3, cover, contenu
   vivant DANS le cadre plutôt qu'en dehors — les trois zones flottent
   directement sur l'illustration (pas de panneau opaque derrière), avec un
   léger voile pour garder les libellés lisibles quel que soit le terrain. */
.fw2-terrain {
    position: relative; width: min(940px, 96vw); aspect-ratio: 4 / 3; margin: 0 auto 22px;
    border-radius: 20px; overflow: hidden; background-color: var(--bg-soft, var(--panel-2));
    background-size: cover; background-position: center;
    border: 1px solid var(--border); box-shadow: 0 14px 34px rgba(0,0,0,.5);
    transition: background-image .25s ease;
}
.fw2-terrain-veil {
    position: absolute; inset: 0; z-index: 1;
    background: linear-gradient(180deg, rgba(6,10,8,.05) 0%, rgba(6,10,8,.18) 55%, rgba(6,10,8,.62) 100%);
}
.fw2-terrain-inner {
    position: absolute; inset: 0; z-index: 2; padding: 18px 24px 20px;
    display: flex; flex-direction: column; justify-content: space-between; gap: 10px;
}
.fw2-zone-title {
    font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .1em;
    color: #fff; text-shadow: 0 1px 6px rgba(0,0,0,.85); margin: 0 0 8px;
}
.fw2-river { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.fw2-river .fw-card { width: 128px; }
/* Carte de rivière déjà prise (par un bot, avant même que ce client n'ait
   fini d'animer la révélation) : reste visible mais assombrie + non
   cliquable, plutôt que de disparaître sans avoir jamais été montrée. */
.fw-card.fw-river-taken { filter: brightness(.4) saturate(.5); cursor: default; }
.fw-river-taken-tag {
    position: absolute; inset: auto 4px 4px 4px; z-index: 4;
    background: rgba(4,6,5,.8); color: #fff; font-size: .62rem; font-weight: 700;
    text-align: center; padding: 2px 4px; border-radius: 6px; text-shadow: 0 1px 3px rgba(0,0,0,.8);
}

.fw2-line-row { display: flex; justify-content: center; }
.fw2-line { display: grid; grid-template-columns: repeat(4, 128px); gap: 8px; }
.fw2-line .fw-card { cursor: default; }
.fw2-line .fw-slot-empty {
    aspect-ratio: 1/1; border-radius: 10px; border: 2px dashed rgba(255,255,255,.4);
    display: flex; align-items: center; justify-content: center; color: rgba(255,255,255,.7); font-size: 1.2rem;
    backdrop-filter: blur(1px);
}

/* min-height = hauteur réelle d'une carte Sanctuaire (76px de large, ratio
   756/1164 -> ~117px) : réservée dès le départ, vide ou pleine, pour que le
   passage de 0 à 1 Sanctuaire ne redistribue jamais l'espace vertical du
   terrain (justify-content: space-between) et ne fasse pas sauter les titres. */
.fw2-sanct-rack { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; min-height: 118px; align-items: flex-end; }
.fw2-sanct-rack .fw-empty-hint { font-size: .76rem; color: #fff; text-shadow: 0 1px 6px rgba(0,0,0,.85); align-self: center; }
.fw-sanct-card {
    position: relative; aspect-ratio: 756 / 1164; width: 76px; border-radius: 10px; overflow: hidden;
    box-shadow: 0 4px 12px rgba(0,0,0,.6); border: 2px solid transparent; flex: none;
}
.fw-sanct-card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fw-sanct-card.clickable { cursor: pointer; transition: transform .15s ease; }
.fw-sanct-card.clickable:hover { transform: translateY(-4px); }
.fw-sanct-card.selected { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent); }

/* ---------- Modale : choix de Sanctuaire / sacrifice ---------- */
.fw-modal-backdrop {
    position: fixed; inset: 0; background: rgba(5,8,6,.72); backdrop-filter: blur(3px);
    display: flex; align-items: center; justify-content: center; z-index: 60; padding: 20px;
}
.fw-modal { background: var(--panel); border: 1px solid var(--border); border-radius: 16px; padding: 22px; max-width: 560px; width: 100%; text-align: center; }
.fw-modal h3 { margin: 0 0 6px; }
.fw-modal p { color: var(--muted); font-size: .86rem; margin: 0 0 16px; }
.fw-modal-choices { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.fw-modal-choices .fw-sanct-card { width: 96px; }
.fw-sac-actions { margin-top: 16px; }

/* Transparence à la demande (bouton 👁️) : voir son propre terrain (aire de
   jeu, Sanctuaires déjà posés) à travers la fenêtre pour aider à décider
   quel Sanctuaire garder, sans avoir à la fermer. */
.fw-modal-see-btn {
    position: absolute; top: 14px; right: 14px; width: 30px; height: 30px; border-radius: 50%;
    background: var(--panel-2); border: 1px solid var(--border); color: var(--text);
    display: flex; align-items: center; justify-content: center; font-size: .85rem; cursor: pointer;
    opacity: .7; transition: opacity .15s;
}
.fw-modal-see-btn:hover { opacity: 1; }
.fw-modal { position: relative; }
/* Déplacement à la souris (voir retour utilisateur : la transparence seule
   ne suffit pas toujours) — on attrape la fenêtre par son titre. Le
   backdrop reste fixe (couvre tout l'écran) : seul le panneau glisse. */
.fw-modal-draggable-handle { cursor: move; user-select: none; }
.fw-modal-backdrop.see-through { background: transparent; backdrop-filter: none; }
.fw-modal-backdrop.see-through .fw-modal {
    background: color-mix(in srgb, var(--panel) 30%, transparent);
    border-color: rgba(255,255,255,.15); box-shadow: none;
}
.fw-modal-backdrop.see-through .fw-modal p,
.fw-modal-backdrop.see-through .fw-modal h3 { text-shadow: 0 1px 6px rgba(0,0,0,.85); }

/* ---------- Fin de partie : décompte droite à gauche (par joueur) ---------- */
.fw-end-overlay {
    position: fixed; inset: 0; background: rgba(4,6,5,.92); z-index: 70;
    display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 24px;
}
/* Chips cliquables pour regarder le décompte de n'importe quel joueur, pas
   seulement le sien (voir retour utilisateur) — mêmes chips que la barre de
   manche (.fw-player-chip), juste posées en haut de l'overlay plein écran. */
.fw-end-players { display: flex; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; justify-content: center; }
.fw-end-score { font-family: "Bitter", Georgia, serif; font-size: 3.2rem; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--accent); }
.fw-end-score-label { font-size: .74rem; text-transform: uppercase; letter-spacing: .14em; color: var(--muted); margin-bottom: 4px; }
.fw-end-line { display: flex; gap: 10px; margin: 26px 0; flex-wrap: wrap; justify-content: center; max-width: 920px; }
.fw-end-line .fw-card { width: 120px; }
.fw-end-line .fw-card.pending { filter: brightness(.35) saturate(.4); }
.fw-end-sanct { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; max-width: 640px; min-height: 96px; }
.fw-end-popup {
    position: absolute; transform: translate(-50%, -140%); font-weight: 800; color: var(--good);
    font-size: 1rem; opacity: 0; pointer-events: none; text-shadow: 0 2px 6px rgba(0,0,0,.6);
    animation: fw-popup 1.1s ease forwards;
}
@keyframes fw-popup {
    0% { opacity: 0; transform: translate(-50%, -110%); }
    25% { opacity: 1; transform: translate(-50%, -160%); }
    75% { opacity: 1; transform: translate(-50%, -160%); }
    100% { opacity: 0; transform: translate(-50%, -190%); }
}

/* ---------- Zoom plein écran sur une carte ---------- */
.fw-zoom-overlay {
    position: fixed; inset: 0; background: rgba(4,6,5,.86); z-index: 90; cursor: zoom-out;
    display: flex; align-items: center; justify-content: center; padding: 30px;
}
.fw-zoom-overlay img { max-width: min(70vh, 90vw); max-height: 82vh; border-radius: 58px; box-shadow: 0 20px 60px rgba(0,0,0,.6); }

.fw-empty-hint { font-size: .76rem; color: var(--muted); }

/* ============ Perudo ============ */
/* Palette "verre chaud" locale au jeu — reprise telle quelle du prototype
   (static/preview.html), qui définissait son propre :root plutôt que
   d'utiliser le thème sombre/bleu du reste du site : --accent (vert) vient
   déjà de game.accent (posé inline sur #game), --pd-gold est le second ton
   du dégradé (mises/bouton principal), --pd-glass/--pd-border habillent
   tous les panneaux "verre dépoli" (sièges, mise, contrôles, révélation).
   Portée ici plutôt que réutiliser var(--accent-2)/var(--bad) globaux : ni
   l'un ni l'autre n'existe au niveau :root du site (seul .room-panel définit
   --accent-2 localement) — les utiliser aurait rendu ces styles invisibles. */
#game[data-slug="perudo"] {
    --pd-gold: #eab308;
    --pd-glass: linear-gradient(165deg, rgba(46, 30, 15, .82), rgba(14, 9, 5, .88));
    --pd-border: rgba(234, 179, 8, .26);
    --pd-bad: #f87171;
}

.pd-meta { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; align-items: center; margin-bottom: 8px; }
.pd-round-pill, .pd-dice-pill {
    font-size: .74rem; font-weight: 700; color: var(--muted);
    background: var(--panel-2); border: 1px solid var(--border); border-radius: 999px;
    padding: 3px 12px;
}

/* Table de jeu : même gabarit que .table-card du prototype — voile sombre +
   terrain (posé via --pd-terrain-url, voir applyTerrainFromView dans
   perudo.js) + bordure dégradée verte/or en 3 couches de background-image.
   Pas d'overflow:hidden (comme le prototype) : le contenu (relance,
   contrôles) peut légitimement dépasser l'aspect-ratio 4/3 préféré certains
   tours, jamais tronqué. */
.pd-table-card {
    /* 847px : largeur exacte du prototype (.page { max-width: 847px }),
       reprise telle quelle — à cette largeur, un ratio 4/3 donne une hauteur
       de ~635px qui colle à la hauteur RÉELLE du contenu (sièges + mise +
       dés + contrôles, voir les min-height ci-dessous). Une largeur plus
       étroite ne suffit PAS pour ce même contenu en pixels fixes : la carte
       devient plus haute que 4/3 et background-size:cover doit rogner les
       CÔTÉS pour compenser — bug constaté en prod avec 760px, voir retour
       utilisateur. */
    width: min(847px, 96vw); margin: 10px auto 16px; position: relative;
    border: 1px solid transparent; border-radius: 16px; box-shadow: var(--shadow);
    padding: 20px; aspect-ratio: 4 / 3;
    --pd-terrain-url: none;
    background-image:
        radial-gradient(ellipse 120% 90% at 50% 8%, rgba(8,13,26,.15) 0%, rgba(8,13,26,.55) 60%, rgba(4,6,12,.8) 100%),
        var(--pd-terrain-url),
        linear-gradient(135deg, color-mix(in srgb, var(--accent) 55%, transparent), color-mix(in srgb, var(--pd-gold) 55%, transparent), transparent 60%);
    background-origin: padding-box, padding-box, border-box;
    background-clip: padding-box, padding-box, border-box;
    background-size: auto, cover, auto;
    background-position: 0 0, center, 0 0;
    background-repeat: no-repeat;
    background-color: var(--bg-soft);
}

.pd-bots-row { display: flex; gap: 8px; justify-content: center; margin-bottom: 18px; flex-wrap: wrap; }
.pd-seat {
    flex: 0 1 130px; max-width: 150px; background-image: var(--pd-glass); border: 1px solid var(--pd-border); border-radius: 16px;
    backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
    box-shadow: 0 10px 20px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.06);
    padding: 11px 12px 9px; text-align: center; position: relative; transition: border-color .2s, box-shadow .2s, opacity .2s;
}
.pd-seat.current {
    border-color: var(--pd-gold);
    box-shadow: 0 0 0 1px var(--pd-gold), 0 0 18px color-mix(in srgb, var(--pd-gold) 55%, transparent), 0 10px 20px rgba(0,0,0,.45);
}
.pd-seat.eliminated { opacity: .4; }
/* Spectateur uniquement : ce joueur est celui dont les dés sont actuellement
   affichés en grand dans .pd-your-seat (voir renderSeats/selectSpectateTarget
   dans perudo.js). */
.pd-seat.viewing { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), 0 0 14px color-mix(in srgb, var(--accent) 45%, transparent); }
/* Spectateur uniquement : toute la carte (pas juste le pseudo) est cliquable
   pour choisir de qui on regarde les vrais dés (affichés en grand dans
   .pd-your-seat) — jamais active pour un joueur en partie, qui ne reçoit
   jamais p.dice des adversaires côté serveur. */
.pd-seat-clickable { cursor: pointer; }
.pd-seat-clickable:hover { border-color: var(--pd-gold); box-shadow: 0 0 0 1px color-mix(in srgb, var(--pd-gold) 60%, transparent), 0 10px 20px rgba(0,0,0,.45); }
.pd-seat-clickable:hover .pd-seat-name { color: var(--pd-gold); }
.pd-seat-name { font-size: .82rem; font-weight: 700; margin-bottom: 5px; letter-spacing: .01em; text-shadow: 0 1px 3px rgba(0,0,0,.6); }
.pd-seat-dice { display: flex; gap: 4px; justify-content: center; flex-wrap: wrap; }
.pd-seat-die-back {
    width: 13px; height: 13px; border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, #e8dcc4, #b89b6a 70%, #8a6f42);
    box-shadow: inset 0 1px 1px rgba(255,255,255,.4), 0 1px 2px rgba(0,0,0,.4);
}
.pd-seat-out-label { font-size: .68rem; color: var(--pd-bad); font-weight: 700; margin-top: 3px; }

/* Hauteur réservée à la plus grande mise en page possible (pas de mise /
   mise affichée / mise + badge Palifico) : sans ça la carte terrain change
   de ratio dès la 1ère enchère et background-size:cover doit zoomer/rogner
   plus fort (comportement constaté sur le prototype, voir son commentaire). */
.pd-bid-center { position: relative; z-index: 2; text-align: center; margin: 10px 0 22px; min-height: 126px; display: flex; flex-direction: column; justify-content: center; }
.pd-no-bid { color: var(--muted); font-size: .88rem; text-shadow: 0 1px 3px rgba(0,0,0,.6); }
.pd-bid-display {
    display: inline-flex; align-items: center; gap: 10px; padding: 12px 26px; border-radius: 999px; margin: 0 auto;
    background-image: radial-gradient(circle at 32% 26%, rgba(234,179,8,.28), transparent 60%), var(--pd-glass);
    border: 1px solid var(--pd-border); backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
    box-shadow: 0 10px 22px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.08);
    font-size: 1.5rem; font-weight: 800; color: var(--text); text-shadow: 0 1px 3px rgba(0,0,0,.5);
}
.pd-bid-face { font-size: 2rem; display: inline-flex; align-items: center; }
.pd-bid-by { font-size: .74rem; color: var(--muted); margin-top: 8px; text-shadow: 0 1px 3px rgba(0,0,0,.6); }
.palifico-badge {
    display: inline-block; margin-bottom: 8px; padding: 4px 14px; border-radius: 999px; font-size: .7rem; font-weight: 700;
    background: rgba(56,189,248,.18); border: 1px solid rgba(56,189,248,.4); color: #7dd3fc; text-shadow: 0 1px 3px rgba(0,0,0,.6);
}

.pd-your-seat { position: relative; z-index: 2; padding: 16px 18px 14px; margin-bottom: 16px; }
.pd-dice-stage { position: relative; height: 150px; margin: 4px auto 0; }
/* Gobelet dessiné (pas d'asset dédié) : trapèze en clip-path, dégradé bois
   chaud, même silhouette que .seat-cup-mini ci-dessous en miniature. */
.pd-dice-cup {
    position: absolute; left: 50%; top: -14px; width: 88px; height: 58px; margin-left: -44px;
    background: linear-gradient(160deg, #8a5a30, #4e2e14 65%, #2c1809);
    clip-path: polygon(16% 0%, 84% 0%, 100% 100%, 0% 100%);
    border-radius: 3px 3px 9px 9px;
    box-shadow: inset 0 -10px 16px rgba(0,0,0,.45), 0 8px 16px rgba(0,0,0,.5);
    z-index: 2; pointer-events: none;
}
.pd-dice-cup::after {
    content: ""; position: absolute; left: 10%; right: 10%; top: 5px; height: 5px; border-radius: 50%;
    background: rgba(0,0,0,.4);
}
.pd-dice-tosses { position: absolute; inset: 0; }
.pd-seat-cup-mini {
    display: inline-block; width: 20px; height: 14px; margin-right: 4px; vertical-align: middle;
    background: linear-gradient(160deg, #8a5a30, #4e2e14 65%, #2c1809);
    clip-path: polygon(16% 0%, 84% 0%, 100% 100%, 0% 100%); border-radius: 1px 1px 3px 3px;
}
/* Face "1" du dé Perudo : la mascotte toucan (voir static/js/perudo.js)
   plutôt qu'un pip central, en plein cadre (pas de texte à recadrer). */
.die3d-face .die3d-logo { grid-column: 1 / 4; grid-row: 1 / 4; width: 78%; height: 78%; align-self: center; justify-self: center; object-fit: contain; }
.pd-face-glyph-inline { height: 1.25em; width: 1.15em; object-fit: contain; vertical-align: -0.3em; }

/* Hauteur réservée à la plus grande des 2 mises en page (contrôles de mise
   vs simple message d'attente) — même raison que .pd-bid-center : sans ça
   la carte change de hauteur à chaque tour. */
.pd-controls-zone { position: relative; z-index: 2; min-height: 152px; display: flex; flex-direction: column; justify-content: center; }
.pd-wait-note { text-align: center; color: var(--muted); font-size: .84rem; text-shadow: 0 1px 3px rgba(0,0,0,.6); }
.pd-controls { display: flex; flex-direction: column; gap: 12px; }
.pd-qty-row { display: flex; align-items: center; justify-content: center; gap: 14px; }
.pd-qty-row .qty-btn {
    width: 36px; height: 36px; border-radius: 50%; border: 1px solid var(--pd-border); background-image: var(--pd-glass);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); box-shadow: 0 4px 10px rgba(0,0,0,.4);
    color: var(--text); font-size: 1.2rem; font-weight: 700; cursor: pointer;
}
.pd-qty-row .qty-btn:disabled { opacity: .3; cursor: not-allowed; }
.pd-qty-row .qty-val { font-size: 1.6rem; font-weight: 800; min-width: 44px; text-align: center; font-variant-numeric: tabular-nums; text-shadow: 0 1px 3px rgba(0,0,0,.6); }
.pd-face-row { display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; }
.pd-face-row .face-btn {
    width: 46px; height: 46px; border-radius: 12px; border: 1px solid var(--pd-border); background-image: var(--pd-glass);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); box-shadow: 0 4px 10px rgba(0,0,0,.4);
    color: var(--text); font-size: 1.7rem; cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.pd-face-row .face-btn.is-active {
    background-image: none; background: linear-gradient(120deg, var(--accent), var(--pd-gold));
    border-color: transparent; color: #04160c; box-shadow: 0 4px 14px color-mix(in srgb, var(--pd-gold) 45%, transparent);
}
.pd-face-row .face-btn:disabled { opacity: .3; cursor: not-allowed; }
.pd-action-row { display: flex; gap: 10px; justify-content: center; margin-top: 4px; flex-wrap: wrap; }
.pd-action-row .action-btn {
    font: inherit; font-size: .92rem; font-weight: 800; cursor: pointer; padding: 11px 22px; border-radius: 999px; border: none;
    box-shadow: 0 6px 16px rgba(0,0,0,.4);
}
.pd-action-row .bid-btn { background: linear-gradient(120deg, var(--accent), var(--pd-gold)); color: #04160c; }
.pd-action-row .bid-btn:disabled { opacity: .35; cursor: not-allowed; filter: grayscale(.4); }
.pd-action-row .calzo-btn { background: linear-gradient(120deg, #0ea5e9, #38bdf8); color: #04121f; }
.pd-action-row .calzo-btn:disabled { opacity: .3; cursor: not-allowed; }
.pd-action-row .doubt-btn { background: linear-gradient(120deg, #b91c1c, var(--pd-bad)); color: #2c0a0a; }
.pd-action-row .doubt-btn:disabled { opacity: .3; cursor: not-allowed; }

/* Révélation (doute/Calzo) : plein écran, dés de tous les joueurs alignés,
   ceux qui correspondent à la face misée surlignés en or — même motif que
   .reveal-strip du prototype. */
.pd-reveal-overlay {
    position: fixed; inset: 0; z-index: 80; background: rgba(6,12,26,.9);
    display: flex; align-items: center; justify-content: center; padding: 24px;
}
.pd-reveal-box {
    display: flex; flex-direction: column; align-items: center; gap: 10px; max-width: 900px; width: 100%;
    padding: 16px 14px; background-image: var(--pd-glass); border: 1px solid var(--pd-border); border-radius: 16px;
    backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px); box-shadow: 0 10px 20px rgba(0,0,0,.4);
}
.pd-reveal-strip { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.pd-reveal-col { text-align: center; padding: 0 4px; }
.pd-reveal-col .pd-reveal-name { font-size: .76rem; font-weight: 700; margin-bottom: 4px; color: var(--muted); }
.pd-reveal-col .pd-reveal-dice { display: flex; gap: 3px; }
.pd-reveal-die { font-size: 1.6rem; line-height: 1; }
.pd-reveal-die img { height: 1.1em; width: 1em; object-fit: contain; vertical-align: -0.15em; }
.pd-reveal-die.match { color: var(--pd-gold); text-shadow: 0 0 8px color-mix(in srgb, var(--pd-gold) 70%, transparent); }
.pd-reveal-verdict { text-align: center; font-size: .9rem; font-weight: 700; margin-top: 8px; text-shadow: 0 1px 4px rgba(0,0,0,.7); }

/* Bouton "Manche suivante" (hôte, dans l'overlay de révélation) — plus
   affirmé qu'un simple .action-btn : plus grand, halo doré pulsé pour
   attirer l'œil vers la seule action possible une fois les dés regardés,
   petit dé qui vacille au survol. */
.pd-next-round-btn {
    margin-top: 10px; font: inherit; font-size: 1.05rem; font-weight: 800; cursor: pointer;
    display: inline-flex; align-items: center; gap: 10px;
    padding: 14px 32px; border-radius: 999px; border: none; color: #04160c;
    background: linear-gradient(120deg, var(--accent), var(--pd-gold));
    box-shadow: 0 8px 22px rgba(0,0,0,.5), 0 0 0 0 color-mix(in srgb, var(--pd-gold) 55%, transparent);
    animation: pd-next-round-pulse 2.2s ease-in-out infinite;
    transition: transform .15s ease, filter .15s ease;
}
.pd-next-round-btn:hover { transform: translateY(-2px); filter: brightness(1.08); }
.pd-next-round-btn:active { transform: translateY(0); }
.pd-next-round-btn-icon { display: inline-block; font-size: 1.2em; animation: pd-next-round-wobble 2.2s ease-in-out infinite; }
@keyframes pd-next-round-pulse {
    0%, 100% { box-shadow: 0 8px 22px rgba(0,0,0,.5), 0 0 0 0 color-mix(in srgb, var(--pd-gold) 45%, transparent); }
    50% { box-shadow: 0 8px 26px rgba(0,0,0,.55), 0 0 0 9px color-mix(in srgb, var(--pd-gold) 0%, transparent); }
}
@keyframes pd-next-round-wobble {
    0%, 100% { transform: rotate(0deg); }
    25% { transform: rotate(-12deg); }
    75% { transform: rotate(12deg); }
}
@media (prefers-reduced-motion: reduce) {
    .pd-next-round-btn, .pd-next-round-btn-icon { animation: none; }
}
