:root {
  /* thème banane (demande Camill, août 2026) : fond crème et bordures
     chaudes plutôt que le gris-bleu neutre d'origine — le jaune vif reste
     réservé aux boutons et au titre, sinon plus rien ne ressort */
  --bg: #fdf6e3;
  --surface: #ffffff;
  --border: #f0e4c3;
  --border-strong: #d9c88f;
  --text: #0f172a;
  --muted: #64748b;
  --accent: #2563eb;
  --accent-strong: #1d4ed8;
  --success: #15803d;
  --danger: #dc2626;
  --warning: #b45309;
  --banana: #fcd34d;
  --banana-strong: #f59e0b;
  --banana-deep: #b45309;
}

* {
  box-sizing: border-box;
}

/* l'attribut hidden doit TOUJOURS gagner, même sur les éléments
   qui ont un display: flex/grid en CSS (modal, bandeau, overlay...) */
[hidden] {
  display: none !important;
}

/* 'Noto Color Emoji' en DERNIER recours : les polices de texte n'ont pas de
   glyphe pour les vrais émojis, qui retombent donc dessus — mais gardent leur
   dessin monochrome habituel pour les symboles qu'elles couvrent (★, ⚙, ⚖…),
   qu'on ne veut pas voir virer à l'émoji couleur au milieu d'un texte. */
body {
  margin: 0;
  min-height: 100vh;
  /* Fredoka (arrondie, esprit jeu) sur TOUTE l'interface sauf le titre, qui
     garde sa cursive Playwrite AR (essai demandé par Camill, août 2026).
     'Noto Color Emoji' reste EN DERNIER : seuls les vrais émojis y retombent,
     les symboles couverts par les polices de texte gardent leur dessin
     monochrome (★, ⚙, ⚖…) au lieu de virer à l'émoji couleur en pleine phrase. */
  font-family: Fredoka, Inter, system-ui, sans-serif, 'Noto Color Emoji';
  /* Fredoka « pese » nettement plus qu'Inter a graisse egale : son 400 se lit
     comme un 600. Le texte courant part donc en 300 (Light), et seuls les
     titres, les actions et les chiffres de score gardent du gras — retour
     Camill : « c'est trop bold partout, varier pour tout ce qui n'est pas
     titre ou action ». */
  font-weight: 300;
  background: var(--bg);
  color: var(--text);
  transition: background 500ms ease;
}

/* manche en cours : fond jaune banane doux — le signal « on est en jeu »
   (idée Camill, à la place du titre du site qui disparaît) */
body.in-game {
  background: #fdf3cf;
}

/* UNE seule largeur de colonne par écran, mini-bandeau compris (retour Camill,
   août 2026 : « le menu du haut ne fait pas la même largeur que les autres »).
   Avant, trois largeurs cohabitaient sans se voir : le mini-bandeau et le titre
   suivaient .app-shell (1180px), le salon était bridé à 720px et l'accueil à
   560px — d'où des bords qui ne s'alignaient sur aucun autre.
   Les écrans « colonne » (accueil, classement, salon hors partie) sont donc
   étroits, et seule la partie en cours prend toute la largeur : la grille de
   mots, elle, en a réellement besoin. C'est updateChrome() qui pose l'attribut. */
.app-shell {
  width: min(1180px, calc(100% - 32px));
  margin: 0 auto;
  padding: 32px 0;
  transition: width 200ms ease;
}

body[data-shell="narrow"] .app-shell {
  width: min(560px, calc(100% - 32px));
}

header {
  text-align: center;
  margin-bottom: 24px;
}

/* mini-bandeau persistant PARTOUT (demande Camill, esprit tusmo.xyz), y
   compris l'accueil et pendant une manche (empilé au-dessus du bandeau de
   jeu) — voir --mini-header-height, utilisée par .game-bar/.letters-card
   pour empiler proprement en dessous */
.mini-header {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  margin-bottom: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
}

/* insécable : sur téléphone, les deux 🍌 qui encadrent le nom passaient au-
   dessus et au-dessous du mot faute de place (retour Camill) — la taille
   s'adapte plutôt que le texte ne se coupe */
.mini-logo {
  font-family: 'Playwrite AR', cursive;
  font-size: clamp(0.85rem, 3.6vw, 1.3rem);
  white-space: nowrap;
  color: var(--banana-strong);
  background: none;
  border: none;
  /* zone cliquable plus haute que le texte (14 px de haut sur téléphone avant —
     audit novembre 2026) sans agrandir le bandeau, dont les boutons font 44 px */
  padding: 11px 0;
  cursor: pointer;
  line-height: 1;
  min-width: 0;
}

.mini-header-actions {
  display: flex;
  gap: 8px;
  margin-left: auto; /* colle à droite même quand le logo est masqué (accueil) */
}

.mini-header-actions .mini {
  margin-left: 0;
}

/* même raison que .mini-logo : jamais de retour à la ligne entre les bananes
   et le nom, la taille se réduit à la place (retour Camill, sur téléphone) */
header h1 {
  margin: 0;
  font-size: clamp(1.5rem, 6.5vw, 3.4rem);
  font-family: 'Playwrite AR', cursive;
  font-weight: normal; /* la police n'a qu'une seule graisse */
  color: var(--banana-strong); /* jaune banane, plus « logo » qu'un simple texte noir */
  text-shadow: 1px 2px 0 rgba(15, 23, 42, 0.15);
  white-space: nowrap;
}

header p {
  margin: 8px auto 0;
  max-width: 720px;
  color: var(--muted);
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 24px;
  padding: 20px;
  margin-bottom: 20px;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
}

.card h2 {
  margin: 0 0 14px;
  font-size: 1.05rem;
}

.muted {
  color: var(--muted);
  font-size: 0.9rem;
  font-weight: 300;
}

label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
}

input[type="text"],
input[type="number"],
input[type="search"],
select {
  width: 100%;
  padding: 12px 14px;
  margin-bottom: 12px;
  border-radius: 14px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font-size: 1rem;
}

input[type="text"]:disabled {
  opacity: 0.5;
}

/* Boutons jaune banane (demande Camill, août 2026). Le relief vit en fin de
   fichier (réglage « doux » choisi en novembre 2026, après un premier essai
   retiré en août). */
/* les contrôles de formulaire n'héritent PAS de la police du document : sans
   cette règle, boutons, champs et listes restaient en Arial pendant que tout
   le reste passait en Fredoka */
button,
input,
select,
textarea {
  font-family: inherit;
}

button {
  padding: 12px 14px;
  border: none;
  border-radius: 14px;
  background: linear-gradient(180deg, #fde68a, var(--banana) 55%, var(--banana-strong));
  color: #4a2c00;
  font-weight: 700;
  cursor: pointer;
  transition: transform 120ms ease, opacity 120ms ease;
}

button:hover:not(:disabled) {
  transform: translateY(-1px);
}

button:disabled {
  opacity: 0.4;
  cursor: default;
}

/* secondaires : neutres — si tout était jaune, plus rien ne ressortirait
   (les boutons d'action principaux perdraient leur mise en avant) */
button.secondary {
  background: var(--border);
  color: var(--text);
}

button.icon {
  padding: 6px 10px;
  background: transparent;
  border: 1px solid var(--border-strong);
  font-size: 0.9rem;
}

/* --- Pseudo et création de salon --- */

.inline-row {
  display: flex;
  gap: 10px;
  align-items: flex-start;
}

.inline-row input {
  flex: 1;
  margin-bottom: 0;
}

/* libellé court à gauche, champ qui prend le reste à droite, sur une seule
   ligne (demande Camill pour « Nom » du salon) */
.field-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.field-row label {
  margin-bottom: 0;
  flex-shrink: 0;
}

/* le CHAMP est poussé à droite (il occupe tout l'espace après le libellé et
   vient buter sur le bord droit) — mais son CONTENU se lit normalement, de
   gauche à droite : retour Camill, « je voulais que le champ soit calé à
   droite, pas son contenu » */
.field-row input {
  flex: 1;
  margin-bottom: 0;
}

#create-room {
  width: 100%;
}

.counter {
  display: inline-block;
  padding: 2px 10px;
  margin-left: 6px;
  border-radius: 12px;
  font-size: 0.85rem;
  background: rgba(37, 99, 235, 0.1);
  color: var(--accent);
  border: 1px solid rgba(37, 99, 235, 0.25);
  vertical-align: middle;
}

/* --- Listes du lobby --- */

.rooms-list,
.scoreboard-list,
.players-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}

.scoreboard-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-radius: 16px;
  background: var(--bg);
  border: 1px solid var(--border);
}

/* --- Classement du lobby --- */

.score-entry {
  justify-content: flex-start;
}

.score-rank {
  width: 34px;
  flex-shrink: 0;
  text-align: center;
  font-size: 1.2rem;
  font-weight: 600;
  color: var(--muted);
}

.score-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.score-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}

.score-pts {
  color: var(--accent);
  font-size: 1.05rem;
  white-space: nowrap;
}

.score-sub {
  font-size: 0.82rem;
}

.score-sub:empty {
  display: none;
}

/* classement par catégories (12 novembre 2026) */
.score-avatar {
  flex-shrink: 0;
  width: 36px;
  display: grid;
  place-items: center;
}

.score-avatar:empty {
  width: 0;
}

.score-entry.me {
  border-color: var(--banana-strong);
  background: color-mix(in srgb, var(--banana) 18%, var(--surface));
}

#classement-section .segmented-group {
  margin: 12px 0 4px;
}

.classement-explain {
  display: block;
  text-align: center;
  font-size: 0.85rem;
  margin: 8px 8px 14px;
}

/* --- Salon (hors manche) : une seule colonne centrée, hiérarchie claire :
   joueurs → PRÊT → règles → chat. En manche, tout disparaît (plein écran). --- */

/* pas de largeur propre : le salon suit celle de .app-shell, étroite hors
   partie et pleine largeur entre deux manches (où il surmonte le bandeau de
   jeu, lui aussi pleine largeur). Avant, un max-width de 720px ici le
   désalignait du mini-bandeau et du titre. */

.ready-block {
  margin-top: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
}

button.ready-big {
  flex: 1;
  min-width: 200px;
  /* plafonné (12 novembre 2026) : entre deux manches sur ordinateur, il
     s'étirait sur 1 000 px */
  max-width: 440px;
  padding: 16px;
  font-size: 1.2rem;
}

/* Entre deux manches sur écran large : la carte du salon (nom + Prêt) tient
   sur UNE ligne, pour que la grille révélée remonte dans l'écran — elle
   commençait sous le bas d'un écran de 768 px de haut. La liste des joueurs
   y est vide (les noms vivent dans le tableau des manches). */
@media (min-width: 1000px) {
  body[data-shell="wide"] .salon-card:has(.players-list:empty) {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 14px 20px;
  }

  body[data-shell="wide"] .salon-card:has(.players-list:empty) h2 {
    margin: 0;
    flex: 1;
  }

  body[data-shell="wide"] .salon-card:has(.players-list:empty) .players-list {
    display: none;
  }

  body[data-shell="wide"] .salon-card:has(.players-list:empty) .ready-block {
    margin-top: 0;
    flex: 0 1 600px;
    justify-content: flex-end;
  }
}

.round-label {
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}

/* réglages avancés repliés par défaut (divulgation progressive) */
.advanced-settings {
  margin-top: 4px;
}

.advanced-settings summary {
  cursor: pointer;
  font-weight: 500;
  color: var(--accent);
  /* zone cliquable portée à ~44 px de haut (20 px avant — audit novembre 2026),
     même espacement visuel qu'avant */
  padding: 12px 0;
  margin: -12px 0 0;
  user-select: none;
}

/* --- Panneau volant ⚙ : pseudo, son, quitter --- */

.fab-settings {
  position: fixed;
  /* + safe-area-inset : sur iPhone à encoche/barre de geste, ne colle pas au bord */
  top: calc(14px + env(safe-area-inset-top, 0px));
  right: calc(14px + env(safe-area-inset-right, 0px));
  z-index: 95;
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-size: 1.15rem;
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.12);
}

/* Quitter = croix ✕ TOUJOURS dans le coin haut-droit ; quand elle est là,
   la roue ⚙ glisse à sa gauche */
.fab-settings {
  transition: right 200ms ease;
}

.fab-exit {
  right: calc(14px + env(safe-area-inset-right, 0px));
  font-size: 1.05rem;
  font-weight: 700;
}

.fab-settings.shifted {
  right: calc(68px + env(safe-area-inset-right, 0px));
}

.user-panel {
  position: fixed;
  top: calc(66px + env(safe-area-inset-top, 0px));
  right: calc(14px + env(safe-area-inset-right, 0px));
  z-index: 95;
  width: 300px;
  /* fixed + contenu qui a grandi (code personnel, 5 formes) : sans ceci, le
     bas du panneau devient inatteignable sur un écran bas, ni scroll de page
     (position fixed) ni scroll interne */
  max-height: calc(100vh - 82px - env(safe-area-inset-top, 0px));
  overflow-y: auto;
  margin-bottom: 0;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.18);
}

/* --- Avatars : emoji animal + couleur + forme, choisis dans le panneau ⚙ --- */

.avatar-badge {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  font-size: 0.9rem;
  line-height: 1;
  flex-shrink: 0;
  vertical-align: middle;
  margin-right: 4px;
}

/* calque du fond : porte la couleur et la découpe (cercle/étoile/...) */
.avatar-shape {
  position: absolute;
  inset: 0;
}

/* calque de l'emoji : toujours par-dessus, jamais rogné par la découpe du
   fond (retour Camill : « le fond des avatars coupe l'émoji au lieu d'être
   derrière ») */
/* Noto Color Emoji EN PREMIER ici (contrairement au reste de la page) : un
   avatar doit avoir exactement le même dessin sur tous les appareils, c'est
   toute sa fonction d'identification (retour Camill : « sur mon téléphone, je
   vois que les émojis d'avatar sont différents »). */
.avatar-emoji,
.avatar-emoji-grid button {
  font-family: 'Noto Color Emoji', system-ui, sans-serif;
}

.avatar-emoji {
  position: relative;
  z-index: 1;
}

/* plus grand sur ordinateur (retour Camill, « genre comme dans un jeu, pas
   comme dans un navigateur qui affiche des caractères typo ») : sur mobile la
   place manque déjà, au-delà de 860px (repère déjà utilisé ailleurs dans ce
   fichier) on peut voir l'avatar en grand sans se battre pour l'espace */
@media (min-width: 861px) {
  .avatar-badge {
    width: 46px;
    height: 46px;
    font-size: 1.7rem;
    margin-right: 8px;
  }

  .user-panel {
    width: 380px;
  }

  .avatar-emoji-grid {
    max-height: 320px;
  }

  .avatar-emoji-grid button {
    font-size: 2rem;
    padding: 6px;
  }

  .avatar-color-row button {
    width: 40px;
    height: 40px;
  }
}

.avatar-circle {
  border-radius: 50%;
}

.avatar-square {
  border-radius: 6px;
}

.avatar-star {
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
  /* les pointes concaves d'une étoile « mangent » de la surface par rapport à
     un cercle/carré de même taille de boîte — elle paraissait plus petite */
  transform: scale(1.15);
}

.avatar-hexagon {
  clip-path: polygon(25% 3%, 75% 3%, 100% 50%, 75% 97%, 25% 97%, 0% 50%);
}

/* cercle étoilé à 6 pétales */
.avatar-flower {
  clip-path: polygon(50% 0%, 70% 15.4%, 93.3% 25%, 90% 50%, 93.3% 75%, 70% 84.6%, 50% 100%, 30% 84.6%, 6.7% 75%, 10% 50%, 6.7% 25%, 30% 15.4%);
}

.avatar-picker {
  margin-top: 14px;
}

.avatar-picker label {
  display: flex;
  align-items: center;
  gap: 8px;
}

.avatar-emoji-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 4px;
  margin-top: 8px;
  max-height: 240px;
  overflow-y: auto;
  padding-right: 2px;
}

/* le sélecteur d'avatar est une grille de choix, pas une rangée d'actions :
   fond neutre, pour ne pas écraser l'avatar qu'on essaie de comparer */
.avatar-emoji-grid button,
.avatar-color-row button,
.avatar-shape-row button {
  background: var(--bg);
  border: 2px solid transparent;
  border-radius: 8px;
  padding: 4px;
  color: inherit;
}

.avatar-emoji-grid button {
  font-size: 1.5rem;
  aspect-ratio: 1;
}

.avatar-emoji-grid button.selected,
.avatar-color-row button.selected,
.avatar-shape-row button.selected {
  border-color: var(--accent);
}

.avatar-color-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

/* 36 px au lieu de 28 (novembre 2026, audit) : visées au doigt, les pastilles
   de 28 px faisaient souvent choisir la voisine */
.avatar-color-row button {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  padding: 0;
}

.avatar-shape-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}

.avatar-shape-row button {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.8rem;
}

/* fin de partie, « Voir la grille » : retour au salon en un clic */
/* fin de partie, après « Voir la grille » : deux boutons flottants, l'un pour
   revoir le récap (fermé un peu vite parfois), l'autre pour revenir au salon */
.post-match-actions {
  position: fixed;
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  gap: 10px;
}

.post-match-actions button {
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.25);
}

.post-match-actions button:hover:not(:disabled) {
  transform: translateY(-1px);
}

.players-list {
  margin-top: 12px;
}

.players-list li {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 4px 16px;
  padding: 8px 14px;
  border-radius: 12px;
  background: var(--bg);
  border: 1px solid var(--border);
}

.player-identity {
  display: flex;
  align-items: center;
  gap: 6px;
}

.player-name {
  font-weight: 500;
}

.player-score {
  color: var(--muted);
}

/* jamais de retour à la ligne : un mot coupé en deux lignes est illisible
   (retour Camill) — c'est --tile-size, calculé en JS d'après la largeur réelle
   et le nombre de lettres, qui absorbe le manque de place */
.letters {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: var(--tile-gap, 10px);
  min-height: var(--tile-size, 44px);
}

/* exception (12 novembre 2026) : un tirage qui tomberait sous 36 px par tuile
   se range sur deux rangées égales — ce n'est pas un mot, juste la réserve de
   lettres ; le mot en cours, lui, reste sur une ligne. Les marges latérales
   (en % de la largeur) ramènent la place utile à une rangée exactement, sans
   changer la largeur mesurée par updateTileSize(). */
.letters.two-rows {
  flex-wrap: wrap;
  box-sizing: border-box;
  padding-inline: max(0px, calc((100% - var(--letters-row-width, 100%)) / 2));
}

.letters span {
  position: relative;
  width: var(--tile-size, 44px);
  height: var(--tile-size, 44px);
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  font-size: calc(var(--tile-size, 44px) * 0.5);
  font-weight: 700;
  border-radius: 14px;
  background: #eef2f7;
  border: 1px solid var(--border-strong);
  cursor: grab;
  user-select: none;
  touch-action: none;
  transition: transform 100ms ease, opacity 100ms ease;
}

.letters span:hover:not(.used) {
  transform: translateY(-2px);
}

/* carte « 3 lettres bloquées » : lettres grisées, non cliquables, tant que
   le blocage tient (mode Cartes uniquement) */
.letters span.blocked {
  opacity: 0.3;
  cursor: not-allowed;
  text-decoration: line-through;
}

/* tuile déjà posée dans le mot en cours */
.letters span.used {
  opacity: 0.35;
  cursor: default;
}

/* tuile en cours de déplacement : pleine, elle suit le curseur */
.letters span.dragging-free {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  transform: scale(1.08);
  box-shadow: 0 8px 20px rgba(15, 23, 42, 0.25);
  transition: none;
}

/* l'espace qui se déplace entre les lettres pendant un glisser :
   invisible, les lettres semblent juste s'écarter */
.letter-placeholder {
  width: var(--tile-size, 44px);
  height: var(--tile-size, 44px);
  flex: 0 0 auto;
}

button.mini {
  padding: 8px 12px;
  font-size: 0.8rem;
  margin-left: 8px;
  vertical-align: middle;
  /* cible tactile ~44px (repère Apple/Google) même pour les boutons icône seule
     (⚙️ ✕ 🔊 ℹ️) : mesurée à 25-38px avant ce correctif */
  min-height: 44px;
  min-width: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* --- Bulle de définition --- */

.def-popup {
  position: absolute;
  z-index: 50;
  max-width: 300px;
  padding: 12px 14px;
  border-radius: 12px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.15);
  font-size: 0.85rem;
}

.def-popup ol {
  margin: 8px 0 0;
  padding-left: 18px;
}

.def-popup li {
  margin-bottom: 4px;
}

.def-loading,
.def-src,
.def-finders {
  margin: 6px 0 0;
  color: var(--muted);
  font-size: 0.8rem;
}

/* définition du mot de BASE (verbe d'une forme conjuguée, mot d'une variante) :
   visuellement détachée, pour qu'on ne la lise pas comme une définition de plus
   du mot demandé */
.def-base {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

.def-base ol {
  margin: 4px 0 0;
}

.def-nature {
  color: var(--muted);
  font-size: 0.8rem;
  font-style: italic;
}

/* sur téléphone, la bulle positionnée près du mot cliqué (souvent tout en bas
   d'une grille qui défile) était trop souvent coupée ou minuscule — on la fixe
   plutôt en bas de l'écran, pleine largeur (retour Camill : « un truc à faire
   pour que ce soit lisible sur téléphone »). !important : la bulle reçoit
   aussi un positionnement en ligne (left/top) via JS, à neutraliser ici. */
@media (max-width: 480px) {
  .def-popup {
    position: fixed !important;
    left: 8px !important;
    right: 8px;
    top: auto !important;
    bottom: 8px;
    max-width: none;
    width: auto;
    max-height: 45vh;
    overflow-y: auto;
    font-size: 0.95rem;
  }
}

.letters span small {
  position: absolute;
  bottom: 2px;
  right: 5px;
  font-size: 0.55rem;
  font-weight: 400;
  color: var(--muted);
}

/* --- Bandeau de jeu : manche, timer et scores en gros --- */

/* la carte des lettres reste épinglée (retour Camill : « c'est les lettres avec
   le mot qu'on tape qui devraient rester visibles quoi qu'il arrive ») —
   décalage calculé en JS d'après la hauteur réelle du mini-bandeau, qui varie
   avec la largeur d'écran (--mini-header-height) */
/* #game-view en colonne flex : l'ordre d'affichage est donné par `order`, ce
   qui permet de placer la carte des lettres AVANT le bandeau de score sans
   toucher à l'ordre du HTML.
   La carte de jeu (lettres + mot en cours) passe en tête et est le SEUL cadre
   épinglé, à toute taille d'écran (retour Camill, août 2026 : « le panneau du
   score passe au-dessus des lettres, ça devrait être en dessous »). Avant,
   cette disposition était réservée au téléphone : sur ordinateur le bandeau de
   score restait au-dessus ET collant, ce qui figeait plus de la moitié de la
   hauteur d'écran (mesuré : 523px sur 900) en plus d'inverser l'ordre. */
#game-view {
  display: flex;
  flex-direction: column;
}

.letters-card { order: 1; }
#game-bar { order: 2; }
.hand-panel { order: 3; }
#game-area { order: 4; }
.chat-card { order: 5; }

.letters-card {
  position: sticky;
  top: calc(var(--mini-header-height, 0px) + 8px);
  z-index: 15;
}

/* manche terminée : plus rien n'est épinglé, pour pouvoir tout parcourir sans
   qu'un cadre ne recouvre la grille révélée (retour Camill). Le bandeau de
   score repasse aussi EN TÊTE : la manche est finie, c'est le résultat qu'on
   vient lire, plus les lettres (inertes à ce stade). */
#game-view.round-over .letters-card {
  position: static;
  order: 2;
}

#game-view.round-over #game-bar {
  order: 1;
}

/* ÉCRAN LARGE (1 000 px et plus), grille affichée — choix de Camill du 13/11.

   PENDANT LA MANCHE : un seul cadre de jeu, façon tableau de bord (« la petite
   tuile avec le temps et la course, c'est un peu nul ») — les LETTRES au centre,
   sur une ligne en 44 px ; « Manche 1/2 » et le chrono dans la marge gauche,
   ⏳+ ⏸ dans la marge droite, la course en bas, sur toute la largeur ; dessous,
   la GRILLE sur toute la largeur. Technique : la carte des lettres et le
   bandeau de jeu occupent la MÊME case de la grille CSS ; le bandeau, devenu
   transparent, se pose par-dessus (ses éléments vont dans les marges, les clics
   au centre traversent jusqu'aux lettres). Les éléments restent à leur place
   dans le HTML — rien ne change sur téléphone.

   EN FIN DE MANCHE : tableau des manches puis lettres (inertes) et chat à
   gauche, grille révélée à droite.

   Historique : tout empilé jusqu'au 12/11 (grille à 600 px du haut sur un
   portable 1366×768) ; deux colonnes le 12/11 (lettres tassées à 34 px) ;
   lettres en haut + petite carte chrono à gauche le matin du 13/11. */
@media (min-width: 1000px) {
  /* --- pendant la manche --- */
  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
  }

  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > .letters-card {
    grid-area: 1 / 1;
    align-self: stretch;
    /* la course se loge en bas de la carte */
    padding-bottom: 92px;
  }

  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > #game-bar {
    grid-area: 1 / 1;
    align-self: stretch;
    position: sticky;
    top: calc(var(--mini-header-height, 0px) + 8px);
    z-index: 16;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto auto 1fr auto;
    align-items: start;
    margin: 0;
    background: none;
    border-color: transparent;
    box-shadow: none;
    pointer-events: none;
  }

  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > #game-bar .gb-top-row {
    display: contents;
  }

  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > #game-bar .gb-round-num {
    grid-area: 1 / 1;
  }

  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > #game-bar .gb-timer {
    grid-area: 2 / 1;
    justify-self: start;
    text-align: left;
  }

  /* la marge gauche a de la place : un chrono qui se lit d'un coup d'œil */
  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > #game-bar .gb-timer strong {
    font-size: 3.2rem;
    line-height: 1.1;
  }

  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > #game-bar .gb-actions {
    grid-area: 1 / 2 / span 2;
    pointer-events: auto;
  }

  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > #game-bar .gb-main-row {
    grid-area: 3 / 1 / auto / span 2;
    pointer-events: auto;
  }

  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > #game-bar #score-track {
    grid-area: 4 / 1 / auto / span 2;
    pointer-events: auto;
  }

  /* main de cartes (mode Cartes) entre le tableau de bord et la grille */
  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > .hand-panel {
    grid-area: 2 / 1;
  }

  #game-view:not(.round-over):has(> #game-area:not([hidden])):has(> .letters-card:not([hidden])) > #game-area {
    grid-area: 3 / 1;
  }

  /* --- fin de manche --- */
  #game-view.round-over:has(> #game-area:not([hidden])) {
    display: grid;
    grid-template-columns: minmax(300px, 400px) minmax(0, 1fr);
    column-gap: 20px;
    align-items: start;
  }

  #game-view.round-over:has(> #game-area:not([hidden])) > #game-bar {
    grid-area: 1 / 1;
  }

  #game-view.round-over:has(> #game-area:not([hidden])) > .letters-card {
    grid-area: 2 / 1;
  }

  #game-view.round-over:has(> #game-area:not([hidden])) > .hand-panel {
    grid-area: 3 / 1;
  }

  #game-view.round-over:has(> #game-area:not([hidden])) > .chat-card {
    grid-area: 4 / 1;
  }

  #game-view.round-over:has(> #game-area:not([hidden])) > #game-area {
    grid-area: 1 / 2 / span 4;
  }
}

/* plus collant : il vit sous la carte de jeu et défile avec la page (voir la
   note d'ordre ci-dessus) */
.game-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 14px 24px;
}

/* titre au centre (Manche X/Y pendant le jeu, « Untel gagne la manche X/Y »
   en fin de manche — même span, contenu remplacé), boutons de part et
   d'autre (retour Camill) ; sa propre ligne pleine largeur à toute taille */
.gb-top-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-basis: 100%;
}

.gb-side {
  display: flex;
  gap: 8px;
  flex: 1 1 0;
  justify-content: flex-end;
}

.gb-side .mini {
  margin-left: 0;
}

/* fin de manche : chrono et boutons ⏳+/⏸ sont masqués, mais leur colonne
   gardait la moitié droite du bandeau — sur téléphone, « ⭐ Bob gagne la
   manche 1/2 (308 pts) » s'écrasait sur trois lignes à côté d'un vide
   (novembre 2026, audit). Le titre prend désormais toute la largeur. */
.game-bar.round-over .gb-side {
  display: none;
}

/* hors mode coopératif, la rangée des plaques de score est vide pendant la
   manche (les scores vivent sur la piste) : ses marges laissaient pourtant
   ~30 px de blanc entre le chrono et la piste */
.gb-main-row:has(> .gb-scores-card:empty) {
  display: none;
}

/* « Manche X/Y » calé à GAUCHE, le chrono occupant le centre (demande Camill) ;
   les deux se partagent la place restante à parts égales, ce qui garde le
   chrono réellement centré quelle que soit la longueur du titre */
.gb-round-num {
  color: var(--muted);
  font-size: 0.9rem;
  text-align: left;
  flex: 1 1 0;
  min-width: 0;
}

/* le chrono ne s'étire PAS : ce sont le titre (à gauche) et les boutons (à
   droite) qui se partagent à parts égales la place restante — c'est ce qui le
   laisse exactement au milieu du bandeau, quelle que soit la longueur du titre.
   Avec un chrono en flex:1, son centre se décalait vers la droite. */
.gb-timer {
  flex: 0 0 auto;
  text-align: center;
}

.gb-round-num strong {
  color: var(--text);
  font-size: 1.1rem;
}


.gb-timer strong {
  font-size: 2.4rem;
  font-variant-numeric: tabular-nums;
}

/* fin de manche imminente : le compteur clignote */
@keyframes timer-blink {
  50% { opacity: 0.15; }
}

.gb-timer strong.urgent {
  color: var(--danger);
  animation: timer-blink 1s step-start infinite;
}

.gb-actions {
  display: flex;
  gap: 8px;
}

.gb-actions .mini {
  margin-left: 0;
}

/* pastilles de manches façon jeu vidéo, sous le score de chaque joueur */
.gb-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.gb-pips {
  display: flex;
  gap: 5px;
  font-size: 0.78rem;
  line-height: 1;
  flex-wrap: wrap;
}

/* le score est directement sur la pastille (pas seulement au survol,
   inutilisable au doigt sur téléphone) */
.pip {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.pip-win {
  color: #f59e0b;
}

.pip-lost {
  color: var(--border-strong);
}

.pip-next {
  color: var(--border);
}

/* le chrono ayant rejoint la rangée du titre (au centre), les scores occupent
   seuls la ligne du dessous */
.gb-main-row {
  display: flex;
  align-items: center;
  flex-basis: 100%;
}

.gb-scores-card {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  flex: 1;
  justify-content: center;
  padding: 4px 8px;
}

/* Course de score : avatars positionnés selon le score actuel / score max
   atteignable sur cette grille, échelle linéaire. Depuis août 2026, elle PORTE
   AUSSI LES SCORES (demande Camill : « remplacer le cadre avec score par : sur
   la ligne de course, au-dessus de l'avatar le nom du joueur, en dessous le
   score ») — d'où la hauteur nécessaire pour trois étages, et la disparition
   des plaques de score pendant le jeu actif. */
.score-track {
  position: relative;
  flex-basis: 100%;
  height: 86px;
  margin: 8px 20px 4px;
  padding-right: 46px;
}

.score-track-line {
  position: absolute;
  left: 0;
  right: 46px;
  top: 50%;
  height: 4px;
  background: var(--border);
  border-radius: 2px;
  transform: translateY(-50%);
}

.score-track-avatars {
  position: absolute;
  inset: 0;
  right: 46px;
}

/* nom au-dessus, avatar au milieu (centré sur le trait), score en dessous */
.score-track-avatar {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  transition: left 300ms ease;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  line-height: 1.1;
}

.stk-name {
  font-size: 0.7rem;
  font-weight: 500;
  color: var(--muted);
  max-width: 72px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stk-score {
  font-size: 1.1rem;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.score-track-avatar.leader .stk-score {
  color: #15803d;
}

.score-track-avatar.targetable {
  cursor: pointer;
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 8px;
}

/* (l'empilement vertical des ex æquo a été retiré en août 2026 : les avatars
   sont désormais poussés côte à côte, sans jamais se recouvrir — voir
   renderScoreTrack) */

.score-track-max {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.75rem;
  color: var(--muted);
  font-weight: 500;
  white-space: nowrap;
  /* le trait de la piste passait SOUS le libellé et le barrait (« 2256 pts »
     plus large que la place réservée) : un fond de carte l'interrompt net */
  background: var(--surface);
  padding-left: 6px;
  z-index: 1;
}

/* Plaque de joueur en jeu : pseudo AU-DESSUS de l'avatar, score EN DESSOUS
   (demande Camill, août 2026). Une pile verticale reste étroite même à 6
   joueurs — c'est la largeur qui manque sur téléphone, pas la hauteur. Le nom
   est tronqué pour garantir cette étroitesse ; un tap sur la plaque l'ouvre
   en entier (.named). */
/* troncature réservée aux PLAQUES de jeu (largeur comptée) — surtout pas au
   tableau de fin de manche, où la place ne manque pas et où un nom coupé en
   « Camill (vo… » n'apporte rien */
.gb-player .gb-name {
  max-width: 84px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
}

.gb-player.named .gb-name {
  max-width: none;
  overflow: visible;
}

.gb-player {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px 14px;
  border-radius: 12px;
  background: var(--bg);
  border: 1px solid var(--border);
}

.gb-player.leader {
  border-color: #16a34a;
  background: rgba(22, 163, 74, 0.08);
}

.gb-player.leader .gb-score {
  color: #15803d;
  font-weight: 600;
}

/* entre deux manches : prêt pour la suivante ? le cadre entier se surligne
   plutôt qu'un sablier pour « pas encore » (retour Camill : pas besoin du
   sablier, mais rendre plus visible qui a coché prêt). Bleu (--accent), pas
   vert : le vert est déjà pris par .leader (meneur de la manche) — un joueur
   peut être meneur ET pas encore prêt, il ne faut pas confondre les deux. */
.gb-player.ready-next {
  border-color: var(--accent);
  background: rgba(37, 99, 235, 0.08);
}

.gb-ready-check {
  margin-left: auto;
  color: var(--accent);
  font-weight: 700;
}

/* mode Cartes : une carte armée cherche sa cible parmi les autres joueurs */
.gb-player.targetable {
  cursor: pointer;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.25);
}

.gb-cards {
  font-size: 0.8rem;
  color: var(--muted);
}

.gb-name {
  font-weight: 500;
  font-size: 0.9rem;
}

.gb-score {
  font-size: 1.6rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  font-weight: 600;
}

/* --- Entre deux manches : UN seul tableau joueurs × manches (demande Camill,
   août 2026) — remplace le rectangle par joueur, qui répétait nom et avatar
   autant de fois qu'il y avait de joueurs --- */
/* le tableau occupe toute la ligne : sans cela, son conteneur défilant restait
   dimensionné à son contenu et se retrouvait centré, décalé sur la droite */
.gb-scores-card > .table-scroll {
  flex: 1 1 100%;
}

/* un élément flex ne rétrécit pas sous la largeur de son contenu par défaut :
   dans la colonne de gauche (fin de manche sur ordinateur, 13/11), le tableau
   des manches débordait alors du cadre au lieu de défiler (retour Camill,
   capture à 3 manches) */
.gb-main-row,
.gb-scores-card,
.gb-scores-card > .table-scroll {
  min-width: 0;
}

/* colonne de gauche étroite (fin de manche sur ordinateur) : cellules
   resserrées, et le pseudo peut passer à la ligne (« Camill / (vous) ») —
   sans quoi le tableau dépassait de quelques pixels et un ascenseur
   horizontal apparaissait (retour Camill du 13/11) */
@media (min-width: 1000px) {
  #game-view.round-over .gb-round-table th,
  #game-view.round-over .gb-round-table td {
    padding-left: 4px;
    padding-right: 4px;
  }

  #game-view.round-over .gb-round-table td.gb-tbl-player {
    white-space: normal;
  }

  #game-view.round-over .gb-round-table .gb-tbl-player .gb-name {
    min-width: 0;
    overflow-wrap: anywhere;
  }
}

.gb-round-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.gb-round-table th {
  font-size: 0.75rem;
  color: var(--muted);
  font-weight: 500;
  text-align: center;
  padding: 2px 8px;
}

.gb-round-table td {
  padding: 6px 8px;
  text-align: center;
  white-space: nowrap;
  border-top: 1px solid var(--border);
}

.gb-tbl-player {
  display: flex;
  align-items: center;
  gap: 8px;
  text-align: left !important;
}

.gb-tbl-words {
  color: var(--muted);
  font-size: 0.8rem;
}

.gb-tbl-total {
  font-weight: 600;
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
}

/* mêmes couleurs que les anciennes pastilles : ★ manche gagnée, ● perdue,
   ○ à venir */
.gb-round-table .pip-win { color: #b45309; font-weight: 700; }
.gb-round-table .pip-lost { color: var(--muted); }
.gb-round-table .pip-next { color: var(--border-strong); }

.gb-round-table tr.leader .gb-tbl-total { color: #15803d; }
.gb-round-table tr.gone { opacity: 0.55; }

/* prêt pour la manche suivante : la LIGNE entière se surligne, comme le cadre
   entier le faisait sur les anciennes plaques (bleu, pas vert : le vert est
   déjà pris par le meneur) */
.gb-round-table tr.ready-next td {
  background: rgba(37, 99, 235, 0.08);
}

.gb-sub {
  color: var(--muted);
  font-size: 0.8rem;
}

/* mode chrono : horloge individuelle sous le score */
.gb-clock {
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.gb-clock.low {
  color: var(--danger);
  font-weight: 700;
}

.gb-clock.frozen {
  opacity: 0.6;
}

/* joueur parti en cours de partie : score conservé, affiché en retrait */
.gb-player.gone,
.players-list li.gone {
  opacity: 0.55;
}

/* courbe de progression des stats : des barres simples, pas de bibliothèque —
   la hauteur est le pourcentage de grille pris sur la manche */
.progress-chart {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 90px;
  padding: 4px 0;
  overflow-x: auto;
}

.progress-bar {
  flex: 1 0 8px;
  min-width: 8px;
  background: linear-gradient(180deg, var(--banana), var(--banana-strong));
  border-radius: 3px 3px 0 0;
  position: relative;
}

/* la valeur ne s'affiche qu'au survol : à 40 barres, les écrire toutes
   rendrait le graphique illisible */
.progress-bar-label {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.7rem;
  color: var(--muted);
  opacity: 0;
  white-space: nowrap;
}

.progress-bar:hover .progress-bar-label {
  opacity: 1;
}

.recap-subtitle {
  margin: 4px 0 8px;
  font-size: 0.95rem;
  color: var(--muted);
}

.recap-breakdown p {
  margin: 0 0 10px;
  font-size: 0.9rem;
}

/* --- Bannière de résultat --- */

.result-banner {
  text-align: center;
  padding: 18px 24px;
  flex-basis: 100%;
}

.result-banner strong {
  display: block;
  font-size: 1.5rem;
}

/* --- Rack du mot en cours --- */

/* le mot composé, au-dessus du tirage et centré (disposition jeu de lettres) —
   comme le tirage, jamais sur deux lignes */
.word-rack {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: var(--rack-gap, var(--tile-gap, 10px));
  margin: 4px 0 16px;
  min-height: var(--rack-size, var(--tile-size, 44px));
}

/* mêmes dimensions que les lettres du tirage tant qu'il tient sur une ligne ;
   s'il passe sur deux rangées (--tile-size grandit), le mot en cours garde la
   taille « une ligne » (--rack-size) : lui ne doit jamais se couper */
.rack-slot {
  width: var(--rack-size, var(--tile-size, 44px));
  height: var(--rack-size, var(--tile-size, 44px));
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  font-size: calc(var(--rack-size, var(--tile-size, 44px)) * 0.48);
  font-weight: 700;
  border-radius: 14px;
  border: 2px dashed var(--border-strong);
  background: transparent;
  user-select: none;
}

.rack-slot.filled {
  border-style: solid;
  border-color: var(--accent);
  background: rgba(37, 99, 235, 0.08);
  cursor: pointer;
}

.rack-slot.filled:hover {
  background: rgba(220, 38, 38, 0.1);
  border-color: var(--danger);
}

.rack-actions {
  display: flex;
  gap: 10px;
  margin-top: 16px; /* respire, ne colle plus aux lettres du tirage */
}

.rack-actions button {
  flex: 1;
}

/* Valider : l'action principale, mise en avant (plus grande, vert « GO »,
   plus fun) — Effacer reste secondaire et discret à côté. Reste VERT malgré le
   passage des boutons au jaune banane : c'est le « GO » du jeu, il doit se
   distinguer au premier coup d'œil des boutons de navigation. */
#submit-word {
  flex: 2;
  padding: 16px;
  font-size: 1.05rem;
  background: linear-gradient(135deg, #22c55e, #15803d);
  color: #ffffff;
  box-shadow: 0 6px 18px rgba(21, 128, 61, 0.35);
}

#submit-word:hover:not(:disabled) {
  box-shadow: 0 8px 22px rgba(21, 128, 61, 0.45);
}

/* sur ordinateur, les trois boutons s'étiraient sur toute la largeur de la
   partie (1 140 px) sous un tirage qui en occupe 400 : rangée ramenée à la
   largeur d'un tirage de 12 lettres et centrée sous lui — mêmes proportions
   (Valider deux fois plus large, à droite). Novembre 2026, audit. */
@media (min-width: 861px) {
  .rack-actions,
  .letters-footer {
    max-width: 640px;
    margin-left: auto;
    margin-right: auto;
  }
}

/* --- Pop-up de points à la validation d'un mot : plus le mot rapporte,
   plus le « +N » est gros et coloré (paliers façon raretés de jeu vidéo).
   Seuils calés sur le barème : 3 lettres ≈ 3-8 pts, 5 ≈ 11-18, 7 ≈ 25-35, 10+ ≥ 60. --- */

.points-pop {
  position: fixed;
  z-index: 70;
  pointer-events: none;
  user-select: none;
  font-weight: 900;
  white-space: nowrap;
  /* contour blanc façon sticker de jeu mobile : le chiffre est « encadré » */
  paint-order: stroke fill;
  -webkit-text-stroke: 5px #ffffff;
  text-shadow: 0 3px 8px rgba(15, 23, 42, 0.35);
  animation: points-pop 2s ease-out forwards;
}

/* monte vite, RESTE lisible un moment, puis s'estompe.
   Le « -100% » vertical cale le BAS du chiffre sur le haut du mot en cours
   (novembre 2026, audit) : avant, c'était son HAUT qui y était calé, et un
   « +98 » doré de 70 px recouvrait le mot qu'on tapait pendant deux secondes —
   on enchaînait à l'aveugle après chaque mot trouvé. */
@keyframes points-pop {
  0% { opacity: 0; transform: translate(-50%, -100%) scale(0.4); }
  8% { opacity: 1; transform: translate(-50%, calc(-100% - 14px)) scale(1.25); }
  18% { transform: translate(-50%, calc(-100% - 20px)) scale(1); }
  75% { opacity: 1; transform: translate(-50%, calc(-100% - 34px)) scale(1); }
  100% { opacity: 0; transform: translate(-50%, calc(-100% - 72px)) scale(1); }
}

.points-pop.pop-t1 { font-size: 1.9rem; color: #64748b; }
.points-pop.pop-t2 { font-size: 2.4rem; color: #16a34a; }
.points-pop.pop-t3 { font-size: 3rem; color: #ea580c; }
.points-pop.pop-t4 { font-size: 3.6rem; color: #9333ea; filter: drop-shadow(0 0 12px rgba(147, 51, 234, 0.45)); }
.points-pop.pop-t5 { font-size: 4.4rem; color: #f59e0b; filter: drop-shadow(0 0 14px rgba(245, 158, 11, 0.7)); }

/* pénalité : le malus s'affiche aussi, en rouge */
.points-pop.pop-neg { font-size: 2.4rem; color: var(--danger); }

/* message de retour et « dernier mot posé » sur une seule ligne : deux lignes
   de texte sous les boutons coûtaient cher en hauteur sur téléphone */
.letters-footer {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}

.letters-footer .feedback {
  flex: 1;
  min-width: 0;
}

.feedback {
  min-height: 24px;
  margin-top: 12px;
  color: var(--muted);
}

.feedback.success {
  color: var(--success);
}

.feedback.error {
  color: var(--danger);
}

.feedback.info {
  color: var(--warning);
}

/* --- Grille de mots (compacte : un rectangle par mot) --- */

.word-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

/* séparateur étiqueté entre les groupes de longueurs : « 6 lettres ─────── » */
.len-sep {
  flex-basis: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
}

.len-sep span {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}

/* groupe de longueur entièrement lisible (trouvé, pris ou révélé) — pas
   affichée pour un mot juste entouré en orange (adversaire, mode libre),
   toujours masqué tant qu'on ne l'a pas trouvé soi-même */
@keyframes len-complete-pop {
  0% { transform: scale(0.3); opacity: 0; }
  60% { transform: scale(1.3); opacity: 1; }
  100% { transform: scale(1); }
}

.len-complete-badge {
  font-size: 0.9rem;
}

.len-complete-badge.empty {
  opacity: 0.35;
}

.len-complete-badge.pop {
  animation: len-complete-pop 350ms ease-out;
}

/* + du bonus de complétion, à côté de l'étoile (retour Camill) — vide tant
   qu'aucun bonus n'a été gagné pour cette catégorie, donc invisible */
.len-bonus-pts {
  color: #15803d;
  font-weight: 600;
}

.len-bonus-pts:empty {
  display: none;
}

.len-sep::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--border);
}

.word-box {
  height: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 0 5px;
  border-radius: 6px;
  background: #e2e8f0;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.5px;
  color: var(--text);
}

.word-points {
  font-size: 0.65rem;
  font-weight: 500;
  color: var(--muted);
}

/* option Indices : la première lettre apparaît en creux sur les cases encore
   vides — assez lisible pour aider, assez discrète pour ne pas se confondre
   avec un mot trouvé (fond vert) ou pris par un adversaire (contour orange) */
.word-box.hinted .word-text {
  color: var(--muted);
  font-weight: 500;
  opacity: 0.75;
}

/* mode Cartes : ce mot rapporte une carte en plus des points — pure
   métadonnée (comme la longueur déjà affichée), ne révèle aucune lettre */
.word-box.has-card::before {
  content: "🃏";
  font-size: 0.7rem;
}

/* mode Cartes : viser cette case pour y poser un piège (carte armée en main) —
   toutes les cases passent .targetable ensemble (voir updateTargetableHighlight
   dans app.js), donc l'agrandissement ci-dessous ne déséquilibre jamais une
   rangée par rapport à ses voisines */
.word-box.targetable {
  cursor: pointer;
  outline: 2px dashed var(--accent);
  outline-offset: 1px;
  height: 40px;
}

/* deux codes indépendants qui se superposent sans ambiguïté :
   fond vert = je l'ai trouvé · contour orange = un adversaire l'a trouvé */
.word-box.mine {
  background: #16a34a;
  color: #ffffff;
}

.word-box.mine .word-points {
  color: #dcfce7;
}

.word-box.theirs,
.word-box.taken {
  /* contour plus large et détaché (offset positif) : un fin liseré collé au
     bord vibrait visuellement sur les petites cases (retour Camill) */
  outline: 3px solid #ea580c;
  outline-offset: 2px;
}

/* mode course : le mot pris est révélé pour tout le monde */
.word-box.taken {
  background: rgba(234, 88, 12, 0.14);
}

/* coopération : le mot d'un coéquipier est un mot de l'équipe, même vert
   que les miens, sans contour d'adversaire */
.word-box.taken.mine {
  background: #16a34a;
  outline: none;
}

/* mot raté, révélé en fin de manche */
.word-box.revealed {
  color: var(--muted);
}

/* les mots visibles sont cliquables (définition) */
.word-box.mine,
.word-box.taken,
.word-box.revealed {
  cursor: pointer;
}

/* petite pulsation à l'instant où un mot rejoint la liste (demande Camill) */
@keyframes word-just-found {
  0% { transform: scale(1); }
  35% { transform: scale(1.18); }
  100% { transform: scale(1); }
}

.word-box.just-found {
  animation: word-just-found 350ms ease-out;
}

/* --- Chat --- */

.chat-messages {
  list-style: none;
  margin: 0 0 12px;
  padding: 10px;
  max-height: 220px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  border-radius: 12px;
  background: var(--bg);
}

.chat-messages li strong {
  color: var(--accent);
  margin-right: 6px;
}

.chat-messages:empty::after {
  content: "Aucun message pour l'instant.";
  color: var(--muted);
  font-size: 0.9rem;
}

/* --- Décompte, rab de temps, modal stats --- */

.countdown-overlay {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: clamp(4rem, 15vw, 8rem);
  font-weight: 800;
  color: var(--text);
  background: rgba(248, 250, 252, 0.75);
  z-index: 100;
  pointer-events: none;
}

.proposal-prompt {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px;
}

.proposal-prompt span {
  flex: 1;
  font-weight: 500;
}

/* --- Pause --- */

.pause-overlay {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(248, 250, 252, 0.6);
  z-index: 85;
}

.pause-box {
  text-align: center;
  padding: 28px 40px;
  margin-bottom: 0;
}

.pause-box strong {
  display: block;
  font-size: 2rem;
  margin-bottom: 16px;
}

/* qui est revenu de la pause : la reprise attend TOUT LE MONDE (décision
   Camill), il faut donc voir d'un coup d'œil qui manque encore */
.pause-players {
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
  display: grid;
  gap: 6px;
  text-align: left;
}

.pause-player {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--bg);
}

.pause-player.ready {
  border-color: var(--accent);
  background: rgba(37, 99, 235, 0.08);
}

.pause-check {
  margin-left: auto;
  color: var(--accent);
  font-weight: 700;
}

/* « accepter pour 50 points » : option secondaire, sur sa propre ligne pour
   ne pas écraser refuser/accepter en largeur */
#proposal-overlay .rack-actions {
  flex-wrap: wrap;
}

#proposal-accept-paid {
  flex-basis: 100%;
}

/* pendant la pause, les données de jeu sont illisibles (le chat, lui, reste
   net : c'est justement le moment de se parler) */
#game-view.paused .letters-card,
#game-view.paused #game-area .card {
  filter: blur(6px) grayscale(0.5);
  pointer-events: none;
  user-select: none;
}

/* manche terminée : on revoit les lettres du tirage, grisées (c'est fini),
   et tous les contrôles de saisie disparaissent */
#game-view.round-over .letters span {
  opacity: 0.45;
  cursor: default;
  pointer-events: none;
}

#game-view.round-over .word-rack,
#game-view.round-over .rack-actions,
#game-view.round-over #shuffle-letters {
  display: none;
}

.checkbox-line {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 2px 0 10px;
  font-weight: 400;
}

.checkbox-line input {
  width: auto;
  margin: 0;
}

/* indicateur de connexion du panneau ⚙ : la valeur colorée selon ce qu'on
   ressent en jouant — sous 80 ms rien ne se voit, au-delà de 200 ms la
   validation d'un mot paraît lente */
.panel-latency {
  margin: 4px 0 0;
  font-size: 0.9rem;
  line-height: 1.4;
}
.panel-latency strong { font-weight: 600; }
.panel-latency .lat-good { color: var(--success); }
.panel-latency .lat-meh { color: var(--warning); }
.panel-latency .lat-bad { color: var(--danger); }

fieldset.settings-fields {
  border: none;
  padding: 0;
  margin: 0;
  min-width: 0;
}

fieldset.settings-fields:disabled {
  opacity: 0.6;
}

.modal {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(15, 23, 42, 0.4);
  z-index: 90;
  padding: 20px;
}

.modal-box {
  max-width: 720px;
  width: 100%;
  max-height: 80vh;
  overflow-y: auto;
  margin-bottom: 0;
}

/* tri du tableau « qui a trouvé quoi » (demande Camill) */
.matrix-sort {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
  font-size: 0.85rem;
  color: var(--muted);
}

.matrix-sort .mini.selected {
  border: 2px solid var(--accent);
}

.word-matrix-pts {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

/* séparateur avant les lignes de synthèse (catégories complètes) */
.matrix-sep th {
  padding-top: 14px;
  color: var(--muted);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* enveloppe .stats-table / .recap-table (voir app.js) : sur téléphone, la
   modale n'a pas la place pour toutes les colonnes — on défile plutôt que
   de les couper (constat : « Meilleur mot » tronqué à 375px de large) */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* ombres de défilement (12 novembre 2026) : une colonne cachée hors de
     l'écran ne se devinait pas (la 4e colonne de « Qui a trouvé quoi » à 4
     joueurs sur téléphone). Une ombre apparaît du côté où il reste à faire
     défiler, et disparaît une fois au bout — les deux premiers fonds suivent
     le contenu (local) et recouvrent les ombres quand on est au bord. */
  background:
    linear-gradient(to right, var(--surface) 40%, transparent) left / 28px 100% no-repeat local,
    linear-gradient(to left, var(--surface) 40%, transparent) right / 28px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(120, 90, 20, 0.28), transparent) left / 12px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(120, 90, 20, 0.28), transparent) right / 12px 100% no-repeat scroll;
}

/* « Qui a trouvé quoi » : colonnes de joueurs étroites — avatar au-dessus,
   pseudo court en dessous —, pour que 4 joueurs tiennent sur un téléphone */
.word-matrix-table th,
.word-matrix-table td {
  padding: 6px 5px;
}

.word-matrix-table tbody td:first-child {
  font-size: 0.82rem;
}

/* ✓ centrés sous l'avatar centré (.recap-table td les alignait à gauche) */
.word-matrix-table td.word-matrix-cell {
  text-align: center;
}

.word-matrix-table th.wm-player {
  text-align: center;
  vertical-align: bottom;
  min-width: 44px;
}

.word-matrix-table th.wm-player .avatar-badge {
  display: block;
  margin: 0 auto 2px;
}

.wm-name {
  display: block;
  max-width: 56px;
  margin: 0 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.72rem;
}

.table-scroll table {
  width: auto;
  min-width: 100%;
}

.stats-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.stats-table th,
.stats-table td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
}

.stats-table th {
  color: var(--muted);
  font-weight: 500;
}

/* ses propres stats : une seule ligne de données, donc une liste libellé/valeur
   plutôt qu'un tableau à colonnes (illisible sur téléphone) */
.stats-facts {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0;
  margin: 0;
}

.stats-facts dt,
.stats-facts dd {
  margin: 0;
  padding: 9px 0;
  border-bottom: 1px solid var(--border);
}

.stats-facts dt {
  color: var(--muted);
}

.stats-facts dd {
  text-align: right;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* --- Récap de fin de partie --- */

.recap-box h2 {
  font-size: 1.4rem;
  text-align: center;
}

.recap-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 14px;
  font-size: 0.92rem;
}

.recap-table th,
.recap-table td {
  padding: 7px 10px;
  text-align: left;
  border-bottom: 1px solid var(--border);
}

.recap-table thead th {
  color: var(--muted);
  font-weight: 500;
}

.recap-table tbody th {
  color: var(--muted);
  font-weight: 500;
  width: 52px;
}

.recap-total td {
  font-weight: 700;
  font-size: 1.05rem;
}

/* cellule de manche : le score, puis le nombre de mots en petit dessous —
   jamais coupés au milieu (novembre 2026, audit) */
.recap-table td {
  white-space: nowrap;
}
.recap-score {
  display: block;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.recap-words {
  display: block;
  font-size: 0.8em;
  color: var(--muted);
}
.recap-table thead th.recap-unit {
  font-size: 0.75rem;
  vertical-align: bottom;
}

/* « Retour au salon / Voir la grille » toujours visibles en bas de la
   fenêtre, au lieu d'être cachés sous la liste des médailles et le détail des
   scores (il fallait défiler pour les trouver sur téléphone) */
.recap-box > .rack-actions {
  position: sticky;
  bottom: -20px; /* colle au bord bas malgré le rembourrage de la carte (.card : 20px) */
  margin: 12px -20px -20px;
  padding: 12px 20px 20px;
  background: var(--surface);
  box-shadow: 0 -8px 12px -8px rgba(15, 23, 42, 0.15);
}

/* tableau « qui a trouvé quoi », un mot par ligne (retour Camill : le
   grisé/contour de la grille ne montre pas ça d'un coup d'œil) */
.word-matrix-cell {
  text-align: center;
  color: #16a34a;
  font-weight: 600;
}

.medal-list {
  list-style: none;
  margin: 0 0 14px;
  padding: 0;
  display: grid;
  gap: 8px;
}

.medal-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  font-size: 0.9rem;
}

.medal-icon {
  font-size: 1.2rem;
}

/* --- Mode Cartes : main de cartes et ciblage --- */

.hand-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  min-height: 44px;
}

.hand-card {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border-radius: 14px;
  background: linear-gradient(135deg, #7c3aed, #5b21b6);
  font-weight: 700;
  white-space: nowrap;
}

.hand-card .hand-card-icon {
  font-size: 1.2rem;
}

/* le bouclier ne se joue jamais manuellement : il s'auto-consomme */
.hand-card.passive {
  background: var(--border);
  color: var(--text);
  cursor: default;
  opacity: 0.85;
}

.hand-card.passive:hover {
  transform: none;
}

/* carte armée, en attente d'une cible (case de la grille ou joueur) */
.hand-card.targeting {
  box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.4);
  transform: translateY(-2px);
}

.hand-cards:empty::after {
  content: "Aucune carte en main pour l'instant.";
  color: var(--muted);
  font-size: 0.9rem;
}

/* Bandeau de jeu sur téléphone : mesuré à 221px de haut (27% d'un écran de
   812px) faute de resserrement dédié — on réduit les marges et la taille du
   chrono, et on autorise les actions à s'enrouler proprement plutôt que de
   déborder sur une seule ligne trop large */
@media (max-width: 480px) {
  .game-bar {
    padding: 10px 12px;
    gap: 8px;
  }

  .gb-timer strong {
    font-size: 1.8rem;
  }

  .gb-scores-card {
    gap: 10px;
  }

  .gb-actions {
    flex-wrap: wrap;
    gap: 6px;
    row-gap: 8px;
  }

  .gb-actions .mini {
    margin-left: 0;
  }

  .gb-top-row {
    gap: 6px;
  }

  .gb-side {
    gap: 4px;
  }
}

/* --- Téléphone en jeu (août 2026, retours Camill) ---
   Un SEUL cadre épinglé : celui du jeu (lettres + mot en cours), remonté juste
   sous le mini-bandeau. Avant, bandeau de jeu ET carte des lettres étaient
   collants tous les deux et mangeaient l'écran (« aucun cadre ne doit rester
   bloqué au-dessus, ça empêche de défiler »). Le bandeau de scores redescend
   simplement dans le flux, sous la zone de jeu. */
@media (max-width: 700px) {
  /* La piste RESTE affichée sur téléphone depuis qu'elle porte les scores
     (août 2026) : elle avait été masquée quand elle faisait doublon avec les
     plaques chiffrées, qui n'existent plus en jeu actif — la masquer
     reviendrait maintenant à n'afficher aucun score. Simplement resserrée. */
  .score-track {
    height: 74px;
    margin: 6px 4px 2px;
    padding-right: 38px;
  }

  .score-track-line,
  .score-track-avatars {
    right: 38px;
  }

  .stk-name {
    max-width: 52px;
    font-size: 0.65rem;
  }

  .stk-score {
    font-size: 0.95rem;
  }

  /* le pseudo reste affiché au-dessus de l'avatar (demande Camill), mais plus
     court sur téléphone : à 6 joueurs, c'est la largeur qui manque */
  .gb-player .gb-name {
    max-width: 56px;
    font-size: 0.8rem;
  }

  .gb-player .gb-score {
    font-size: 1.25rem;
  }

  .gb-scores-card {
    padding: 4px 0;
    gap: 8px;
  }

  /* CLAVIER FIXE AU NIVEAU DES POUCES (demande Camill, août 2026 : « sur
     téléphone les lettres à taper soient au niveau des pouces, et juste
     au-dessus le mot qu'on est en train d'écrire… que le clavier et le mot
     soient fixes par rapport au reste qui peut lui défiler »). La carte de jeu
     quitte le flux et se cale en BAS de l'écran : c'est là que sont les pouces,
     et le reste (piste, grille, chat) défile librement derrière.
     Elle contient déjà, dans cet ordre, le mot en cours puis les lettres puis
     Valider/Effacer/Mélanger — donc les lettres et les boutons tombent
     naturellement dans la zone atteignable. */
  #game-view:not(.round-over) .letters-card {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    z-index: 25;
    margin: 0;
    border-radius: 16px 16px 0 0;
    padding: 8px 8px calc(8px + env(safe-area-inset-bottom, 0px));
    box-shadow: 0 -4px 16px rgba(120, 90, 20, 0.18);
  }

  /* la hauteur réelle de cette carte (mesurée en JS) est réservée en bas du
     flux, sinon la fin de la grille resterait cachée dessous */
  #game-view:not(.round-over) {
    padding-bottom: var(--letters-card-height, 200px);
  }

  /* en fin de manche, la carte revient dans le flux : plus rien de fixe, on
     doit pouvoir parcourir toute la grille révélée */
  .letters-card {
    padding: 14px 8px;
  }

  /* PENDANT LA MANCHE (revue du 12 novembre 2026) : le mini-bandeau du site
     disparaît, ses boutons ⚙ 🔊 ✕ ont rejoint ⏳+ ⏸ dans le bandeau de jeu
     (placeHeaderActions(), app.js). Mesuré avant : 32 px de marge + 82 px de
     mini-bandeau au-dessus du bandeau de jeu, pour une grille réduite à 40 %
     de l'écran. */
  body.header-merged .mini-header {
    display: none;
  }

  body.header-merged .app-shell {
    padding-top: 8px;
  }

  body.header-merged .gb-actions .mini-header-actions {
    display: contents;
  }

  /* 42 px plutôt que 44 : cinq boutons doivent tenir à côté du chrono sur un
     écran de 360 px */
  body.header-merged .gb-actions .mini {
    min-width: 42px;
    min-height: 42px;
    padding: 6px;
  }

  body.header-merged .game-bar {
    padding: 8px 10px 6px;
  }

  body.header-merged .gb-actions {
    flex-wrap: nowrap;
    gap: 4px;
  }
}

/* cinq boutons sur la ligne du chrono : « Manche » s'efface, « 1/3 » reste */
@media (max-width: 440px) {
  body.header-merged .gb-round-word {
    display: none;
  }
}

/* =====================================================================
   Accueil en tableau de bord, pages intérieures, réglages en lignes
   (novembre 2026, demande Camill : « m'inspirer de tusmo.xyz pour les menus,
   catégories… sans trop copier »). Repris de tusmo : la grande carte pour
   l'action principale, les petits intitulés de section en majuscules, les
   cartes entières cliquables, les tuiles à icône, les pages au titre centré,
   les réglages « libellé à gauche / choix à droite » et les boutons à
   onglets. PAS repris : leur logo en lettres-tuiles, leur fond bleu canard,
   leurs majuscules très épaisses partout (retour Camill d'août : « trop bold »)
   — majuscules réservées aux intitulés de section et aux titres, en 700.
   ===================================================================== */

/* intitulé de section : petit, espacé, grisé — il range la page sans
   concurrencer le contenu */
.hub-section {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 26px 4px 10px;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

/* petit compteur à côté d'un intitulé (« SALONS OUVERTS 2 ») */
.hub-count {
  display: inline-block;
  min-width: 20px;
  padding: 1px 7px;
  border-radius: 10px;
  background: var(--banana);
  color: #4a2c00;
  font-size: 0.75rem;
  letter-spacing: 0;
  text-align: center;
}

/* titres de carte, de tuile et de page en majuscules (demande Camill : « si
   ça améliore la lisibilité ») */
.hub-title,
.hub-tile-title,
.card-title,
.page-title {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 700;
}

/* --- Carte principale : créer un salon --- */
.hub-hero {
  border: 2px solid var(--banana);
  margin-bottom: 0;
}

.hub-card-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 16px;
}

.hub-icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: 14px;
  background: var(--bg);
  font-size: 1.6rem;
}

.hub-hero .hub-title {
  margin: 0;
  font-size: 1.15rem;
}

.hub-sub {
  margin: 2px 0 0;
  font-size: 0.85rem;
  color: var(--muted);
}

/* --- « Jouer seul » : même carte cliquable qu'un salon ouvert, juste sous
   « Créer un salon » (revue du 12 novembre 2026) --- */
.hub-solo {
  margin-top: 14px;
  border: 2px solid var(--banana);
}

.hub-solo .hub-card-text {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.hub-solo .hub-title {
  font-size: 1.05rem;
}

.hub-solo-go {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--banana);
  color: #4a2c00;
  font-size: 1rem;
}

/* --- Salons ouverts : une carte entière cliquable par salon --- */
.room-card {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 14px 16px;
  text-align: left;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 20px;
  font-weight: 400;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
}

.room-card:hover:not(:disabled) {
  border-color: var(--banana-strong);
}

/* partie déjà lancée : on peut entrer, mais ça se voit d'un coup d'œil */
.room-card.playing {
  border-left: 4px solid var(--banana-strong);
}

.room-card-avatar {
  flex-shrink: 0;
}

.room-card-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.room-card-name {
  font-size: 1.05rem;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.room-card-sub {
  font-size: 0.82rem;
  color: var(--muted);
}

.room-card-side {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.room-card-count {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.room-card-arrow {
  font-size: 1.2rem;
  color: var(--banana-strong);
}

/* état vide : un cadre pointillé plutôt qu'une phrase perdue dans le vide */
.hub-empty {
  margin: 0;
  padding: 18px 16px;
  border: 2px dashed var(--border-strong);
  border-radius: 20px;
  text-align: center;
  color: var(--muted);
  font-size: 0.9rem;
}

/* --- Tuiles : stats, classement --- */
.hub-tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.hub-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 18px 12px 16px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 20px;
  text-align: center;
  font-weight: 400;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
}

.hub-tile:hover:not(:disabled) {
  border-color: var(--banana-strong);
}

.hub-tile-icon {
  font-size: 1.9rem;
  line-height: 1.2;
}

.hub-tile-title {
  font-size: 0.95rem;
}

.hub-tile-sub {
  font-size: 0.78rem;
  color: var(--muted);
  line-height: 1.3;
}

/* --- Pages intérieures : classement, stats --- */
.page-title {
  margin: 8px 0 4px;
  text-align: center;
  font-size: 1.4rem;
}

.page-sub {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin: 0 0 18px;
  text-align: center;
  color: var(--muted);
  font-size: 0.9rem;
}

/* le classement n'est plus enfermé dans une carte : chaque entrée EST une
   carte, comme les salons ouverts */
.page .scoreboard-list li {
  background: var(--surface);
  border-radius: 20px;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.06);
}

/* stats : les trois chiffres qu'on vient chercher, en grand */
.stat-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.stat-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 14px 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 20px;
  text-align: center;
}

.stat-tile strong {
  font-size: 1.6rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--banana-deep);
}

.stat-tile span {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.stats-content > .card {
  margin-bottom: 0;
}

.stats-words {
  margin: 0;
  line-height: 1.7;
}

/* --- Réglages du salon : une ligne par réglage --- */
.card-title {
  font-size: 1rem;
}

.setting {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
}

.setting > label,
.setting-label {
  margin: 0;
  font-weight: 500;
}

/* choix à onglets : libellé au-dessus, rangée de boutons pleine largeur */
.setting-stack {
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
}

.setting-unit {
  color: var(--muted);
  font-weight: 400;
  font-size: 0.85rem;
}

.setting-hint {
  display: block;
  margin: 0;
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--muted);
}

.setting-hint:empty {
  display: none;
}

input.setting-num {
  width: 76px;
  margin: 0;
  padding: 10px 8px;
  text-align: center;
  font-weight: 600;
}

.setting-pair {
  display: flex;
  align-items: center;
  gap: 8px;
}

.setting-pair-label {
  margin: 0;
  color: var(--muted);
  font-weight: 400;
}

/* case à cocher en fin de ligne, le texte explicatif à gauche */
.setting-check {
  justify-content: space-between;
  gap: 12px;
  margin: 0;
  padding: 12px 0;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}

.setting-check-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.setting-check-text strong {
  font-weight: 500;
}

.setting-check input[type="checkbox"] {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  accent-color: var(--banana-strong);
}

.setting-group {
  padding-left: 12px;
  border-left: 3px solid var(--banana);
}

.settings-note {
  margin: 12px 0 0;
  font-size: 0.8rem;
}

/* boutons à onglets : une piste crème, l'onglet choisi en jaune banane */
.segmented-group {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--seg-min, 80px), 1fr));
  gap: 4px;
  padding: 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 14px;
}

.segmented-group .seg {
  min-height: 40px;
  padding: 8px 6px;
  border-radius: 10px;
  background: transparent;
  color: var(--text);
  font-weight: 500;
  font-size: 0.9rem;
  line-height: 1.2;
  box-shadow: none;
}

.segmented-group .seg:hover:not(:disabled):not(.selected) {
  background: rgba(252, 211, 77, 0.25);
  transform: none;
}

.segmented-group .seg.selected {
  background: linear-gradient(180deg, #fde68a, var(--banana) 55%, var(--banana-strong));
  color: #4a2c00;
  font-weight: 700;
  box-shadow: 0 1px 3px rgba(120, 90, 20, 0.25);
}

.segmented-group .seg:disabled {
  opacity: 1; /* l'atténuation vient déjà du fieldset désactivé */
  cursor: default;
}

/* =====================================================================
   Relief des boutons — réglage « C, doux » (12 novembre 2026, choisi par
   Camill parmi trois essais capturés : A fin 3 px, B marqué 5 px, C doux).
   Historique : un premier relief avait été essayé puis retiré en août
   (« je ne suis pas convaincu ») ; réessayé à sa demande en novembre, en
   s'inspirant de tusmo.xyz SANS le copier : chez eux la tranche est grise et
   identique partout, ici c'est une version FONCÉE DE LA COULEUR du bouton
   lui-même (ambre sous le jaune, vert sombre sous Valider, beige foncé sous
   les neutres et les cartes cliquables), à 70 % d'opacité, avec une ombre
   floue légère. Au toucher, le bouton descend de 3 px : la touche qu'on
   enfonce. Exclus : la grille d'avatar (une grille de choix, pas une rangée
   d'actions), les onglets non choisis et le logo-lien du mini-bandeau.
   ===================================================================== */
button {
  --tranche: #c47f0a;
}
button.secondary,
.room-card,
.hub-tile {
  --tranche: #cdb877;
}
#submit-word {
  --tranche: #0f5f2d;
}

button,
.room-card,
.hub-tile {
  --relief-course: 3px;
  --relief-ombre: 0 4px 0 color-mix(in srgb, var(--tranche) 70%, transparent), 0 8px 14px rgba(120, 90, 20, 0.14);
  --relief-ombre-enfoncee: 0 1px 0 color-mix(in srgb, var(--tranche) 70%, transparent), 0 2px 4px rgba(120, 90, 20, 0.12);
}

button:not(.seg):not(.avatar-emoji-btn):not(.avatar-color-btn):not(.avatar-shape-btn):not(.mini-logo),
.room-card,
.hub-tile {
  box-shadow: var(--relief-ombre);
  transition: transform 90ms ease, box-shadow 90ms ease, opacity 120ms ease;
}

/* plus de petit soulèvement au survol : c'est l'enfoncement qui parle */
button:not(.seg):not(.avatar-emoji-btn):not(.avatar-color-btn):not(.avatar-shape-btn):not(.mini-logo):hover:not(:disabled) {
  transform: none;
}

button:not(.seg):not(.avatar-emoji-btn):not(.avatar-color-btn):not(.avatar-shape-btn):not(.mini-logo):active:not(:disabled),
.room-card:active:not(:disabled),
.hub-tile:active:not(:disabled) {
  transform: translateY(var(--relief-course));
  box-shadow: var(--relief-ombre-enfoncee);
}

/* Valider garde son halo vert en plus de la tranche (sa règle par
   identifiant l'emporterait sinon sur celles ci-dessus) */
#submit-word {
  box-shadow: var(--relief-ombre), 0 6px 18px rgba(21, 128, 61, 0.3);
}
#submit-word:active:not(:disabled) {
  transform: translateY(var(--relief-course));
  box-shadow: var(--relief-ombre-enfoncee), 0 3px 10px rgba(21, 128, 61, 0.3);
}

/* l'onglet choisi d'un groupe à onglets : relief très léger */
.segmented-group .seg.selected {
  box-shadow: 0 2px 0 #c47f0a;
}

/* =====================================================================
   Comptes joueurs (novembre 2026) : encart « votre compte est prêt »,
   connexion sur un nouvel appareil, page « Mon compte ».
   ===================================================================== */
/* lien discret « J'ai déjà un compte » : un lien, pas une action principale.
   Par identifiant : la règle générale du relief (plus haut, chaîne de :not)
   l'emporterait sinon et lui redonnerait fond et tranche de bouton */
#show-login {
  display: block;
  margin: 14px auto 0;
  padding: 4px 8px;
  background: none;
  border: none;
  color: var(--accent);
  font-weight: 500;
  text-decoration: underline;
  box-shadow: none;
}
#show-login:active:not(:disabled) {
  transform: none;
  box-shadow: none;
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.login-form button {
  margin-top: 6px;
}

.account-notice {
  border: 2px solid var(--banana-strong);
}
.account-notice p {
  margin: 0 0 8px;
}
.account-notice-title {
  font-weight: 600;
  font-size: 1.05rem;
}

/* le code : gros, espacé, sélectionnable d'un geste pour le recopier */
.account-code {
  margin: 10px 0;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px dashed var(--border-strong);
  border-radius: 12px;
  text-align: center;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  user-select: all;
  overflow-wrap: anywhere;
}

.account-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  margin: 6px 0;
}

.compte-card p {
  margin: 0 0 10px;
}
.compte-card p:last-child {
  margin-bottom: 0;
}
.compte-card p:empty {
  display: none;
}

.compte-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 0;
}
.compte-row + .compte-row {
  border-top: 1px solid var(--border);
}
.compte-row > span {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.compte-row strong {
  font-weight: 500;
}
.compte-row button {
  flex-shrink: 0;
}

/* =====================================================================
   Page « Mes mots » (novembre 2026) : vocabulaire, mots qui m'échappent,
   journal de tous les mots trouvés.
   ===================================================================== */
#mots-content .stat-tiles {
  grid-template-columns: repeat(2, 1fr);
}
#mots-content .card p {
  margin: 0 0 10px;
}
#mots-content .card p:last-child {
  margin-bottom: 0;
}

/* un mot touchable : sa définition s'ouvre au clic, comme dans la grille */
.mots-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  line-height: 1.3;
}
.mot-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  padding: 5px 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-weight: 500;
  cursor: pointer;
}
.mot-chip:hover,
.mot-chip:focus-visible {
  border-color: var(--banana-strong);
  outline: none;
}
.mot-chip .muted {
  font-size: 0.78rem;
  font-weight: 400;
}

/* part des mots trouvés par longueur : une barre par longueur */
.len-title {
  font-weight: 500;
  margin-top: 6px;
}
.len-row {
  display: grid;
  grid-template-columns: 5.5em 1fr 3.2em;
  align-items: center;
  gap: 8px;
  margin: 4px 0;
  font-size: 0.9rem;
}
.len-label {
  color: var(--muted);
}
.len-bar {
  height: 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
}
.len-bar span {
  display: block;
  height: 100%;
  background: var(--banana-strong);
  border-radius: 999px;
}
.len-pct {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.journal-search {
  width: 100%;
  margin: 4px 0 10px;
}
.journal-summary:empty {
  display: none;
}
#journal-list {
  margin-top: 12px;
}
#journal-more {
  display: block;
  margin: 14px auto 0;
}

/* lien de « Mes stats » vers « Mes mots » : une tuile pleine largeur, en ligne */
.stats-to-mots {
  width: 100%;
  margin-top: 18px;
  flex-direction: row;
  justify-content: center;
  gap: 10px;
}
.stats-to-mots .hub-tile-icon {
  font-size: 1.4rem;
}
