/* Palette partagée avec L'Etabli (static/game-builder.html) : neutres
   gris/blanc à haut contraste, un seul accent bleu, thèmes clair/sombre. */
:root {
  --bg: #f4f5f7;
  --panel: #ffffff;
  --panel-raised: #eef0f3;
  --panel-sunken: #e4e6ea;
  --text: #16181d;
  --text-soft: #474b53;
  --text-faint: #767b85;
  --line: #dadde2;
  --accent: #2f5fd6;
  --accent-strong: #1f47ad;
  --danger: #c8402f;
  --success: #1c8a53;
  --shadow: 0 1px 2px rgba(16, 18, 22, 0.06), 0 4px 14px rgba(16, 18, 22, 0.05);
  --radius: 6px;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Code", Consolas, "Liberation Mono", monospace;
  /* Carte de base, mise à l'échelle par la taille déclarée d'une zone. */
  --card-w: 60px;
  --card-h: 84px;
  --card-scale: 1;
  /* Thème courant, 0 clair ou 1 sombre : choisit entre les deux teintes
     résolues par le serveur (`--<nom>-light`/`-dark`, voir `themed` plus
     bas et `setThemedColor` dans render/cards.js). */
  --dark: 0;
}
@media (prefers-color-scheme: dark) {
  :root {
    --dark: 1;
    --bg: #101216; --panel: #191c21; --panel-raised: #21252c; --panel-sunken: #0c0e11;
    --text: #edeef1; --text-soft: #b0b4bc; --text-faint: #767c88; --line: #2b2f37;
    --accent: #6d97f5; --accent-strong: #94b4f8; --danger: #e2695b; --success: #4fbf82;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 8px 24px rgba(0, 0, 0, 0.4);
  }
}
:root[data-theme="dark"] {
  --dark: 1;
  --bg: #101216; --panel: #191c21; --panel-raised: #21252c; --panel-sunken: #0c0e11;
  --text: #edeef1; --text-soft: #b0b4bc; --text-faint: #767c88; --line: #2b2f37;
  --accent: #6d97f5; --accent-strong: #94b4f8; --danger: #e2695b; --success: #4fbf82;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 8px 24px rgba(0, 0, 0, 0.4);
}
:root[data-theme="light"] {
  --dark: 0;
  --bg: #f4f5f7; --panel: #ffffff; --panel-raised: #eef0f3; --panel-sunken: #e4e6ea;
  --text: #16181d; --text-soft: #474b53; --text-faint: #767b85; --line: #dadde2;
  --accent: #2f5fd6; --accent-strong: #1f47ad; --danger: #c8402f; --success: #1c8a53;
  --shadow: 0 1px 2px rgba(16, 18, 22, 0.06), 0 4px 14px rgba(16, 18, 22, 0.05);
}
/* Thème de la table déclaré par le jeu (`view.theme`, `applyTheme`) : fond
   de page nuancé de la teinte `table`, accent remplacé. */
:root.theme-table {
  --theme-table: color-mix(in srgb, var(--theme-table-dark) calc(var(--dark) * 100%), var(--theme-table-light));
}
:root.theme-table body { background: color-mix(in srgb, var(--theme-table) 20%, var(--bg)); }
:root.theme-accent {
  --accent: color-mix(in srgb, var(--theme-accent-dark) calc(var(--dark) * 100%), var(--theme-accent-light));
  --accent-strong: color-mix(in srgb, var(--accent) 78%, var(--text));
}

* { box-sizing: border-box; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.5;
  margin: 0;
}
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }
code { font-family: var(--mono); background: var(--panel-raised); padding: 1px 5px; border-radius: 3px; font-size: 12px; }

.shell { max-width: 980px; margin: 0 auto; padding: 24px 20px 60px; display: flex; flex-direction: column; gap: 18px; }

/* Téléphone : moins de marge perdue sur les côtés, la page reste lisible
   d'une seule main (voir docs/front-end.md). Cibles tactiles (`min-height:
   44px` sur les boutons) et sécurité anti-débordement (`.hint`, `.chip-row`)
   s'appliquent à toutes les largeurs, pas seulement ici. */
@media (max-width: 640px) {
  .shell { padding: 14px 12px 40px; gap: 14px; }
}

/* Navigation du site : liens en pastilles, cibles tactiles de 44px. */
.top-nav { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: -6px; }
.top-nav-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--panel);
  color: var(--text-soft);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
}
.top-nav-link:hover { color: var(--accent-strong); border-color: var(--accent); }

.masthead { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.masthead h1 { font-size: 22px; margin: 0 0 4px; }
.masthead .tag {
  font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-faint); border: 1px solid var(--line); border-radius: 999px; padding: 3px 10px;
}
.viewer-badge { font-size: 13px; color: var(--text-soft); }
.viewer-badge[hidden] { display: none; }

/* Écran de connexion : remplace l'ancien prompt() natif. */
.join-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 20px 22px 22px;
  max-width: 420px;
}
.join-card h2 { font-size: 16px; margin: 0 0 6px; }
.join-card[hidden] { display: none; }
.join-card .action-feedback { margin-top: 10px; min-height: 0; }
.join-card .action-feedback:empty { display: none; }

/* Connexion : liste des joueurs déjà présents (un bouton par pseudo, pour se
   reconnecter directement) + formulaire "pseudo" pour rejoindre une place
   libre — jamais de place choisie manuellement (voir docs/front-end.md). */
.seat-list { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.seat-btn { width: 100%; text-align: left; }
.join-form { display: flex; gap: 8px; margin-top: 12px; }
.join-form input {
  flex: 1;
  min-width: 0;
  background: var(--panel-raised);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: var(--radius);
  padding: 8px 10px;
  font-size: 13px;
  font-family: var(--sans);
}
.join-form input:focus { border-color: var(--accent); }
.join-form[hidden] { display: none; }

#game-ui[hidden] { display: none; }
#game-ui { display: flex; flex-direction: column; gap: 18px; }

/* Rôle d'origine du spectateur (jeux à rôles cachés) : en grand au-dessus
   du bandeau de tour — champ `your_role`, privé, absent des autres jeux. */
.your-role-banner {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: center;
  font: inherit;
  color: inherit;
  width: 100%;
  cursor: pointer;
}
.your-role-banner:disabled { cursor: default; }
.your-role-banner[hidden] { display: none; }
.your-role-hint[hidden] { display: none; }
.your-role-hint { font-size: 12px; color: var(--text-faint); }
.your-role-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}
.your-role-name { font-size: 26px; font-weight: 700; color: var(--accent-strong); }

.turn-banner {
  background: var(--panel-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 16px;
  font-size: 14px;
  font-weight: 600;
}
.turn-banner.your-turn { border-color: var(--accent); background: var(--panel-raised); color: var(--accent-strong); }
.turn-banner.your-turn { background: color-mix(in srgb, var(--accent) 12%, var(--panel-raised)); }
.turn-banner.ended { border-color: var(--success); background: var(--panel-raised); color: var(--success); }
.turn-banner.ended { background: color-mix(in srgb, var(--success) 14%, var(--panel-raised)); }
.turn-banner.waiting { border-style: dashed; color: var(--text-soft); font-weight: 500; }

.phase-chrono {
  align-self: flex-start;
  background: var(--panel-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 4px 12px;
  font-size: 18px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.phase-chrono.urgent { border-color: var(--danger); color: var(--danger); }

/* Onglets Plateau/Discussion (voir docs/front-end.md) : boutons pleine
   largeur pour rester confortables au doigt sur mobile. */
.view-tabs { display: flex; gap: 6px; }
.view-tab {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
  min-height: 44px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-soft);
  cursor: pointer;
  font-family: var(--sans);
}
.view-tab.active { color: var(--accent-strong); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--panel)); }
.view-tab[hidden] { display: none; }
.unread-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
}
.unread-badge[hidden] { display: none; }

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

/* Plateau en 3 bandes spatiales génériques (voir renderZones) : adversaires
   compacts en haut, plateau commun au centre, ta rangée en bas, mise en
   avant — jamais un nom de zone/joueur codé en dur, juste owner + qui
   regarde. */
.board-layout { display: flex; flex-direction: column; gap: 16px; }

.opponents-row { display: flex; flex-wrap: wrap; gap: 10px; }
.opponent-panel {
  flex: 1 1 200px;
  background: var(--panel-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
}

.shared-board { display: flex; flex-direction: column; gap: 12px; }
/* `area: side` (fichier d'affichage) : colonne à droite du plateau commun
   sur grand écran, simplement dessous sur mobile. */
.board-middle { display: grid; gap: 12px; }
@media (min-width: 900px) {
  .board-middle { grid-template-columns: minmax(0, 1fr) minmax(0, 320px); align-items: start; }
}

.my-panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px 18px;
}

/* Halo sur le panneau du joueur dont c'est le tour (`current_player`,
   concept structurel du moteur) — repère immédiat à plusieurs joueurs. */
.player-panel.active-player {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent);
}
.player-panel-header { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.player-panel-name { font-size: 13px; font-weight: 700; color: var(--text); }
.opponent-panel .player-panel-name { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-soft); }
.active-badge {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--accent-strong); border: 1px solid var(--accent); border-radius: 999px; padding: 2px 8px;
}
/* En-tête de panneau adversaire cliquable directement (voir
   docs/front-end.md, "un objet = une cible") : contour visible sans
   attendre le survol (inutile sur tactile), pas seulement `:hover`. */
.player-panel-header.clickable-target {
  cursor: pointer;
  border: 1px dashed var(--accent);
  border-radius: var(--radius);
  padding: 4px 8px;
  margin: -4px -8px 6px;
}
.player-panel-header.clickable-target:hover,
.player-panel-header.clickable-target:focus-visible {
  border-style: solid;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.player-panel-header.clickable-target:active { background: color-mix(in srgb, var(--accent) 18%, transparent); }
/* Panneaux d'information (`view.panels`) : lignes libellé/valeur, qui
   passent à la ligne sur mobile plutôt que de déborder. */
.player-panel-info { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 0 0 10px; }
.player-panel-info > div { display: flex; align-items: baseline; gap: 6px; }
.player-panel-info dt {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--text-faint);
}
.player-panel-info dd { margin: 0; font-size: 15px; font-weight: 700; color: var(--text); }
.opponent-panel .player-panel-info dd { font-size: 13px; }
.player-panel-zones { display: flex; flex-direction: column; gap: 10px; }
.opponent-panel .player-panel-zones { gap: 6px; }
.player-panel-zone-label {
  display: block; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--text-faint); margin-bottom: 4px;
}

.zone-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px 16px;
}
.zone-card h3 { margin: 0 0 8px; font-size: 13px; color: var(--text); }
.zone-card h3 .meta { font-weight: 400; color: var(--text-faint); font-size: 11px; margin-left: 6px; }

/* Salle d'attente : roster des places (réclamées ou non) + statut "prêt". */
.roster-list { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.roster-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  flex-wrap: wrap;
}
.roster-name { font-weight: 600; flex: 1; min-width: 0; overflow-wrap: anywhere; }
.roster-status { font-size: 12px; color: var(--text-soft); padding: 2px 8px; border-radius: 999px; border: 1px solid var(--line); }
.roster-status.ready { color: var(--success); border-color: var(--success); }

/* `flex-wrap` reflow une rangée de cartes sur plusieurs lignes ; le
   `overflow-x` reste un filet de sécurité si une zone en contient un jour
   trop pour même rester lisible repliée (aucun cas actuel). */
.chip-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: flex-end; overflow-x: auto; }

/* `layout` d'une zone (fichier d'affichage) : `row` est la rangée
   ci-dessus ; `fan` chevauche les cartes en éventail, `stack` les empile en
   cascade (le bord de chacune reste visible), `grid` les aligne en colonnes
   égales. Chevauchement en fraction de la largeur de carte, à toute taille.
   La carte survolée ou ciblée passe au premier plan. */
.zone-layout-fan, .zone-layout-stack { flex-wrap: nowrap; gap: 0; padding-top: 4px; }
.zone-layout-fan > * + * { margin-left: calc(var(--card-w) * var(--card-scale) * var(--card-own-scale, 1) * var(--shape-w, 1) * -0.4); }
.zone-layout-stack > * + * { margin-left: calc(var(--card-w) * var(--card-scale) * var(--card-own-scale, 1) * var(--shape-w, 1) * -0.8); }
.zone-layout-fan > :hover, .zone-layout-fan > :focus-within,
.zone-layout-stack > :hover, .zone-layout-stack > :focus-within { z-index: 1; }
.zone-layout-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, calc(var(--card-w) * var(--card-scale)));
  justify-content: start;
}

/* `size` d'une zone : échelle des cartes (et de la grille) de la zone. */
.zone-size-small { --card-scale: 0.75; }
.zone-size-medium { --card-scale: 1; }
.zone-size-large { --card-scale: 1.35; }

/* Zone `structure: "grid"` (ex: Morpion) : vraie grille CSS plutôt qu'une
   liste de cases empilées — `--cols` posé en JS depuis les dimensions
   reçues du serveur, jamais codé en dur pour un jeu précis. Cases carrées
   (`aspect-ratio`) pour que la grille garde ses proportions à toute taille. */
.board-grid {
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  gap: 6px;
  max-width: calc(360px * var(--card-scale));
}
.board-grid-cell {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.board-grid-cell.clickable-target {
  cursor: pointer;
  border: 1px dashed var(--accent);
}
.board-grid-cell.clickable-target:hover,
.board-grid-cell.clickable-target:focus-visible {
  border-style: solid;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}
.board-grid-cell.clickable-target:active { background: color-mix(in srgb, var(--accent) 18%, transparent); }
.board-grid-cell .card { width: 100%; height: 100%; }
/* Pastille simple (pas une carte) : compteur de reste de pile ("+N sous la
   pile"). */
.chip {
  display: inline-flex;
  align-items: center;
  background: var(--panel-raised);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 5px 9px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-faint);
  font-family: var(--mono);
}
.empty-slot { color: var(--text-faint); font-size: 12.5px; font-style: italic; }

/* Vraie forme de carte (ratio portrait) pour tout objet de contenu du jeu —
   cartes en main, dessus de pile, pile de réserve, dos de carte caché.
   Son style vient du fichier d'affichage, résolu par le serveur
   (`item.style`) : forme, taille, fond ou nuance, face, coins. */
.card {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: calc(var(--card-w) * var(--card-scale) * var(--card-own-scale, 1) * var(--shape-w, 1));
  height: calc(var(--card-h) * var(--card-scale) * var(--card-own-scale, 1) * var(--shape-h, 1));
  padding: 6px 4px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: var(--shadow);
  color: var(--text);
  text-align: center;
  flex-shrink: 0;
  font-family: var(--sans);
  container-type: inline-size;
}
/* Nom sur la carte : retour à la ligne entre mots seulement (voir
   `setCardText`), police réduite selon le plus long mot (`--chars`, en
   largeur de carte `cqw`), ellipse en dernier recours. */
.card-face, .card-id { max-width: 100%; overflow: hidden; }
.card-word { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.card-face {
  font-size: clamp(6px, calc(125cqw / var(--chars, 1)), calc(var(--face-max, 15px) * var(--card-scale)));
  font-weight: 800; line-height: 1.15; max-height: 3.45em;
}
.card-id {
  font-size: clamp(6px, calc(105cqw / var(--chars, 1)), 8.5px);
  font-weight: 600; color: var(--text-faint); text-transform: uppercase;
  line-height: 1.15; max-height: 2.3em;
}
.card-badge {
  position: absolute; top: 4px; right: 5px;
  font-family: var(--mono); font-size: 9px; font-weight: 700; color: var(--text-faint);
}
.card-tinted {
  --card-color: color-mix(in srgb, var(--card-color-dark) calc(var(--dark) * 100%), var(--card-color-light));
  background: color-mix(in srgb, var(--card-color) 22%, var(--panel));
  border-color: color-mix(in srgb, var(--card-color) 55%, var(--line));
}
.card-tinted .card-face { color: color-mix(in srgb, var(--card-color) 78%, var(--text)); }

/* Vocabulaire des cartes (`cards.<catalogue>` du fichier d'affichage). */
.card-shape-landscape { --shape-w: 1.4; --shape-h: 0.714; }
.card-shape-square { --shape-w: 1.1; --shape-h: 0.786; }
.card-shape-round { --shape-w: 1.1; --shape-h: 0.786; border-radius: 50%; }
.card-shape-token { --shape-w: 0.7; --shape-h: 0.5; border-radius: 50%; padding: 2px; }
.card-size-small { --card-own-scale: 0.75; }
.card-size-large { --card-own-scale: 1.35; }
/* Fond plein : la teinte de la carte, un liseré couleur de panneau et
   l'encre lisible calculée par le serveur. */
.card-filled {
  --card-bg: color-mix(in srgb, var(--card-bg-dark) calc(var(--dark) * 100%), var(--card-bg-light));
  --card-ink: color-mix(in srgb, var(--card-bg-ink-dark) calc(var(--dark) * 100%), var(--card-bg-ink-light));
  background: var(--card-bg);
  color: var(--card-ink);
  border-color: color-mix(in srgb, var(--card-bg) 65%, var(--text));
  box-shadow: inset 0 0 0 calc(3px * var(--card-scale)) var(--panel), var(--shadow);
}
.card-filled .card-id, .card-filled .card-badge { color: var(--card-ink); opacity: 0.85; }
.card-face-small { --face-max: 11px; }
.card-face-large { --face-max: 28px; }
.card-face-at-top { justify-content: flex-start; }
.card-face-at-bottom { justify-content: flex-end; }
.card-corner {
  position: absolute; max-width: 45%;
  font-size: calc(9px * var(--card-scale)); font-weight: 800; line-height: 1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card-corner-start { top: calc(6px * var(--card-scale)); left: calc(6px * var(--card-scale)); }
.card-corner-end { bottom: calc(6px * var(--card-scale)); right: calc(6px * var(--card-scale)); transform: rotate(180deg); }

/* Carte de ta main pour laquelle une action légale existe : la carte
   elle-même est le bouton, plutôt qu'un texte séparé dans le panneau
   d'actions (voir renderPlayableChip). */
.card-playable {
  cursor: pointer;
  appearance: none;
  border-color: var(--accent);
  border-width: 2px;
}
.card-playable:hover,
.card-playable:focus-visible { transform: translateY(-3px); box-shadow: var(--shadow), 0 4px 10px rgba(0, 0, 0, 0.12); }
.card-playable:active { transform: translateY(-1px); }
.card-playable.card-tinted { border-color: color-mix(in srgb, var(--card-color) 55%, var(--accent)); }

/* Dos de carte uniforme (main d'un adversaire) : motif diagonal plutôt
   qu'une image, pour rester générique. */
.card-back {
  background: repeating-linear-gradient(
    135deg, var(--panel-sunken), var(--panel-sunken) 4px, var(--line) 4px, var(--line) 5px
  );
  border-color: var(--line);
}
/* `.card-back` et `.card-playable` ont la même spécificité (une classe) :
   sans ce sélecteur combiné, `border-color: var(--line)` de `.card-back`
   gagnait sur celui de `.card-playable` (défini plus haut) simplement parce
   qu'il est déclaré après — un dos de carte cliquable (carte du centre,
   voir renderHiddenCards) restait donc gris, indiscernable d'un dos non
   cliquable. */
.card-back.card-playable { border-color: var(--accent); }
/* Dos déclaré (`backs` du fichier d'affichage, `zone.back` dans la vue). */
.card-back-styled {
  --back-bg: color-mix(in srgb, var(--back-bg-dark) calc(var(--dark) * 100%), var(--back-bg-light));
  --back-ink: color-mix(in srgb, var(--back-bg-ink-dark) calc(var(--dark) * 100%), var(--back-bg-ink-light));
  --back-text: color-mix(in srgb, var(--back-text-dark, var(--back-bg-ink-dark)) calc(var(--dark) * 100%), var(--back-text-light, var(--back-bg-ink-light)));
  background: var(--back-bg);
  border-color: color-mix(in srgb, var(--back-bg) 65%, var(--text));
  box-shadow: inset 0 0 0 calc(3px * var(--card-scale)) var(--panel), var(--shadow);
}
.card-back-text {
  max-width: 100%; overflow: hidden;
  font-size: clamp(5px, calc(80cqw / var(--chars, 1)), calc(18px * var(--card-scale)));
  font-weight: 900; font-style: italic; line-height: 1; color: var(--back-text);
  transform: rotate(-18deg);
}
.card-back-count {
  position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
  padding: 0 5px; border-radius: 8px; background: var(--panel); color: var(--text);
  font-family: var(--mono); font-size: 10px; font-weight: 700; line-height: 1.4;
}

/* Infobulle au survol : ce que fait la carte (`item.description`, générée
   côté serveur depuis ses effets — voir `phases::describe_item`). CSS pur
   (pas le `title` natif, lent et sans mise en forme) ; `--text`/`--bg`
   inversés restent lisibles dans les deux thèmes sans code dupliqué. */
.card[data-description] { cursor: help; }
.card[data-description]::after {
  content: attr(data-description);
  position: absolute;
  bottom: calc(100% + 9px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  width: max-content;
  max-width: 200px;
  background: var(--text);
  color: var(--bg);
  font-family: var(--sans);
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.4;
  text-align: left;
  padding: 8px 10px;
  border-radius: 6px;
  box-shadow: var(--shadow);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease;
  z-index: 30;
}
/* Au toucher, le panneau de détail remplace l'infobulle (sinon affichée
   par le survol émulé, et rognée au bord de l'écran). */
@media (hover: hover) {
  .card[data-description]:hover::after,
  .card[data-description]:focus-visible::after {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0);
  }
}
.opponent-panel .card[data-description]::after { max-width: 160px; font-size: 10.5px; }

/* Pas de menu ni de sélection du navigateur sur l'appui long d'une carte
   (réservé au panneau de détail, voir render/card_detail.js). */
.card { -webkit-touch-callout: none; user-select: none; }

/* Détail d'une carte : feuille fixée en bas d'écran, sans voile — le reste
   de la page reste utilisable ; fermée au toucher dehors. */
.card-detail {
  position: fixed;
  left: 50%;
  bottom: 12px;
  transform: translateX(-50%);
  width: min(420px, calc(100% - 24px));
  max-height: 60vh;
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  box-shadow: var(--shadow), 0 8px 30px rgba(0, 0, 0, 0.25);
  padding: 12px 16px 14px;
  z-index: 50;
}
.card-detail[hidden] { display: none; }
.card-detail-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.card-detail-header h2 { margin: 0; font-size: 16px; overflow-wrap: anywhere; }
.card-detail-header .btn-cancel { min-height: 44px; }
.card-detail-fields { display: grid; grid-template-columns: auto 1fr; gap: 2px 12px; margin: 10px 0 0; font-size: 13px; }
.card-detail-fields[hidden] { display: none; }
.card-detail-fields dt { color: var(--text-faint); font-weight: 600; }
.card-detail-fields dd { margin: 0; overflow-wrap: anywhere; }
.card-detail-description { margin: 10px 0 0; font-size: 13.5px; line-height: 1.45; white-space: pre-wrap; }
.card-detail-description[hidden] { display: none; }

/* Pile de réserve (ex: supply Dominion) : la face porte le compte restant. */
.card-pile .card-face { font-family: var(--mono); }
.card-pile.card-empty { opacity: 0.4; }
.opponent-panel { --card-w: 34px; --card-h: 48px; }
.opponent-panel .card, .opponent-panel .card-row { padding: 3px 2px; border-radius: 5px; }
.opponent-panel .card-face { font-size: clamp(6px, calc(125cqw / var(--chars, 1)), 10px); }
.opponent-panel .card-id { display: none; }
.opponent-panel .card-badge { font-size: 7px; top: 2px; right: 3px; }

.end-screen h3 { font-size: 16px; margin: 0; }
.score-list { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
.score-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px 12px 8px 34px;
  border-radius: var(--radius);
  background: var(--panel-raised);
  border: 1px solid var(--line);
  position: relative;
}
.score-rank {
  position: absolute;
  left: 10px;
  top: 9px;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 700;
  color: var(--text-faint);
}
.score-row.winner .score-rank { color: var(--success); }
.score-row-main { display: flex; justify-content: space-between; align-items: center; }
.score-row.winner {
  border-color: var(--success);
  background: color-mix(in srgb, var(--success) 14%, var(--panel-raised));
  font-weight: 700;
}
.score-value { font-family: var(--mono); font-variant-numeric: tabular-nums; color: var(--text-soft); }
.score-row.winner .score-value { color: var(--success); }
.score-detail {
  font-size: 11px;
  font-weight: 400;
  color: var(--text-soft);
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
.winner-badge {
  margin-left: 6px;
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--success);
  border: 1px solid var(--success);
  border-radius: 999px;
  padding: 1px 7px;
}

.actions-panel, .chat-panel, .rules-panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px 18px 18px;
}
.actions-panel h2, .chat-panel h2, .rules-panel h2 { font-size: 15px; margin: 0 0 10px; }
.rules-text { white-space: pre-wrap; line-height: 1.5; }
.actions-list { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 6px; }
.btn-action {
  background: var(--accent);
  color: #fff;
  border: none;
  padding: 9px 14px;
  min-height: 44px;
  border-radius: var(--radius);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  font-family: var(--sans);
}
.btn-action:hover { background: var(--accent-strong); }
/* `overflow-wrap: anywhere` : les blocs "Variables"/"Compteurs" (JSON brut
   de debug, voir renderZones) sont un seul token sans espace — sans ça, ils
   forcent un débordement horizontal de toute la page sur mobile. */
.hint { font-size: 12px; color: var(--text-soft); margin: 6px 0; overflow-wrap: anywhere; }

/* Choix de couleur en ligne (remplace l'ancien prompt() natif) : le bouton
   se transforme en petit formulaire au clic, au lieu d'ouvrir une popup. */
.color-action { display: inline-flex; }
.color-action-form { display: inline-flex; gap: 6px; align-items: center; }
.color-action-form[hidden] { display: none; }
.color-action-form input {
  background: var(--panel-raised);
  border: 1px solid var(--accent);
  color: var(--text);
  border-radius: var(--radius);
  padding: 8px 10px;
  font-size: 13px;
  width: 140px;
  font-family: var(--sans);
}

/* Liste fermée de couleurs (needs_choice) : un bouton par couleur déclarée
   par le jeu, jamais un champ texte libre. */
.color-options { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.color-swatch-btn {
  border: 1px solid var(--line);
  background: var(--panel-raised);
  color: var(--text);
  border-radius: var(--radius);
  padding: 7px 12px;
  font-size: 12.5px;
  font-weight: 600;
  font-family: var(--sans);
  cursor: pointer;
}
.color-swatch-btn:hover { border-color: var(--accent); }
.color-swatch-tinted {
  --swatch: color-mix(in srgb, var(--swatch-dark) calc(var(--dark) * 100%), var(--swatch-light));
  background: var(--swatch);
  border-color: var(--swatch);
  color: color-mix(in srgb, var(--swatch-ink-dark) calc(var(--dark) * 100%), var(--swatch-ink-light));
}
.color-swatch-tinted:hover { filter: brightness(1.1); }
.btn-cancel {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--text-soft);
  padding: 8px 12px;
  border-radius: var(--radius);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  font-family: var(--sans);
}
.btn-cancel:hover { border-color: var(--danger); color: var(--danger); }

/* Montant d'enchère : même formulaire en ligne que le choix de couleur. */
.color-action-form select {
  background: var(--panel-raised);
  border: 1px solid var(--accent);
  color: var(--text);
  border-radius: var(--radius);
  padding: 8px 8px;
  font-size: 13px;
  font-family: var(--sans);
}
.color-action-form input[type="number"] { width: 90px; }
.color-action-form input.formula-input { width: 200px; font-family: var(--mono); }

/* Paiement d'enchère par combinaison de cartes. */
.pay-panel {
  width: 100%;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--panel-raised);
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.pay-panel[hidden] { display: none; }
.pay-title { margin: 0; font-size: 13px; font-weight: 600; }
.pay-cards { display: flex; flex-wrap: wrap; gap: 8px; }
.combo-card {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 4px 8px 4px 6px;
  background: var(--panel);
}
.combo-card:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.combo-card.ineligible { opacity: 0.45; cursor: not-allowed; }
.pay-status { margin: 0; font-size: 12.5px; color: var(--text-soft); }
.pay-panel .btn-primary { align-self: flex-start; }
.pay-panel .btn-primary:disabled { opacity: 0.45; cursor: not-allowed; }

.command-row { display: flex; gap: 8px; margin-top: 8px; }
.command-row input {
  flex: 1;
  background: var(--panel-raised);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: var(--radius);
  padding: 8px 10px;
  font-size: 13px;
  font-family: var(--sans);
}
.command-row input:focus { border-color: var(--accent); }
.btn-primary {
  background: var(--accent); color: #fff; border: none; padding: 8px 16px;
  min-height: 44px;
  border-radius: var(--radius); font-size: 13px; font-weight: 700; cursor: pointer; font-family: var(--sans);
}
.btn-primary:hover { background: var(--accent-strong); }

.action-feedback { margin-top: 10px; font-size: 12.5px; color: var(--text-soft); min-height: 1.4em; }
.action-feedback.error { color: var(--danger); font-weight: 600; }

.chat-panel-header { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 6px 12px; }
.chat-panel-header h2 { margin: 0; }
.chat-log-toggle { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-soft); cursor: pointer; }

.messages { list-style: none; margin: 10px 0 0; padding: 0; max-height: 220px; overflow-y: auto; border-top: 1px solid var(--line); }
.messages li { padding: 6px 2px; border-bottom: 1px solid var(--line); font-size: 13px; white-space: pre-line; overflow-wrap: anywhere; }
.messages li:last-child { border-bottom: none; }
.messages li.system { color: var(--accent-strong); font-style: italic; }
.messages li.system-error { color: var(--danger); font-style: italic; }
/* Par défaut, seul le chat des joueurs est affiché (voir game.js,
   `#show-logs`) : les événements système (changements de phase, résultats
   de commande...) noient vite la discussion — visibles seulement en
   activant la case "Afficher les journaux de partie". */
.messages li.system,
.messages li.system-error {
  display: none;
}
.messages.show-logs li.system,
.messages.show-logs li.system-error {
  display: list-item;
}
