/* Reset & Base */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  margin: 0;
  padding: 0;
  height: 100%;
  background: linear-gradient(to bottom, #0d1821 0%, #1a2332 100%);
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  background: linear-gradient(to bottom, #0d1821 0%, #1a2332 100%);
  color: var(--color-text);
  line-height: 1.5;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
}
/* REPARATION 1/3 — le selecteur `:root {` manquait ici.
   Les declarations de variables qui suivent flottaient au niveau racine et
   l'accolade de la ligne suivante ne fermait rien : le parseur les consommait
   comme un prelude de selecteur invalide, ce qui faisait tomber la regle `body`
   juste apres (heureusement un doublon exact de celle du dessus, donc sans
   degat visible). En revanche 5 variables n'etaient definies NULLE PART
   ailleurs — --gradient-primary, --gradient-dice, --spacing-xs, --spacing-xl,
   --radius-sm — alors qu'elles sont referencees 21 fois dans le fichier. Ces
   var() retombaient en invalid-at-computed-value-time : la propriete concernee
   etait purement abandonnee. */
:root {
  /* Premium Palette - Inspired by Logo */
  --color-primary-dark: #0d2847;
  --color-primary-light: #2d7ab8;
  --color-secondary: var(--color-mocha);
  --color-success: #5da283;
  --color-error: #c85a54;
  --color-warning: #d4a574;
  
  /* Modern Gradients 2025 - Minimal use */
  --gradient-primary: linear-gradient(135deg, #1a5a8a 0%, #0d2847 100%);
  --gradient-dice: linear-gradient(145deg, #ffffff 0%, #f8f6f0 100%);
  
  /* Modern Shadows 2025 - Ultra Light */
  --shadow-xs: 0 1px 3px rgba(0, 0, 0, 0.06);
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.08);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.12);
  --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.15);
  --shadow-xl: 0 12px 48px rgba(0, 0, 0, 0.18);
  
  /* Glassmorphism 2025 */
  --glass-bg: rgba(255, 255, 255, 0.1);
  --glass-border: rgba(255, 255, 255, 0.18);
  --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
  
  /* Backgrounds & Text */
  --color-bg-light: #1a2332;
  --color-text-dim: #a8a39a;
  
  /* Spacing supplémentaire */
  --spacing-xs: 0.5rem;
  --spacing-xl: 3rem;
  
  /* Radius supplémentaire */
  --radius-sm: 0.375rem;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  background: linear-gradient(to bottom, #0d1821 0%, #1a2332 100%);
  color: var(--color-text);
  line-height: 1.5;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
}

/* ⚠️ IMPORTANT : Variables CSS principales définies dans index.html (critical inline CSS)
   Seules les variables supplémentaires non-critiques sont définies ici pour éviter duplication */

:root {
  /* Mocha Mousse 2025 - Pantone Color of the Year */
  --color-mocha: #A89080;
  --color-mocha-light: #C4B5A0;
  --color-mocha-dark: #6B5D53;
  /* ⚠️ MOCHA POUR UN BOUTON PLEIN, C'EST-À-DIRE SOUS DU TEXTE BLANC.
     `--color-mocha` est une couleur d'ACCENT : elle est faite pour être posée
     SUR un fond sombre, pas pour en servir. Mesuré au navigateur le 10 août,
     blanc sur `#A89080` : **3,01:1** pour un minimum de 4,5. Les six boutons
     `.network-btn--primary` du multijoueur en ligne étaient dans ce cas.
     Même clarté de teinte et de saturation, luminosité abaissée jusqu'au seuil :
     blanc sur `#8a705e` = **4,63:1**. L'autre extrémité du dégradé,
     `--color-mocha-dark`, passait déjà (6,33) et ne bouge pas.
     ➡️ Ne PAS corriger cela en changeant `--color-mocha` : la variable est
     employée partout comme couleur de texte sur fond sombre, où elle est juste.
     Un accent et un fond de bouton sont deux rôles, donc deux variables. */
  --color-mocha-plein: #8a705e;
  
  /* Modern Palette 2025 - Soothing & Warm */
  --color-primary-dark: #0d2847;
  --color-primary-light: #2d7ab8;
  --color-secondary: #D4A574; /* Reduced from flashy gold */
  --color-success: #5da283;
  --color-error: #c85a54;
  --color-warning: #d4a574;
}
/* ^ REPARATION 2/3 — CETTE ACCOLADE EST LA PLUS IMPORTANTE DU FICHIER.
   Le bloc `:root {` ouvert plus haut n'etait jamais referme : il avalait
   7 370 lignes, jusqu'a une accolade surnumeraire situee bien plus bas.
   Chrome ne jetait pas ces lignes, il les reinterpretait en CSS Nesting —
   758 regles imbriquees dont les selecteurs devenaient `:root .header`,
   `:root .die--rolling`... Deux consequences, toutes deux verifiees dans le
   navigateur avant correction :
     1. @keyframes n'etant PAS imbricable, 62 des 78 animations du fichier
        etaient supprimees en silence. 45 declarations `animation:` pointaient
        vers un nom inexistant : les des ne tremblaient pas au lancer, les
        confettis ne tombaient pas, l'ecran de victoire ne zoomait pas.
     2. La specificite des 758 regles etait gonflee de +(0,1,0). C'est LA vraie
        explication du « piege n°10 » de CLAUDE.md et des !important qu'il a
        fallu poser : ce n'etait pas un mystere de cascade, juste de la
        specificite ordinaire.
   Risque supprime au passage : le rattrapage reposait entierement sur le
   support natif de CSS Nesting (Chrome 112+). Sur une WebView Android ancienne,
   ces 758 regles auraient ete rejetees et le jeu se serait affiche quasiment
   sans style. */

/* ==================== HEADER PROTOTYPE 2 - MINIMALISTE ==================== */

/* Header principal */
.header {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--spacing-sm);
  padding-top: max(var(--spacing-sm), var(--safe-area-top));
  padding-left: max(var(--spacing-md), var(--safe-area-left));
  padding-right: max(var(--spacing-md), var(--safe-area-right));
  padding-bottom: var(--spacing-sm);
  background: transparent;
  box-shadow: none;
  border-bottom: none;
  position: relative;
  z-index: 100;
}

/* Bouton retour - VISUELLEMENT DISTINCT */
.header__btn--back {
  /* ✅ FIX iOS: Taille minimale pour accessibilité tactile */
  min-width: 52px;
  min-height: 52px;
  background: var(--gradient-primary);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  color: var(--color-cream);
  font-size: 1.5rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-md);
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  position: relative;
  overflow: hidden;
}

.header__btn--back svg {
  stroke: currentColor;
}

.header__btn--menu svg {
  stroke: currentColor;
}

.header__btn--back::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  background: rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  transition: width 0.6s, height 0.6s;
}

/* Hover uniquement desktop */
@media (hover: hover) and (pointer: fine) {
  .header__btn--back:hover {
    transform: translateX(-4px);
    border-color: var(--color-mocha-light);
    box-shadow: var(--shadow-lg);
  }
  
  .header__btn--back:hover::before {
    width: 200px;
    height: 200px;
  }
}

/* ✅ Feedback tactile mobile CRITIQUE */
.header__btn--back:active {
  transform: translateX(-2px) scale(0.95);
}

/* ✅ Focus clavier visible */
.header__btn--back:focus-visible {
  outline: 3px solid white;
  outline-offset: 3px;
}

/* Titre central */
.header__title {
  text-align: center;
  font-size: 1.75rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin: 0;
}

.header__title-emoji {
  font-size: 1.5rem;
  filter: drop-shadow(0 0 4px rgba(168, 144, 128, 0.3)) brightness(1.1);
}

.header__title-text {
  background: var(--gradient-gold-text);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.15));
}

/* Bouton menu contextuel - Identique au bouton retour */
.header__btn--menu {
  /* ✅ FIX iOS: Taille minimale pour accessibilité tactile */
  min-width: 52px;
  min-height: 52px;
  background: var(--gradient-primary);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  color: var(--color-cream);
  font-size: 1.75rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-md);
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  position: relative;
  overflow: hidden;
}

.header__btn--menu::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  background: rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  transition: width 0.6s, height 0.6s;
}

/* Hover uniquement desktop */
@media (hover: hover) and (pointer: fine) {
  .header__btn--menu:hover {
    transform: translateX(4px);
    border-color: var(--color-mocha-light);
    box-shadow: var(--shadow-lg);
  }
  
  .header__btn--menu:hover::before {
    width: 200px;
    height: 200px;
  }
}

/* ✅ Feedback tactile mobile CRITIQUE */
.header__btn--menu:active {
  transform: translateX(2px) scale(0.95);
}

/* ✅ Focus clavier visible */
.header__btn--menu:focus-visible {
  outline: 3px solid white;
  outline-offset: 3px;
}

/* ✅ Style de base pour tous les boutons */
.header__btn {
  position: relative;
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

.header__btn-icon {
  display: block;
}

/* ==================== INDICATEUR DE CONNEXION RÉSEAU (GLOBAL) ==================== */

.network-status-indicator {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.7rem;
  border-radius: 2rem;
  background: rgba(93, 162, 131, 0.15);
  border: 1px solid rgba(93, 162, 131, 0.3);
  font-size: 0.7rem;
  font-weight: 500;
  color: var(--color-success, #5da283);
  transition: all 0.3s ease;
  animation: uiFondu 0.3s ease;
}

.network-status-indicator__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-success, #5da283);
  box-shadow: 0 0 6px var(--color-success, #5da283);
  animation: pulse-dot 2s ease-in-out infinite;
}

@keyframes pulse-dot {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.7; transform: scale(0.9); }
}

/* État : Reconnexion en cours */
.network-status-indicator--reconnecting {
  background: rgba(212, 165, 116, 0.15);
  border-color: rgba(212, 165, 116, 0.3);
  color: var(--color-warning, #d4a574);
}

.network-status-indicator--reconnecting .network-status-indicator__dot {
  background: var(--color-warning, #d4a574);
  box-shadow: 0 0 6px var(--color-warning, #d4a574);
  animation: blink-dot 0.8s ease-in-out infinite;
}

@keyframes blink-dot {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

/* État : Déconnecté */
.network-status-indicator--disconnected {
  background: rgba(200, 90, 84, 0.15);
  border-color: rgba(200, 90, 84, 0.3);
  color: var(--color-error, #c85a54);
}

.network-status-indicator--disconnected .network-status-indicator__dot {
  background: var(--color-error, #c85a54);
  box-shadow: 0 0 6px var(--color-error, #c85a54);
  animation: none;
}

/* État : Erreur */
.network-status-indicator--error {
  background: rgba(200, 90, 84, 0.2);
  border-color: rgba(200, 90, 84, 0.4);
  color: var(--color-error, #c85a54);
  cursor: pointer;
}

.network-status-indicator--error:hover {
  background: rgba(200, 90, 84, 0.3);
}

/* ==================== OVERLAY DE RECONNEXION ==================== */

.reconnect-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(13, 24, 33, 0.9);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  z-index: 9000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.5rem;
  animation: uiFondu 0.3s ease;
}

.reconnect-overlay__icon {
  width: 64px;
  height: 64px;
  color: var(--color-warning, #d4a574);
  animation: spin-slow 2s linear infinite;
}

@keyframes spin-slow {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.reconnect-overlay__title {
  font-size: 1.4rem;
  font-weight: 600;
  color: var(--color-text, #f5f5dc);
  text-align: center;
}

.reconnect-overlay__subtitle {
  font-size: 0.95rem;
  color: var(--color-mocha-light, #C4B5A0);
  text-align: center;
}

.reconnect-overlay__attempts {
  font-size: 0.85rem;
  color: var(--color-mocha, #A89080);
  margin-top: 0.5rem;
}

.reconnect-overlay__btn {
  margin-top: 1rem;
  padding: 0.75rem 1.5rem;
  border-radius: 0.5rem;
  background: var(--color-primary, #1a5a8a);
  color: white;
  font-weight: 500;
  border: none;
  cursor: pointer;
  transition: all 0.2s ease;
}

.reconnect-overlay__btn:hover {
  background: var(--color-primary-dark, #14466b);
  transform: translateY(-1px);
}

.reconnect-overlay__btn--secondary {
  background: transparent;
  border: 1px solid var(--color-mocha, #A89080);
  color: var(--color-mocha-light, #C4B5A0);
}

.reconnect-overlay__btn--secondary:hover {
  background: rgba(168, 144, 128, 0.1);
}

/* ==================== MENU CONTEXTUEL (DROPDOWN) ==================== */

.header-menu {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 200;
  display: none;
}

.header-menu:not([hidden]) {
  display: block;
}

.header-menu__overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.1);
  backdrop-filter: blur(8px);
  animation: uiFondu 0.2s ease-out;
}

/* ==================== ÉCRAN DE CHARGEMENT ==================== */

/* ==================== LOADER (Écran de chargement) ==================== */

.loader {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(to bottom, #0d1821 0%, #1a2332 100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3rem;
  z-index: 9999;
  transition: opacity 0.5s ease-out, visibility 0.5s ease-out;
  opacity: 1;
  visibility: visible;
}

.loader--hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.loader__digits {
  display: flex;
  gap: 1.5rem;
  align-items: center;
  animation: fadeInDigits 0.5s ease-out forwards;
  will-change: opacity, transform;
}

@keyframes fadeInDigits {
  from {
    opacity: 0;
    transform: translateY(-20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ✨ Brillance ultra-soft 2025 - effet organique */
.loader__digit::before {
  content: '';
  position: absolute;
  /* Positionnement plus naturel et organique */
  top: 5%;
  left: 5%;
  right: 40%;
  bottom: 50%;
  
  /* Dégradé radial pour effet plus naturel (comme vraie lumière) */
  background: radial-gradient(
    ellipse at 30% 30%,
    rgba(255, 255, 255, 0.6) 0%,
    rgba(255, 255, 255, 0.35) 30%,
    rgba(255, 255, 255, 0.15) 60%,
    rgba(255, 255, 255, 0) 100%
  );
  
  border-radius: 50% 40% 60% 50%;
  pointer-events: none;
  opacity: 1;
  
  /* Micro-animation pour effet "vivant" */
  animation: breatheLight 4s ease-in-out infinite;
}

/* 🌬️ Animation respiration de lumière */
@keyframes breatheLight {
  0%, 100% {
    opacity: 0.85;
    transform: scale(1);
  }
  50% {
    opacity: 1;
    transform: scale(1.05);
  }
}

.loader__digit:nth-child(1) {
  animation: 
    bounceDice 1.6s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite,
    shimmerGlow 3s ease-in-out infinite;
  animation-delay: 0s, 0s;
}

.loader__digit:nth-child(2) {
  animation: 
    bounceDice 1.6s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite,
    shimmerGlow 3s ease-in-out infinite;
  animation-delay: 0.2s, 0.5s;
}

.loader__digit:nth-child(3) {
  animation: 
    bounceDice 1.6s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite,
    shimmerGlow 3s ease-in-out infinite;
  animation-delay: 0.4s, 1s;
}

.loader__digit {
  width: 120px;
  height: 120px;
  
  /* 🎨 ULTRA TENDANCE 2025 - Glassmorphism + Soft Gradients */
  background: 
    /* Couche 1 : Lumière douce top-left (très subtile) */
    radial-gradient(
      circle at 20% 20%,
      rgba(255, 255, 255, 0.35) 0%,
      rgba(255, 255, 255, 0) 50%
    ),
    /* Couche 2 : Gradient ultra-doux Mocha (7 stops pour smoothness) */
    linear-gradient(
      140deg,
      #F0E4D7 0%,
      #E8D5C4 15%,
      #D4C4B0 30%,
      #C4B5A0 45%,
      #A89080 60%,
      #8B7869 80%,
      #6B5D53 100%
    );
  
  /* Glassmorphism ultra-subtil */
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  
  border-radius: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 5rem;
  font-weight: 700;
  
  /* Couleur texte avec micro-gradient pour sophistication */
  background-clip: padding-box;
  color: #1a1410;
  text-shadow: 
    0 1px 2px rgba(0, 0, 0, 0.15),
    0 0 20px rgba(232, 213, 196, 0.3);
  
  /* 💎 Ombres 2025 - Ultra soft avec blur élevé */
  box-shadow: 
    /* Ombre principale très douce et large */
    0 20px 60px rgba(168, 144, 128, 0.4),
    /* Ombre secondaire pour profondeur */
    0 8px 25px rgba(107, 93, 83, 0.3),
    /* Ombre proche très subtile */
    0 2px 8px rgba(107, 93, 83, 0.2),
    /* Lumière inset top (glassmorphism) */
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    /* Ombre inset bottom douce */
    inset 0 -1px 4px rgba(107, 93, 83, 0.2),
    /* Halo lumineux externe */
    0 0 50px rgba(232, 213, 196, 0.25);
  
  position: relative;
  opacity: 1;
  
  /* Animation bounce ultra-fluide avec easing personnalisé */
  animation: 
    bounceDice 1.6s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite,
    shimmerGlow 3s ease-in-out infinite;
  
  /* ✨ Gradient Border 2025 - plus tendance qu'une border simple */
  border: 1.5px solid transparent;
  background-clip: padding-box;
  
  /* Effet de "glow border" avec pseudo-element */
  overflow: hidden;
}

/* 🌟 Gradient Border externe avec pseudo-element */
.loader__digit::after {
  content: '';
  position: absolute;
  inset: -1.5px;
  border-radius: 22px;
  padding: 1.5px;
  background: linear-gradient(
    135deg,
    rgba(255, 255, 255, 0.4) 0%,
    rgba(232, 213, 196, 0.3) 25%,
    rgba(168, 144, 128, 0.2) 50%,
    rgba(232, 213, 196, 0.3) 75%,
    rgba(255, 255, 255, 0.4) 100%
  );
  -webkit-mask: 
    linear-gradient(#fff 0 0) content-box, 
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  opacity: 0.8;
  animation: rotateBorder 4s linear infinite;
}

/* 💫 Animation shimmer ultra-subtile */
@keyframes shimmerGlow {
  0%, 100% {
    filter: brightness(1) drop-shadow(0 0 15px rgba(232, 213, 196, 0.3));
  }
  50% {
    filter: brightness(1.08) drop-shadow(0 0 25px rgba(232, 213, 196, 0.5));
  }
}

/* 🔄 Rotation subtile du gradient border */
@keyframes rotateBorder {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

/* 🎯 Animation bounce ULTRA FLUIDE avec easing naturel */
@keyframes bounceDice {
  0% {
    transform: translateY(0) scale(1);
  }
  10% {
    /* Très légère compression avant le saut */
    transform: translateY(1px) scale(0.995);
  }
  25% {
    /* Début du saut - accélération */
    transform: translateY(-10px) scale(1.01);
  }
  40% {
    /* Montée continue - pic d'accélération */
    transform: translateY(-25px) scale(1.025);
  }
  50% {
    /* Point culminant - légère pause naturelle */
    transform: translateY(-30px) scale(1.03);
  }
  60% {
    /* Redescente - début de décélération */
    transform: translateY(-25px) scale(1.025);
  }
  75% {
    /* Continue la descente */
    transform: translateY(-10px) scale(1.01);
  }
  85% {
    /* Approche du sol */
    transform: translateY(-2px) scale(0.998);
  }
  90% {
    /* Micro-rebond au sol */
    transform: translateY(0) scale(0.995);
  }
  95% {
    /* Retour à la normale - début stabilisation */
    transform: translateY(0) scale(0.998);
  }
  100% {
    /* Repos complet */
    transform: translateY(0) scale(1);
  }
}

/* Responsive loader */
@media (max-width: 768px) {
  .loader__digit {
    width: 90px;
    height: 90px;
    font-size: 3.5rem;
  }
  
  .loader__digits {
    gap: 1rem;
  }
}

/* @keyframes fadeIn — doublon strict retire (A9). Definition unique l.8252 avant retraits. */

.header-menu__content {
  position: absolute;
  top: 4.5rem;
  right: 1rem;
  background: rgba(13, 24, 33, 0.85);
  backdrop-filter: blur(12px);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  border: 1px solid rgba(255, 255, 255, 0.12);
  min-width: 200px;
  overflow: hidden;
  animation: menuDeroulement 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* @keyframes slideDown — definition morte retiree (A9) : une definition plus bas dans le
   fichier (l.5680 avant retraits) gagnait deja pour TOUT le document, donc
   celle-ci ne s'appliquait nulle part. Le rendu est inchange. */

.header-menu__item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-sm) var(--spacing-md);
  background: none;
  border: none;
  color: var(--color-text);
  font-size: 1rem;
  text-align: left;
  cursor: pointer;
  transition: all 0.2s ease;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

/* ⚠️ SANS CETTE REGLE, L'ATTRIBUT `hidden` DU HTML NE MASQUE RIEN ICI.
   `[hidden] { display: none }` vient de la feuille de l'AGENT UTILISATEUR, et
   TOUTE declaration d'auteur la bat — la specificite n'entre meme pas en jeu,
   c'est l'origine de la regle qui tranche. Le `display: flex` ci-dessus suffisait
   donc a rendre l'attribut decoratif.
   Constate le 9 aout 2026 : `#btnConfidentialitePubs` porte `hidden` dans
   index.html et app.js ne le revele que si le UMP l'exige (donc jamais sur le
   web) — il s'affichait quand meme, et n'ouvrait rien. Mesure : c'etait le SEUL
   element du document a porter `hidden` avec un `display` calcule autre que
   `none`.
   ➡️ Regle generale, pour tous les items presents et a venir. Le controle qui
   prouve quelque chose :
      [...document.querySelectorAll('[hidden]')]
        .filter(e => getComputedStyle(e).display !== 'none')
   doit renvoyer un tableau vide. */
.header-menu__item[hidden] {
  display: none;
}

.header-menu__item:last-child {
  border-bottom: none;
}

@media (hover: hover) and (pointer: fine) {
  .header-menu__item:hover {
    background: rgba(168, 144, 128, 0.15);
    padding-left: 1.25rem;
    color: var(--color-mocha-light);
  }
}

.header-menu__item:active {
  background: rgba(168, 144, 128, 0.25);
  transform: scale(0.98);
}

.header-menu__item:focus-visible {
  outline: 2px solid var(--color-mocha-light);
  outline-offset: -2px;
  z-index: 10;
}

.header-menu__icon {
  min-width: 1.5rem;
  text-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.header-menu__icon svg {
  width: 20px;
  height: 20px;
  color: var(--color-mocha-light);
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  transition: all 0.3s ease;
  flex-shrink: 0;
}

/* Animation hover des icônes */
.header-menu__item:hover .header-menu__icon svg {
  color: #ffffff;
  transform: scale(1.1);
}

/* Icônes de thème (Moon/Sun) - Toggle visuel */
.theme-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

.theme-icon--moon {
  opacity: 1;
  transform: translate(-50%, -50%) rotate(0deg);
}

.theme-icon--sun {
  opacity: 0;
  transform: translate(-50%, -50%) rotate(180deg);
}

/* Mode clair actif */
body.light-mode .theme-icon--moon {
  opacity: 0;
  transform: translate(-50%, -50%) rotate(-180deg);
}

body.light-mode .theme-icon--sun {
  opacity: 1;
  transform: translate(-50%, -50%) rotate(0deg);
}

.header-menu__text {
  flex: 1;
  font-weight: 500;
}

/* Séparateur visuel dans le menu */
.header-menu__separator {
  height: 1px;
  background: rgba(255, 255, 255, 0.1);
  margin: var(--spacing-xs) 0;
}

/* Item de langue actif */
.header-menu__item--active {
  background: rgba(168, 144, 128, 0.1);
  font-weight: 600;
}

/* Coche pour la langue active */
.header-menu__check {
  margin-left: auto;
  color: var(--color-mocha-light);
  font-size: 1.2rem;
  font-weight: bold;
}


/* ✅ AMÉLIORATION 1 : Phase centrée (conservée) */
.header__phase {
  font-size: 0.875rem;
  font-weight: 600;
  padding: var(--spacing-xs) var(--spacing-sm);
  background: var(--color-primary);
  border-radius: var(--radius-md);
  text-transform: uppercase;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}

/* Compatibilité anciens boutons (si utilisés ailleurs) */
.header__options-btn {
  font-size: 1.5rem;
  background: none;
  border: none;
  color: var(--color-text);
  cursor: pointer;
  padding: var(--spacing-xs);
  border-radius: var(--radius-sm);
  transition: background 0.2s;
}

.header__options-btn:hover {
  background: rgba(255, 255, 255, 0.1);
}

.header__options-btn:focus {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Container */
.container {
  flex: 1;
  padding: var(--spacing-md);
  padding-bottom: max(var(--spacing-md), var(--safe-area-bottom));
  max-width: 800px;
  margin: 0 auto;
  width: 100%;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* Container fullscreen pour l'écran d'accueil */
.screen--setup .container,
.screen--setup {
  padding: 0;
  max-width: none;
  margin: 0;
}

/* Logo 421 */
.game-logo {
  text-align: center;
  margin-bottom: var(--spacing-md);
  padding: var(--spacing-lg) var(--spacing-md);
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  flex-shrink: 1;
}

/* Desktop - prend de l'espace */
@media (min-width: 769px) {
  .game-logo {
    flex: 1;
  }
}

/* Tablets */
@media (max-width: 768px) and (min-width: 376px) {
  .game-logo {
    flex: 0;
    padding: var(--spacing-md) var(--spacing-sm);
    margin-bottom: var(--spacing-sm);
  }
}

/* iPhone SE & petits écrans - ultra compact */
@media (max-width: 375px) {
  .game-logo {
    flex: 0;
    padding: 0.75rem var(--spacing-sm) 0.5rem;
    margin-bottom: 0.5rem;
  }
}

.logo-text {
  display: flex;
  justify-content: center;
  gap: 0.3rem;
  font-size: 6rem;
  font-weight: 700;
  margin-bottom: var(--spacing-xs);
  position: relative;
  z-index: 1;
}

/* Tablets */
@media (max-width: 768px) and (min-width: 376px) {
  .logo-text {
    font-size: 4.5rem;
    gap: 0.2rem;
    margin-bottom: 0.25rem;
  }
}

/* iPhone SE & petits écrans */
@media (max-width: 375px) {
  .logo-text {
    font-size: 3.5rem;
    gap: 0.15rem;
    margin-bottom: 0.25rem;
  }
}

/* Chaque chiffre avec effet Mocha moderne, float et scintillement subtil */
.logo-digit {
  display: inline-block;
  background: linear-gradient(
    135deg,
    var(--color-mocha-light) 0%,
    var(--color-mocha) 50%,
    var(--color-mocha-light) 100%
  );
  background-size: 200% auto;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  font-family: Arial Black, sans-serif;
  letter-spacing: 0.05em;
  border: none !important;
  outline: none !important;
  box-shadow: none !important;
  
  /* Animations séparées pour chaque effet.
     Les noms sont prefixes depuis la regression du logo : `shimmer` est defini
     TROIS fois dans ce fichier (1088, 2016, 7347) et la derniere definition du
     document gagne pour tout le document. Le logo recuperait donc celle de la
     ligne 7347 — un balayage prevu pour .btn--glowing::before,
     transform: rotate(45deg) translateX(...) — au lieu de son halo dore.
     Les trois @keyframes logo421* sont definies dans le <style> critique de
     index.html, qui doit de toute facon les porter pour le rendu avant que ce
     fichier ne soit charge. Les @keyframes sont globales au document : les
     referencer d'ici fonctionne, et il n'y a ainsi qu'une seule definition. */
  animation-name: logo421Shine, logo421Float, logo421Halo;
  animation-duration: 2s, 3s, 1.5s;
  animation-timing-function: linear, ease-in-out, ease-in-out;
  animation-iteration-count: infinite, infinite, infinite;
  animation-fill-mode: forwards, forwards, forwards;
  
  filter: drop-shadow(0 0 6px rgba(168, 144, 128, 0.3))
          drop-shadow(0 2px 4px rgba(0, 0, 0, 0.15));
  
  will-change: transform, filter, opacity;
}

/* Animations décalées pour effet de vague - RENFORCÉ */
.logo-digit-1 {
  animation-delay: 0s, 0s, 0s;
}

.logo-digit-2 {
  animation-delay: 0s, 0.15s, 0.5s;
}

.logo-digit-3 {
  animation-delay: 0s, 0.3s, 1s;
}

/* Animation de scintillement léger - Moderne et subtile */
/* @keyframes shimmer — definition morte retiree (A9) : une definition plus bas dans le
   fichier (l.7396 avant retraits) gagnait deja pour TOUT le document, donc
   celle-ci ne s'appliquait nulle part. Le rendu est inchange. */

/* Ancien style gold-text (conservé pour compatibilité) */
.gold-text {
  background: linear-gradient(
    135deg,
    var(--color-mocha-light) 0%,
    var(--color-mocha) 25%,
    var(--color-mocha-light) 50%,
    var(--color-mocha) 75%,
    var(--color-mocha-light) 100%
  );
  background-size: 200% auto;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  animation: shine 3s linear infinite;
  filter: drop-shadow(0 0 8px rgba(168, 144, 128, 0.2))
          drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
  font-family: Arial Black, sans-serif;
  letter-spacing: 0.05em;
  font-size: 4.5rem;
  border: none !important;
  outline: none !important;
  box-shadow: none !important;
}

/* Container avec halo lumineux pulsant */
.gold-glow {
  position: relative;
  display: inline-block;
  margin-bottom: 0.5rem;
}

/* Désactivé temporairement - peut causer le cadre clignotant
.gold-glow::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 150%;
  height: 150%;
  background: radial-gradient(circle, rgba(255, 215, 0, 0.2) 0%, transparent 70%);
  animation: revanchePulse 2s ease-in-out infinite;
  pointer-events: none;
  z-index: 0;
}
*/

/* ANCIENS STYLES - Non utilisés avec la nouvelle structure
.logo-number {
  display: inline-block;
  background: linear-gradient(
    135deg,
    var(--color-mocha-light) 0%,
    var(--color-mocha) 25%,
    var(--color-mocha-light) 50%,
    var(--color-mocha) 75%,
    var(--color-mocha-light) 100%
  );
  background-size: 200% auto;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  animation: shine 3s linear infinite, uiFlottement 3s ease-in-out infinite;
  filter: drop-shadow(0 0 8px rgba(168, 144, 128, 0.2))
          drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
  position: relative;
  z-index: 1;
  font-family: Arial Black, sans-serif;
  letter-spacing: 0.05em;
}

.logo-number:nth-child(1) {
  animation-delay: 0s;
}

.logo-number:nth-child(2) {
  animation-delay: 0.2s;
}

.logo-number:nth-child(3) {
  animation-delay: 0.4s;
}

.logo-number--accent {
  background: linear-gradient(135deg, var(--color-mocha-light) 0%, var(--color-mocha) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 4px 8px rgba(250, 227, 130, 0.5));
}
*/

/* ==================== ANIMATIONS V2 ENHANCED ==================== */

/* Animation flottante pour le 421 */
/* @keyframes float — doublon strict retire (A9). Definition unique l.1691 avant retraits. */

/* Effet gold brillant */
@keyframes shine {
  0% {
    background-position: -200% center;
  }
  100% {
    background-position: 200% center;
  }
}

/* Pulse pour le halo lumineux */
/* @keyframes pulse — definition morte retiree (A9) : une definition plus bas dans le
   fichier (l.10125 avant retraits) gagnait deja pour TOUT le document, donc
   celle-ci ne s'appliquait nulle part. Le rendu est inchange. */

/* Animation particules - plus visible et dynamique */
@keyframes particleFloat {
  0% { 
    transform: translateY(0px) translateX(0px); 
    opacity: 0.8;
  }
  25% {
    transform: translateY(-25px) translateX(15px);
    opacity: 0.6;
  }
  50% { 
    transform: translateY(-40px) translateX(-10px); 
    opacity: 0.9;
  }
  75% {
    transform: translateY(-20px) translateX(20px);
    opacity: 0.5;
  }
  100% { 
    transform: translateY(0px) translateX(0px); 
    opacity: 0.8;
  }
}

/* Ancienne animation (conservée pour compatibilité) */
@keyframes logoFloat {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-12px); }
}

/* Classe pour appliquer l'animation float */
.float-animation {
  animation: uiFlottement 3s ease-in-out infinite;
}

.logo-subtitle {
  font-size: 0.95rem;
  color: #C9CDD6; /* ✅ SPRINT 1: Était #a8a39a (2.5:1), maintenant 4.6:1 WCAG AA */
  font-weight: 400;
  animation: subtitleFade 1s ease-out 0.5s backwards;
}

/* Tablets */
@media (max-width: 768px) and (min-width: 376px) {
  .logo-subtitle {
    font-size: 0.85rem;
  }
}

/* iPhone SE & petits écrans */
@media (max-width: 375px) {
  .logo-subtitle {
    font-size: 0.75rem;
  }
}

@keyframes subtitleFade {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ==================== V2 ENHANCED - ICÔNE DÉS FLOTTANTE ==================== */
.logo-dice-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 1rem;
  animation: uiFlottement 3s ease-in-out infinite;
}

.logo-dice-icon svg {
  width: 48px;
  height: 48px;
  /* P8 (12 août 2026) : l'icône portait un ambre en dur (#fbbf24) posé juste
     à côté de l'or de la marque (--color-gold #f4d03f, index.html:146). Un
     seul or. Le thème clair, lui, garde son trait mocha (règle plus bas). */
  color: var(--color-gold, #f4d03f);
  stroke: var(--color-gold, #f4d03f);
  filter: drop-shadow(0 4px 12px rgba(244, 208, 63, 0.4));
}

/* Tablets */
@media (max-width: 768px) and (min-width: 376px) {
  .logo-dice-icon {
    margin-bottom: 0.5rem;
  }
  
  .logo-dice-icon svg {
    width: 40px;
    height: 40px;
  }
}

/* iPhone SE & petits écrans */
@media (max-width: 375px) {
  .logo-dice-icon {
    margin-bottom: 0.375rem;
  }
  
  .logo-dice-icon svg {
    width: 32px;
    height: 32px;
  }
}

/* Styles pour les icônes SVG lucide */
.lucide-icon {
  display: inline-block;
  vertical-align: middle;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.primary-cta__icon svg {
  width: 24px;
  height: 24px;
}

.icon-menu-btn__icon svg {
  width: 32px;
  height: 32px;
  stroke: white;
}

/* ==================== V2 ENHANCED - PARTICULES DORÉES ==================== */
.home-particles {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}

.particle {
  position: absolute;
  width: 10px;
  height: 10px;
  background: radial-gradient(circle, rgba(255, 215, 0, 1) 0%, rgba(251, 191, 36, 0.9) 100%);
  border-radius: 50%;
  animation: particleFloat 4s ease-in-out infinite;
  box-shadow: 
    0 0 20px rgba(255, 215, 0, 1),
    0 0 10px rgba(251, 191, 36, 0.8);
  z-index: 1;
}

.particle:nth-child(1) {
  top: 20%;
  left: 10%;
  width: 6px;
  height: 6px;
  animation-delay: 0s;
  animation-duration: 5s;
}

.particle:nth-child(2) {
  top: 40%;
  right: 16%;
  width: 4px;
  height: 4px;
  animation-delay: 1.5s;
  animation-duration: 4.5s;
}

.particle:nth-child(3) {
  top: 60%;
  left: 20%;
  width: 5px;
  height: 5px;
  animation-delay: 0.8s;
  animation-duration: 5.5s;
}

.particle:nth-child(4) {
  top: 30%;
  right: 25%;
  width: 3px;
  height: 3px;
  animation-delay: 2s;
  animation-duration: 4s;
}

.particle:nth-child(5) {
  top: 70%;
  left: 30%;
  width: 4px;
  height: 4px;
  animation-delay: 1s;
  animation-duration: 4.8s;
}

/* Info card avec backdrop blur */
.home-info-card {
  background: rgba(30, 41, 59, 0.5);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-radius: var(--radius-lg);
  padding: 1rem 1.5rem;
  text-align: center;
  border: 1px solid rgba(100, 116, 139, 0.5);
  margin: var(--spacing-md) auto;
  max-width: 300px;
}

.home-info-card__title {
  font-size: 0.9rem;
  color: #e2e8f0;
  margin-bottom: 0.25rem;
}

.home-info-card__subtitle {
  font-size: 0.75rem;
  color: #B0B8C3;
}

/* ==================== BOTTOM NAVIGATION V2 ENHANCED ==================== */
.home-bottom-nav {
  padding: 1rem;
  max-width: 400px;
  margin: 0 auto;
  width: 100%;
  flex-shrink: 0;
}

/* Tablets */
@media (max-width: 768px) and (min-width: 376px) {
  .home-bottom-nav {
    padding: 0.75rem 1rem 1rem;
  }
}

/* iPhone SE & petits écrans - très compact */
@media (max-width: 375px) {
  .home-bottom-nav {
    padding: 0.5rem 0.75rem 0.75rem;
  }
}

/* Bouton principal JOUER MAINTENANT */
.home-play-btn {
  width: 100%;
  background: linear-gradient(to right, #3b82f6, #2563eb);
  border: none;
  border-radius: 1rem;
  padding: 1rem;
  margin-bottom: 0.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  color: white;
  font-size: 1.125rem;
  font-weight: 700;
  cursor: pointer;
  transition: all 0.3s ease;
  box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}

/* Tablets */
@media (max-width: 768px) and (min-width: 376px) {
  .home-play-btn {
    padding: 0.875rem;
    font-size: 1.0625rem;
    margin-bottom: 0.625rem;
  }
}

/* iPhone SE & petits écrans */
@media (max-width: 375px) {
  .home-play-btn {
    padding: 0.75rem;
    font-size: 1rem;
    margin-bottom: 0.5rem;
  }
}

.home-play-btn svg {
  stroke: white;
}

@media (hover: hover) and (pointer: fine) {
  .home-play-btn:hover {
    background: linear-gradient(to right, #2563eb, #1d4ed8);
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(59, 130, 246, 0.5);
  }
}

.home-play-btn:active {
  transform: translateY(0) scale(0.98);
}

/* Boutons multijoueur */
.home-multiplayer-btns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

/* Tablets */
@media (max-width: 768px) and (min-width: 376px) {
  .home-multiplayer-btns {
    gap: 0.5rem;
    margin-bottom: 0.625rem;
  }
}

/* iPhone SE & petits écrans */
@media (max-width: 375px) {
  .home-multiplayer-btns {
    gap: 0.375rem;
    margin-bottom: 0.5rem;
  }
}

.home-multiplayer-btn {
  background: rgba(74, 85, 104, 0.6);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(148, 163, 184, 0.2);
  border-radius: 0.875rem;
  padding: 0.875rem 0.75rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  color: rgba(255, 255, 255, 0.9);
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
  position: relative;
  text-align: center;
  line-height: 1.3;
}

/* Tablets */
@media (max-width: 768px) and (min-width: 376px) {
  .home-multiplayer-btn {
    padding: 0.75rem 0.625rem;
    font-size: 0.8rem;
  }
}

/* iPhone SE & petits écrans */
@media (max-width: 375px) {
  .home-multiplayer-btn {
    padding: 0.625rem 0.5rem;
    gap: 0.25rem;
    font-size: 0.75rem;
    border-radius: 0.75rem;
  }
}

.home-multiplayer-btn svg {
  stroke: rgba(255, 255, 255, 0.8);
  flex-shrink: 0;
}

/* iPhone SE & petits écrans - réduire icônes */
@media (max-width: 375px) {
  .home-multiplayer-btn svg {
    width: 18px;
    height: 18px;
  }
}

@media (hover: hover) and (pointer: fine) {
  .home-multiplayer-btn:hover {
    background: rgba(74, 85, 104, 0.8);
    border-color: rgba(148, 163, 184, 0.4);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  }
  
  .home-multiplayer-btn:hover svg {
    stroke: white;
  }
}

.home-multiplayer-btn:active {
  transform: translateY(0) scale(0.98);
}

.home-multiplayer-btn--disabled {
  background: rgba(51, 65, 85, 0.4);
  border-color: rgba(71, 85, 105, 0.3);
  color: rgba(148, 163, 184, 0.6);
  cursor: not-allowed;
  position: relative;
}

.home-multiplayer-btn--disabled svg {
  stroke: rgba(148, 163, 184, 0.5);
}

.home-multiplayer-btn--disabled:hover {
  background: rgba(51, 65, 85, 0.4);
  border-color: rgba(71, 85, 105, 0.3);
  transform: none;
  box-shadow: none;
}

.home-multiplayer-btn--disabled:hover svg {
  stroke: rgba(148, 163, 184, 0.5);
}

.home-multiplayer-btn__badge {
  display: inline-block;
  background: linear-gradient(135deg, #f59e0b, #d97706);
  color: white;
  font-size: 0.625rem;
  font-weight: 700;
  padding: 0.125rem 0.375rem;
  border-radius: 0.25rem;
  text-transform: uppercase;
  letter-spacing: 0.025em;
  margin-top: 0.125rem;
}

/* Header Multijoueur Local */
.multiplayer-header {
  text-align: center;
  margin-bottom: 2.5rem;
  padding: 1.5rem 0;
}

.multiplayer-header__icon {
  display: flex;
  justify-content: center;
  margin-bottom: 1rem;
  animation: uiFlottement 3s ease-in-out infinite;
}

.multiplayer-header__icon svg {
  stroke: var(--color-mocha);
  filter: drop-shadow(0 0 6px rgba(168, 144, 128, 0.2));
}

@keyframes uiFlottement {
  0%, 100% {
    transform: translateY(0px) translateZ(0);
  }
  50% {
    transform: translateY(-15px) translateZ(0);
  }
}

.multiplayer-header__title {
  font-size: 2rem;
  font-weight: 700;
  margin: 0;
  color: #f5f5dc;
  text-align: center;
}

@media (max-width: 768px) {
  .multiplayer-header__title {
    font-size: 1.75rem;
  }
}

/* Grid pour les boutons secondaires */
.home-secondary-btns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
}

/* iPhone SE & petits écrans */
@media (max-width: 375px) {
  .home-secondary-btns {
    gap: 0.375rem;
  }
}

/* Boutons secondaires Tuto et Stats */
.home-secondary-btn {
  background: rgba(51, 65, 85, 0.7);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: none;
  border-radius: 0.75rem;
  padding: 0.75rem 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  color: white;
  font-size: 0.875rem;
  cursor: pointer;
  transition: all 0.3s ease;
}

/* Tablets */
@media (max-width: 768px) and (min-width: 376px) {
  .home-secondary-btn {
    padding: 0.625rem 0.875rem;
    font-size: 0.8125rem;
  }
}

/* iPhone SE & petits écrans */
@media (max-width: 375px) {
  .home-secondary-btn {
    padding: 0.5rem 0.75rem;
    gap: 0.375rem;
    font-size: 0.75rem;
  }
}

.home-secondary-btn svg {
  stroke: white;
  flex-shrink: 0;
}

/* iPhone SE & petits écrans - réduire icônes */
@media (max-width: 375px) {
  .home-secondary-btn svg {
    width: 16px;
    height: 16px;
  }
}

@media (hover: hover) and (pointer: fine) {
  .home-secondary-btn:hover {
    background: rgba(51, 65, 85, 0.9);
    transform: translateY(-1px);
  }
}

.home-secondary-btn:active {
  transform: translateY(0) scale(0.98);
}

/* Screens */
.screen {
  display: none;
}

.screen--active {
  display: block;
}

/* Setup Screen */
.setup__modes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--spacing-md);
  margin: var(--spacing-lg) 0;
}

.screen--setup {
  background: linear-gradient(to bottom, #0d1821 0%, #1a2332 100%);
  position: relative;
  overflow: hidden;
  min-height: 100vh;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}

/* Desktop - centré verticalement */
@media (min-width: 769px) {
  .screen--setup {
    justify-content: center;
  }
}

/* Mobile & Tablets - optimiser l'espace vertical */
@media (max-width: 768px) {
  .screen--setup {
    justify-content: space-between;
    padding-top: max(0.5rem, var(--safe-area-top));
    padding-bottom: max(0.5rem, var(--safe-area-bottom));
  }
}

/* iPhone SE & petits écrans - ultra compact */
@media (max-width: 375px) and (max-height: 667px) {
  .screen--setup {
    justify-content: flex-start;
    gap: 0.5rem;
  }
}

/* FRISE DECORATIVE DE L'ACCUEIL — action A7.
   Elle utilisait les caracteres Unicode ⚀ ⚁ ⚂ ⚃ ⚄ ⚅. Meme famille de risque que
   les emojis proscrits par le projet : ces glyphes viennent de la police systeme,
   qui n'est pas la meme sous Windows, Android et iOS — et qui peut ne pas les
   avoir du tout. Une WebView Android ancienne aurait affiche six rectangles vides.

   Remplaces par les traces Lucide dice-1 a dice-6 (lucide-static v1.28.0, ISC),
   les memes que ceux de lib/icons.js, en data-URI.

   ⚠️ POURQUOI UN MASQUE ET NON UNE IMAGE DE FOND. La couleur de la frise suit le
      theme : `color` vaut #F5F5DC en sombre et rgba(0,0,0,0.15) en clair (voir la
      surcharge `body.light-mode .screen--setup::before`). Un data-URI ne connait
      pas `currentColor`. Le SVG sert donc de MASQUE et c'est
      `background-color: currentColor` qui porte la couleur : les deux surcharges
      de theme continuent de fonctionner sans y toucher.
      Les traits sont en blanc opaque, ce qui donne le meme resultat que le masque
      soit interprete en alpha ou en luminance.

   La boite reprend les proportions de l'ancienne frise : 6 des de 24 unites
   espaces de 12, soit un rapport 204:24. A 9rem de haut, elle fait 1224 px de
   large — l'ancienne en mesurait 1397 pour une hauteur d'encre comparable. Elle
   deborde de l'ecran et se fait rogner, exactement comme avant. */
.screen--setup::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 76.5rem;
  height: 9rem;
  opacity: 0.03;
  background-color: currentColor;
  -webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 204 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><g transform='translate(0 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M12 12h.01'/></g><g transform='translate(36 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M15 9h.01'/><path d='M9 15h.01'/></g><g transform='translate(72 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M16 8h.01'/><path d='M12 12h.01'/><path d='M8 16h.01'/></g><g transform='translate(108 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M16 8h.01'/><path d='M8 8h.01'/><path d='M8 16h.01'/><path d='M16 16h.01'/></g><g transform='translate(144 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M16 8h.01'/><path d='M8 8h.01'/><path d='M8 16h.01'/><path d='M16 16h.01'/><path d='M12 12h.01'/></g><g transform='translate(180 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M16 8h.01'/><path d='M16 12h.01'/><path d='M16 16h.01'/><path d='M8 8h.01'/><path d='M8 12h.01'/><path d='M8 16h.01'/></g></svg>");
          mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 204 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><g transform='translate(0 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M12 12h.01'/></g><g transform='translate(36 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M15 9h.01'/><path d='M9 15h.01'/></g><g transform='translate(72 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M16 8h.01'/><path d='M12 12h.01'/><path d='M8 16h.01'/></g><g transform='translate(108 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M16 8h.01'/><path d='M8 8h.01'/><path d='M8 16h.01'/><path d='M16 16h.01'/></g><g transform='translate(144 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M16 8h.01'/><path d='M8 8h.01'/><path d='M8 16h.01'/><path d='M16 16h.01'/><path d='M12 12h.01'/></g><g transform='translate(180 0)'><rect width='18' height='18' x='3' y='3' rx='2' ry='2'/><path d='M16 8h.01'/><path d='M16 12h.01'/><path d='M16 16h.01'/><path d='M8 8h.01'/><path d='M8 12h.01'/><path d='M8 16h.01'/></g></svg>");
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
  /* Renommee : `diceFloat` est aussi definie l.7000 pour .welcome-screen__icon,
     et la derniere definition du document gagne partout. Cette frise recuperait
     donc une version SANS translate(-50%, -50%) — mesure : matrix(0.996, 0.087,
     -0.087, 0.996, 0, -9.96), soit une rotation sans le centrage — et se
     retrouvait decalee de la moitie de sa propre taille. */
  animation: setupFriseFlottante 20s infinite ease-in-out;
  pointer-events: none;
}

@keyframes setupFriseFlottante {
  0%, 100% { 
    transform: translate(-50%, -50%) rotate(0deg); 
    opacity: 0.03;
  }
  50% { 
    transform: translate(-50%, -52%) rotate(5deg); 
    opacity: 0.05;
  }
}

/* Désactiver le scroll sur écran d'ordinateur pour l'écran d'accueil */
@media (min-width: 1025px) {
  body:has(.screen--setup.screen--active) .container,
  .container:has(.screen--setup.screen--active) {
    overflow: hidden;
  }
}

.setup__mode-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-sm);
  padding: var(--spacing-lg);
  background: var(--color-bg-light);
  border: 2px solid transparent;
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  color: var(--color-text);
  position: relative;
  overflow: hidden;
}

.setup__mode-btn::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.1),
    transparent
  );
  transition: left 0.5s;
}

.setup__mode-btn:hover {
  border-color: var(--color-primary);
  transform: translateY(-4px) scale(1.02);
  box-shadow: 
    0 8px 24px rgba(37, 99, 235, 0.3),
    0 0 40px rgba(37, 99, 235, 0.1);
}

.setup__mode-btn:hover::before {
  left: 100%;
}

.setup__mode-btn:active {
  transform: translateY(-2px) scale(0.98);
}

.setup__mode-icon {
  font-size: 3rem;
}

.setup__mode-title {
  font-size: 1.125rem;
  font-weight: 600;
}

.setup__mode-desc {
  font-size: 0.875rem;
  color: var(--color-text-dim);
}

.setup__config {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-md);
  margin-top: var(--spacing-lg);
}

.setup__config label {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
}

.setup__config input,
.setup__config select {
  padding: var(--spacing-sm);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-bg-light);
  background: var(--color-bg-light);
  color: var(--color-text);
  font-size: 1rem;
}

/* Game Screen */
.game__info {
  background: var(--color-bg-light);
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--radius-lg);
  margin-bottom: var(--spacing-sm);
}

/* ✅ AMÉLIORATION 1 : Phase indicator centré */
/* ========== BADGE COMPACT - INDICATEUR DE PHASE ========== */
.game__phase-indicator {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--spacing-sm);
}

.phase-badge-compact {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 1.25rem;
  background: linear-gradient(135deg, rgba(212, 165, 116, 0.2) 0%, rgba(13, 24, 33, 0.5) 100%);
  border: 2px solid var(--color-secondary);
  border-radius: 50px;
  box-shadow: 0 4px 16px rgba(212, 165, 116, 0.3),
              inset 0 1px 0 rgba(255, 255, 255, 0.1);
  position: relative;
  overflow: hidden;
  transition: all 0.3s ease;
}

/* Phase CHARGE - Couleurs chaudes (bronze/gold) */
.phase-badge-compact--charge {
  background: linear-gradient(135deg, rgba(212, 165, 116, 0.2) 0%, rgba(13, 24, 33, 0.5) 100%);
  border-color: var(--color-secondary);
  box-shadow: 0 4px 16px rgba(212, 165, 116, 0.3),
              inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.phase-badge-compact--charge::before {
  background: linear-gradient(90deg, transparent, rgba(212, 165, 116, 0.25), transparent);
}

/* Phase DÉCHARGE - Couleurs froides (vert success) */
.phase-badge-compact--decharge {
  background: linear-gradient(135deg, rgba(93, 162, 131, 0.2) 0%, rgba(13, 24, 33, 0.5) 100%);
  border-color: var(--color-success);
  box-shadow: 0 4px 16px rgba(93, 162, 131, 0.3),
              inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.phase-badge-compact--decharge::before {
  background: linear-gradient(90deg, transparent, rgba(93, 162, 131, 0.25), transparent);
}

/* Animation de fond shimmer */
.phase-badge-compact::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(168, 144, 128, 0.2), transparent);
  /* Renommee : `shimmer` est definie trois fois (1097, ici, 7356) et c'est celle
     de 7356 qui gagnait — un balayage en transform: rotate(45deg) translateX()
     prevu pour .btn--glowing::before. Ce badge-ci balaie par `left`, pas par
     transform : son reflet restait donc gare a left: -100%, hors du badge, et
     ne traversait jamais. Mesure avant correction : left calcule = -100%. */
  animation: badgePhaseBalayage 3s infinite;
}

@keyframes badgePhaseBalayage {
  0% { left: -100%; }
  100% { left: 100%; }
}

.phase-badge-compact__indicator {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--color-secondary);
  box-shadow: 0 0 10px var(--color-secondary), 0 0 20px rgba(212, 165, 116, 0.5);
  animation: pulseIndicator 2s ease-in-out infinite;
  position: relative;
  z-index: 1;
  transition: all 0.3s ease;
}

.phase-badge-compact__indicator--decharge {
  background: var(--color-success);
  box-shadow: 0 0 10px var(--color-success), 0 0 20px rgba(93, 162, 131, 0.5);
}

@keyframes pulseIndicator {
  0%, 100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.05);
    opacity: 0.8;
  }
}

/* Icône Lucide (remplace l'emoji) */
.phase-badge-compact__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #E8D5C4;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
  position: relative;
  z-index: 1;
  transition: color 0.3s ease;
}

.phase-badge-compact--charge .phase-badge-compact__icon {
  color: #E8D5C4;
}

.phase-badge-compact--decharge .phase-badge-compact__icon {
  color: #a8d5c2;
}

.phase-badge-compact__icon .lucide-icon {
  width: 20px;
  height: 20px;
}

/* Ancien emoji - masqué si présent */
.phase-badge-compact__emoji {
  display: none;
}

.phase-badge-compact__text {
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 1px;
  color: #E8D5C4;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
  position: relative;
  z-index: 1;
  transition: color 0.3s ease;
}

.phase-badge-compact--charge .phase-badge-compact__text {
  color: #E8D5C4;
}

.phase-badge-compact--decharge .phase-badge-compact__text {
  color: #a8d5c2;
}

.phase-badge-compact__progress {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  padding: 0.2rem 0.5rem;
  background: rgba(0, 0, 0, 0.3);
  border-radius: 12px;
  font-size: 0.75rem;
  font-weight: 700;
  position: relative;
  z-index: 1;
}

.phase-badge-compact__step {
  color: #E8D5C4;
  font-size: 0.9rem;
  transition: color 0.3s ease;
}

.phase-badge-compact--charge .phase-badge-compact__step {
  color: #E8D5C4;
}

.phase-badge-compact--decharge .phase-badge-compact__step {
  color: #a8d5c2;
}

.phase-badge-compact__separator {
  /* 0.4 donnait 3,57:1 au pire point du degrade du badge, pour un seuil WCAG AA
     de 4,5 (texte de 12 px, donc « petit texte »). 0.55 donne 5,38:1 au pire et
     6,16:1 au mieux, tout en restant sous le 0.6 de `__total` : la hierarchie
     visuelle — le « / » plus discret que les chiffres — est preservee. */
  color: rgba(255, 255, 255, 0.55);
}

.phase-badge-compact__total {
  color: rgba(255, 255, 255, 0.6);
}

/* ========== NOUVEAU : Sous-texte explicatif ========== */
.phase-badge-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 6px 14px;
  margin-top: 8px;
  background: rgba(0, 0, 0, 0.2);
  border-radius: 20px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  transition: all 0.3s ease;
}

.phase-badge-hint__icon {
  opacity: 0.7;
  color: var(--color-secondary);
  transition: color 0.3s ease;
}

.phase-badge-hint--decharge .phase-badge-hint__icon {
  color: var(--color-success);
}

.phase-badge-hint__text {
  font-size: 0.8rem;
  font-weight: 500;
  color: rgba(245, 245, 220, 0.7);
}

/* Animation de transition entre phases */
@keyframes badgeTransition {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.08);
    border-color: white;
  }
  100% {
    transform: scale(1);
  }
}

.phase-badge-compact--transitioning {
  animation: badgeTransition 0.6s ease;
}

/* Responsive pour petits écrans */
@media (max-width: 400px) {
  .phase-badge-compact {
    gap: 0.6rem;
    padding: 0.5rem 1rem;
  }
  
  .phase-badge-compact__icon .lucide-icon {
    width: 18px;
    height: 18px;
  }
  
  .phase-badge-compact__text {
    font-size: 0.8rem;
  }
  
  .phase-badge-compact__progress {
    font-size: 0.7rem;
  }
  
  .phase-badge-hint {
    padding: 5px 12px;
    margin-top: 6px;
  }
  
  .phase-badge-hint__text {
    font-size: 0.85rem;
  }
}

/* Anciens styles gardés pour compatibilité (mode badge non utilisé) */
.mode-badge {
  display: none !important;
}

.mode-badge--competitive {
  display: none !important;
}

.game__pot {
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  margin-bottom: var(--spacing-md);
}

.game__pot-bar {
  flex: 1;
  height: 1rem;
  background: rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.game__pot-fill {
  height: 100%;
  /* P8 (12 août 2026) : le pot était le SEUL bleu de l'écran de jeu — il se
     lisait comme un chargement système, pas comme des jetons. Le dégradé
     mocha → bronze relie la barre aux jetons, bronze partout ailleurs.
     Le bleu (--color-primary) est rendu au chrome : en-tête et navigation. */
  background: linear-gradient(90deg, var(--color-mocha-plein, #8a705e) 0%, var(--color-bronze, #d4a574) 100%);
  transition: width 0.3s;
}

/* En arabe la barre grandit de droite à gauche : le bout clair doit rester du
   côté qui avance. Miroir du dégradé, rien d'autre (piège n° 44 : un glyphe
   directionnel — ici un dégradé — se juge dans les deux sens). */
[dir="rtl"] .game__pot-fill {
  background: linear-gradient(270deg, var(--color-mocha-plein, #8a705e) 0%, var(--color-bronze, #d4a574) 100%);
}

/* Le thème clair a SES deux valeurs (piège n° 46 : jamais une seule valeur
   « qui passera partout »). Mesuré sur le fond réel de `.game__info` (#ffffff) :
   le bronze #d4a574 tombe à 1,96:1 — sous le seuil graphique de 3:1 — alors
   que mocha-dark 6,3:1 et mocha-plein 4,63:1 passent tous deux. Le dégradé
   reste sombre → clair, la pointe reste le bout qui avance.
   ⚠️ Ces deux règles battent les deux du dessus à spécificité (0,2,1) contre
   (0,1,1) et (0,2,0) : l'ordre n'y change rien, le couple clair+RTL doit
   exister EXPLICITEMENT, sinon le clair gagnerait sur le miroir arabe. */
body.light-mode .game__pot-fill {
  background: linear-gradient(90deg, var(--color-mocha-dark, #6B5D53) 0%, var(--color-mocha-plein, #8a705e) 100%);
}

[dir="rtl"] body.light-mode .game__pot-fill {
  background: linear-gradient(270deg, var(--color-mocha-dark, #6B5D53) 0%, var(--color-mocha-plein, #8a705e) 100%);
}

/* Et la PISTE : rgba(255,255,255,.1) est invisible sur le blanc du panneau —
   le joueur ne voyait pas la part vide du pot en clair (défaut antérieur au
   P8, la proposition prévoit rgba(0,0,0,.07)). */
body.light-mode .game__pot-bar {
  background: rgba(0, 0, 0, 0.07);
}

/* ==================== ZONE JOUEURS - VERSIONS HYBRIDES A + D ==================== */
/**
 * Version A (Solo - 2 joueurs) : Compact avec avatars SVG colorés
 * Version D (Multijoueur - 3-6 joueurs) : Premium avec 6 couleurs uniques
 * Switch automatique selon le nombre de joueurs
 * Ultra-optimisé pour mobile
 */

.game__players {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
}

/* ==================== VERSION A : SOLO (2 JOUEURS) ==================== */

.player-solo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: rgba(255, 255, 255, 0.04);
  backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 12px;
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
}

/* ═══════════════════════════════════════════════════════════════════════════
   L'ÉTAT « c'est son tour » NE PORTE PLUS LA COULEUR D'IDENTITÉ DU JOUEUR.

   Avant : `border-color: var(--player-color)` et un anneau de 2 px de la même
   couleur. Le joueur d'index 0 étant `#e74c3c`, le joueur local voyait SA
   PROPRE carte cerclée de rouge vif au moment où c'était à lui de jouer —
   mesuré `rgb(231,76,60)` sur `.player-solo--active`. Un rouge vif au moment
   où l'on n'a rien fait de mal se lit comme une erreur.

   Une couleur ne peut pas dire à la fois « c'est toi » et « c'est cassé » :
   l'état passe donc sur l'accent mocha — le MÊME pour tous les joueurs, donc
   apprenable — et l'identité reste dans l'avatar et dans le filet ci-dessous.

   ⚠️ `.player-multi--active` (3 à 6 joueurs) faisait DÉJÀ cela : c'est le
   seul cas solo/2 joueurs qui empruntait la couleur d'identité. La
   proposition P2 décrivait « six couleurs à six joueurs » — c'est faux, et
   elle visait `.player-card--active`, qui n'est référencé nulle part.
   ═══════════════════════════════════════════════════════════════════════════ */
.player-solo--active {
  background: rgba(196, 181, 160, 0.15);
  border-color: rgba(196, 181, 160, 0.5);
  box-shadow:
    0 4px 20px rgba(196, 181, 160, 0.25),
    0 0 0 2px rgba(196, 181, 160, 0.4),
    inset 0 0 20px rgba(196, 181, 160, 0.1);
  animation: playerActivePulse 2s ease-in-out infinite;
  transform: scale(1.02);
}

/* Le filet d'identité : 4 px de la couleur du joueur, au bord de départ.
   `inset-inline-start` et pas `left` — en arabe le filet doit passer à droite
   tout seul. `.player-solo` porte déjà `position: relative` et
   `overflow: hidden`, donc le filet épouse le rayon de la carte. */
.player-solo--active::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  bottom: 0;
  width: 4px;
  background: var(--player-color, rgba(196, 181, 160, 0.5));
  pointer-events: none;
}

@keyframes playerActivePulse {
  0%, 100% {
    box-shadow:
      0 4px 20px rgba(196, 181, 160, 0.25),
      0 0 0 2px rgba(196, 181, 160, 0.4),
      inset 0 0 20px rgba(196, 181, 160, 0.1);
  }
  50% {
    box-shadow:
      0 6px 25px rgba(196, 181, 160, 0.35),
      0 0 0 3px rgba(196, 181, 160, 0.6),
      inset 0 0 25px rgba(196, 181, 160, 0.15);
  }
}

.player-solo__left {
  display: flex;
  align-items: center;
  gap: 12px;
}

.player-solo__avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  flex-shrink: 0;
  position: relative;
}

.player-solo__avatar--player {
  background: linear-gradient(135deg, #3b82f6, #2563eb);
}

/* 6 couleurs uniques pour mode solo/réseau 2 joueurs - MÊME PALETTE QUE LE LOBBY */
.player-solo__avatar--color1 {
  background: linear-gradient(135deg, #e74c3c, #c0392b); /* Rouge */
}

.player-solo__avatar--color2 {
  background: linear-gradient(135deg, #3498db, #2980b9); /* Bleu */
}

.player-solo__avatar--color3 {
  background: linear-gradient(135deg, #2ecc71, #27ae60); /* Vert */
}

.player-solo__avatar--color4 {
  background: linear-gradient(135deg, #f39c12, #e67e22); /* Orange */
}

.player-solo__avatar--color5 {
  background: linear-gradient(135deg, #9b59b6, #8e44ad); /* Violet */
}

.player-solo__avatar--color6 {
  background: linear-gradient(135deg, #1abc9c, #16a085); /* Turquoise */
}

.player-solo__avatar--ai {
  background: linear-gradient(135deg, #A89080, #6B5D53);
}

/* État déconnecté pour mode solo */
.player-solo--disconnected {
  opacity: 0.5;
  filter: grayscale(60%);
  border: 2px dashed var(--color-error, #c85a54) !important;
  background: rgba(200, 90, 84, 0.1) !important;
}

.player-solo--disconnected .player-solo__avatar {
  filter: grayscale(100%);
  opacity: 0.6;
}

.player-solo--disconnected .player-solo__name {
  text-decoration: line-through;
  opacity: 0.7;
}

.player-solo__disconnected-icon {
  position: absolute;
  bottom: -4px;
  right: -4px;
  width: 16px;
  height: 16px;
  background: var(--color-error, #c85a54);
  color: white;
  border-radius: 50%;
  font-size: 11px;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
  animation: disconnectedPulse 2s ease-in-out infinite;
}

.player-solo--active .player-solo__avatar {
  animation: avatarPulse 2s ease-in-out infinite;
}

@keyframes avatarPulse {
  0%, 100% { 
    transform: scale(1);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  }
  50% { 
    transform: scale(1.08);
    box-shadow: 0 4px 16px rgba(196, 181, 160, 0.5);
  }
}

.player-solo__avatar svg {
  width: 20px;
  height: 20px;
  stroke: white;
  fill: none;
  stroke-width: 2;
}

.player-solo__name {
  color: var(--color-cream, #f5f5dc);
  font-weight: 600;
  font-size: 0.95rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 190px;
  flex: 1;
  min-width: 0;
}

.player-solo__right {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}

.player-solo__tokens {
  display: flex;
  align-items: center;
  gap: 6px;
  background: rgba(0, 0, 0, 0.3);
  padding: 6px 12px;
  border-radius: 10px;
  color: #C4B5A0;
  font-weight: 700;
  font-size: 1rem;
}

.player-solo__mini-bar {
  width: 50px;
  height: 4px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 2px;
  overflow: hidden;
}

.player-solo__mini-fill {
  height: 100%;
  background: linear-gradient(90deg, #C4B5A0, #f4d03f);
  border-radius: 2px;
  transition: width 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* ==================== VERSION D : MULTIJOUEUR (3-6 JOUEURS) ==================== */

.player-multi {
  background: linear-gradient(135deg, 
    rgba(255, 255, 255, 0.06),
    rgba(255, 255, 255, 0.03)
  );
  backdrop-filter: blur(15px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 12px;
  padding: 12px 16px;
  transition: all 0.3s ease;
  position: relative;
}

.player-multi--active {
  border-color: rgba(196, 181, 160, 0.5);
  box-shadow: 0 0 25px rgba(196, 181, 160, 0.3);
  transform: translateY(-2px);
}

.player-multi__content {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.player-multi__left {
  display: flex;
  align-items: center;
  gap: 10px;
}

.player-multi__avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
  flex-shrink: 0;
  position: relative;
}

/* 6 couleurs uniques pour multijoueur - MÊME PALETTE QUE LE LOBBY */
.player-multi__avatar--color1 {
  background: linear-gradient(135deg, #e74c3c, #c0392b); /* Rouge */
}

.player-multi__avatar--color2 {
  background: linear-gradient(135deg, #3498db, #2980b9); /* Bleu */
}

.player-multi__avatar--color3 {
  background: linear-gradient(135deg, #2ecc71, #27ae60); /* Vert */
}

.player-multi__avatar--color4 {
  background: linear-gradient(135deg, #f39c12, #e67e22); /* Orange */
}

.player-multi__avatar--color5 {
  background: linear-gradient(135deg, #9b59b6, #8e44ad); /* Violet */
}

.player-multi__avatar--color6 {
  background: linear-gradient(135deg, #1abc9c, #16a085); /* Turquoise */
}

.player-multi__avatar--ai {
  background: linear-gradient(135deg, #A89080, #6B5D53);
}

.player-multi--active .player-multi__avatar {
  animation: avatarGlow 2s ease-in-out infinite;
}

@keyframes avatarGlow {
  0%, 100% { 
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
  }
  50% { 
    box-shadow: 0 0 20px rgba(196, 181, 160, 0.6);
  }
}

.player-multi__avatar svg {
  width: 20px;
  height: 20px;
  stroke: white;
  fill: none;
  stroke-width: 2;
}

.player-multi__name {
  color: var(--color-cream, #f5f5dc);
  font-weight: 600;
  font-size: 0.9rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 140px;
  flex: 1;
  min-width: 0;
}

.player-multi__right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.player-multi__tokens {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--color-cream, #f5f5dc);
  font-weight: 700;
  font-size: 1rem;
}

.player-multi__minibar {
  width: 45px;
  height: 4px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 2px;
  overflow: hidden;
}

.player-multi__minibar-fill {
  height: 100%;
  background: linear-gradient(90deg, #C4B5A0, #f4d03f);
  border-radius: 2px;
  transition: width 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
  box-shadow: 0 0 8px rgba(196, 181, 160, 0.4);
}

/* ==================== PLAYER OUT (Éliminé) ==================== */

.player-solo--out,
.player-multi--out {
  opacity: 0.5;
  background: rgba(255, 255, 255, 0.02);
  pointer-events: none;
}

.player-solo--out .player-solo__avatar,
.player-multi--out .player-multi__avatar {
  filter: grayscale(1);
  opacity: 0.6;
}

/* ==================== ANIMATIONS JETONS ==================== */

@keyframes tokenLost {
  0%, 100% { transform: scale(1); }
  25% { transform: scale(1.15) rotate(-5deg); }
  50% { transform: scale(0.95) rotate(5deg); }
  75% { transform: scale(1.05) rotate(-2deg); }
}

.player-solo__tokens--lost,
.player-multi__tokens--lost {
  animation: tokenLost 0.5s ease-out;
}

@keyframes tokenGained {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.2); }
}

.player-solo__tokens--gained,
.player-multi__tokens--gained {
  animation: tokenGained 0.4s ease-out;
}

/* ==================== ANCIENS STYLES (Rétro-compatibilité) ==================== */

.player-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--spacing-xs) var(--spacing-sm);
  border-radius: var(--radius-sm);
  transition: background 0.2s;
}

.player-item--active {
  background: rgba(37, 99, 235, 0.2);
  font-weight: 600;
}

.player-tokens {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-weight: 700;
  font-size: 0.95rem;
  transition: all 0.3s ease;
}

.player-item--active .player-tokens {
  color: #3b82f6;
}

.player-tokens__icon {
  font-size: 1.1rem;
}

.player-tokens__value {
  font-variant-numeric: tabular-nums;
}

/* ==================== NIVEAU IA - DOTS ==================== */

.ai-level-dots {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 6px;
}

.ai-level-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.2);
  transition: all 0.3s ease;
}

.ai-level-dot--active {
  background: #C4B5A0;
  box-shadow: 0 0 6px rgba(196, 181, 160, 0.4);
}

/* Animation subtile au survol */
.player-solo:hover .ai-level-dot--active,
.player-multi:hover .ai-level-dot--active {
  background: #f4d03f;
  box-shadow: 0 0 8px rgba(244, 208, 63, 0.6);
}

/* ==================== RESPONSIVE MOBILE ==================== */

@media (max-width: 768px) {
  /* Version Solo - Cacher la barre sur très petits écrans */
  .player-solo__mini-bar {
    display: none;
  }
  
  .player-solo__name {
    max-width: 100px;
    font-size: 0.95rem;
  }
  
  .player-solo__tokens {
    padding: 5px 10px;
    font-size: 0.95rem;
  }
  
  /* Version Multi - Optimisations */
  .player-multi__minibar {
    width: 40px;
  }
  
  .player-multi__name {
    max-width: 90px;
    font-size: 0.9rem;
  }
  
  .player-multi__tokens {
    font-size: 0.95rem;
  }
  
  .player-multi__avatar,
  .player-solo__avatar {
    width: 32px;
    height: 32px;
  }
  
  .player-multi__avatar svg,
  .player-solo__avatar svg {
    width: 18px;
    height: 18px;
  }
}

@media (max-width: 480px) {
  /* Ultra-compact pour petits téléphones */
  .game__players {
    gap: 8px;
  }
  
  .player-solo,
  .player-multi {
    padding: 10px 12px;
  }
  
  .player-solo__left,
  .player-multi__left {
    gap: 8px;
  }
  
  .player-solo__name,
  .player-multi__name {
    font-size: 0.85rem;
    max-width: 80px;
  }
  
  .player-multi__minibar {
    display: none;
  }
  
  .player-solo__tokens,
  .player-multi__tokens {
    gap: 4px;
    padding: 4px 8px;
    font-size: 0.9rem;
  }
}

/* Dice */
.game__dice-area {
  margin: var(--spacing-xs) 0 var(--spacing-sm) 0;
}

.dice {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--spacing-sm);
  margin-bottom: 0;
  /* ✅ Perspective 3D pour l'animation des dés */
  perspective: 1000px;
  perspective-origin: center center;
  /* ✅ AMÉLIORATION: Padding pour éviter les clics trop près des bords */
  padding: 0.25rem;
}

.die {
  aspect-ratio: 1;
  background: #ffffff !important;
  border: 2px solid rgba(13, 24, 33, 0.12);
  border-radius: var(--radius-lg);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3rem;
  font-weight: 700;
  color: #0d1821 !important;
  transition: background 0.15s, border 0.15s, box-shadow 0.15s, opacity 0.15s;
  min-width: 80px;
  min-height: 80px;
  box-shadow: var(--shadow-md);
  position: relative;
  transform-style: preserve-3d;
  
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  
  pointer-events: auto !important;
}

/* ✅ NOUVEAU: ZONE DE CLIC ÉTENDUE - Augmente la zone tactile sans changer l'apparence */
.die::before {
  content: '';
  position: absolute;
  inset: -12px; /* Étend la zone de 12px dans toutes les directions */
  background: transparent;
  border-radius: calc(var(--radius-lg) + 8px);
  /* Debug: décommenter pour voir la zone étendue
  background: rgba(255, 0, 0, 0.1);
  */
}

.die__value {
  pointer-events: none;
  user-select: none;
  position: relative;
  z-index: 2;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
  font-weight: 700;
  color: #0d1821 !important;
}

/* ✅ Hover pour desktop uniquement */
@media (hover: hover) and (pointer: fine) {
  .die:hover:not(.die--kept) {
    transform: scale(1.05);
  }
}

/* ✅ NOUVEAU: FEEDBACK TACTILE IMMÉDIAT avec la classe .touching */
.die.touching:not(.die--kept) {
  transform: scale(0.95) !important;
  filter: brightness(1.1) !important;
  box-shadow: var(--shadow-lg) !important;
  border: 2px solid var(--color-mocha-light) !important;
  transition: all 0.05s ease !important;
}

.die:focus {
  outline: none; /* On utilise un style personnalisé */
}

.die:focus-visible {
  outline: 3px solid var(--color-primary);
  outline-offset: 4px;
  box-shadow: var(--shadow-md);
}

/* ==================== DÉ BLOQUÉ - STYLE SIMPLE MOCHA ==================== */

.die--kept {
  /* Bordure Mocha clair épaisse - BIEN VISIBLE */
  border: 4px solid #C4B5A0 !important;
  background: #ffffff !important;
  
  /* Glow Mocha intense pour bien montrer que c'est bloqué */
  box-shadow: 
    0 0 35px rgba(196, 181, 160, 0.8),
    0 8px 32px rgba(168, 144, 128, 0.6),
    inset 0 0 25px rgba(196, 181, 160, 0.2) !important;
  
  transform: translateY(-8px) scale(1.05);
  position: relative;
  z-index: 10;
  /* Pas de `cursor: not-allowed` ici : un de garde SE RECLIQUE pour etre
     relache — `toggleKeep()` (state.js:131) inverse la valeur. Cette
     declaration existait, mais le doublon `.die` retire en A8 la masquait :
     le curseur affiche etait donc `pointer`, et c'est le bon. La retirer
     preserve le rendu actuel au lieu de faire ressurgir une intention fausse. */
}

/* ✅ Pas de feedback tactile sur dé gardé */
.die--kept.touching,
.die--kept:active {
  transform: translateY(-8px) scale(1.05) !important;
}

.die--rolling {
  /* La secousse `diceWobble` n'est plus souhaitee — decision du proprietaire.
     Seule la ligne `animation` est retiree ici : le reste de cette regle n'est pas
     decoratif. pointer-events: none empeche de cliquer un de en plein lancer, et
     transition: none evite qu'une transition en cours ne se superpose au tirage.
     Les retirer rouvrirait un vrai defaut d'interaction.
     Pour retablir la secousse : remettre la ligne ci-dessous.
       animation: diceWobble 0.6s ease-in-out !important;
     Les @keyframes diceWobble restent definies juste en dessous.

     ⚠️ Le de n'est PAS immobile pendant le lancer pour autant : une autre
     animation, `deLancerEnCours`, est posee sur cette meme classe en fin de
     fichier. Elle est verticale et non laterale, et elle enchaine sans rupture
     sur l'atterrissage. Voir le bloc correspondant. */
  transform-style: preserve-3d !important;
  /* ✅ AMÉLIORATION: Désactiver les interactions pendant l'animation */
  pointer-events: none !important;
  /* ✅ FIX: Désactiver complètement les transitions pendant l'animation */
  transition: none !important;
}

@keyframes diceWobble {
  0%, 100% {
    transform: translateX(0%) rotateZ(0deg);
  }
  15% {
    transform: translateX(-25px) rotateZ(-10deg);
  }
  30% {
    transform: translateX(20px) rotateZ(8deg);
  }
  45% {
    transform: translateX(-15px) rotateZ(-6deg);
  }
  60% {
    transform: translateX(10px) rotateZ(4deg);
  }
  75% {
    transform: translateX(-5px) rotateZ(-2deg);
  }
}

/* ✅ Optimisation petits écrans - Réduire taille des dés et espacements */
@media (max-width: 400px) {
  .game__dice-area {
    margin: 0.25rem 0 0.5rem 0;
  }
  
  .dice {
    gap: 0.5rem;
    padding: 0.125rem;
  }
  
  .die {
    min-width: 70px;
    min-height: 70px;
    font-size: 2.5rem;
  }
  
  .game__info {
    padding: 0.75rem 1rem;
  }
  
  .game__phase-indicator {
    margin-bottom: 0.5rem;
  }
  
  .phase-badge-hint {
    margin-top: 6px;
    padding: 4px 10px;
  }
  
  .phase-badge-hint__text {
    font-size: 0.85rem;
  }
}

/* ✅ Ultra-compact pour très petits écrans (iPhone SE, etc.) */
@media (max-height: 700px) {
  .game__info {
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.25rem;
  }
  
  .game__phase-indicator {
    margin-bottom: 0.35rem;
  }
  
  .phase-badge-compact {
    padding: 0.4rem 1rem;
    gap: 0.5rem;
  }
  
  .phase-badge-compact__text {
    font-size: 0.8rem;
  }
  
  .phase-badge-compact__icon .lucide-icon {
    width: 16px;
    height: 16px;
  }
  
  .phase-badge-hint {
    margin-top: 4px;
    padding: 3px 8px;
  }
  
  .phase-badge-hint__text {
    font-size: 0.8rem;
  }
  
  .game__players {
    gap: 6px;
  }
  
  .player-solo,
  .player-multi {
    padding: 8px 10px;
  }
  
  .game__dice-area {
    margin: 0.125rem 0 0.25rem 0;
  }
  
  .dice {
    gap: 0.375rem;
    padding: 0;
  }
  
  .die {
    min-width: 65px;
    min-height: 65px;
    font-size: 2.25rem;
    border-radius: 12px;
  }
  
  .game__actions {
    gap: 0.5rem;
  }
  
  .btn--large {
    padding: 0.6rem var(--spacing-md);
    font-size: 1rem;
  }
}

/* Buttons */
.game__actions {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
}

.btn {
  padding: var(--spacing-sm) var(--spacing-md);
  border-radius: var(--radius-md);
  border: none;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
  font-size: 1rem;
}

.btn--primary {
  background: var(--color-primary);
  color: white;
}

.btn--primary:hover {
  /* P8 (12 août 2026) : le survol sautait du bleu chrome (#1a5a8a) à un bleu
     Tailwind #1d4ed8 en dur — hors famille. On éclaircit DANS la famille.
     Blanc sur #2d7ab8 : 4,59:1, calculé puis vérifié à la sonde.
     ⚠️ Ne vaut que pour le thème SOMBRE : en clair, la règle gagnante est
     `body.light-mode .btn--primary` (fin de fichier, correctif vague 29). */
  background: var(--color-primary-light, #2d7ab8);
}

.btn--secondary {
  background: var(--color-bg-light);
  color: var(--color-text);
}

.btn--secondary:hover {
  background: #475569;
}

.btn--success {
  background: var(--color-success);
  color: white;
}

.btn--full-width {
  width: 100%;
  text-align: center;
}

.btn--success:hover {
  background: #059669;
}

.btn--large {
  padding: 0.75rem var(--spacing-lg); /* Réduit de 1rem à 0.75rem verticalement */
  font-size: 1.125rem;
}

.btn:disabled,
.btn[data-enabled="false"] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* End Screen */
.screen--end {
  text-align: center;
  padding: var(--spacing-xl);
}

.end__winner {
  font-size: 1.5rem;
  margin: var(--spacing-lg) 0;
}

.end__ranking {
  background: var(--color-bg-light);
  padding: var(--spacing-md);
  border-radius: var(--radius-lg);
  margin: var(--spacing-lg) 0;
}

.end__ranking-item {
  display: flex;
  justify-content: space-between;
  padding: var(--spacing-xs);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.end__ranking-item:last-child {
  border-bottom: none;
}

.end__actions {
  display: flex;
  gap: var(--spacing-md);
  justify-content: center;
}

/* Modal */
.modal {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 100;
}

.modal__overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.12);
}

.modal__content {
  position: relative;
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  max-width: 90%;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
}

.modal__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--spacing-md);
  border-bottom: 1px solid var(--color-bg-light);
}

.modal__close {
  background: none;
  border: none;
  font-size: 1.5rem;
  cursor: pointer;
  color: var(--color-text);
  padding: var(--spacing-xs);
}

.modal__body {
  padding: var(--spacing-md);
}

.modal__footer {
  padding: var(--spacing-md);
  border-top: 1px solid var(--color-bg-light);
  display: flex;
  gap: var(--spacing-sm);
  justify-content: flex-end;
}

/* Modal compact pour confirmations */
.modal__content--small {
  width: min(400px, 90vw);
  text-align: center;
}

.modal__content--small .modal__header {
  justify-content: center;
  border-bottom: none;
  padding-bottom: 0;
}

.modal__content--small .modal__header h2 {
  font-size: 1.5rem;
}

.modal__content--small .modal__body {
  padding: var(--spacing-md) var(--spacing-lg);
}

.modal__content--small .modal__body p {
  color: var(--color-text-muted);
  margin: 0;
}

.modal__content--small .modal__footer {
  border-top: none;
  justify-content: center;
  flex-wrap: wrap;
}

/* Confettis */
.confetti {
  position: fixed;
  width: 10px;
  height: 10px;
  top: -10px;
  z-index: 9999;
  animation: confettiFall 3s ease-in forwards;
  pointer-events: none;
}

@keyframes confettiFall {
  0% {
    transform: translateY(0) rotateZ(0deg);
    opacity: 1;
  }
  100% {
    transform: translateY(100vh) rotateZ(720deg);
    opacity: 0;
  }
}

/* Nénette Animation */
@keyframes nenetteShake {
  0%, 100% { 
    transform: translateX(0);
  }
  25% { transform: translateX(-3px); }
  50% { transform: translateX(3px); }
  75% { transform: translateX(-3px); }
}

.nenette-shake {
  animation: nenetteShake 0.4s ease-in-out 2;
  background: linear-gradient(135deg, #ffcccb, #ffb3b3) !important;
  border-color: #ff6b6b !important;
  box-shadow: 0 0 15px rgba(255, 107, 107, 0.5) !important;
}

/* .laughing-emoji et @keyframes laughingFall retires : la pluie d'emojis de la
   Nenette est remplacee par le mot ecrit. Voir .nenette-annonce en fin de fichier. */

/* ✅ AMÉLIORATION 3 : Score actuel sous les dés avec largeur des dés */
.current-score-display {
  margin-top: var(--spacing-sm);
  padding: 0.4rem 0.875rem;
  background: rgba(59, 130, 246, 0.15);
  border: 2px solid rgba(59, 130, 246, 0.3);
  border-radius: var(--radius-lg);
  text-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 36px;
  width: fit-content;
  margin-left: auto;
  margin-right: auto;
}

.current-score-display__name {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-text);
}

.current-score-display__value {
  font-size: 1.125rem;
  font-weight: 700;
  color: #3b82f6;
  display: flex;
  align-items: center;
  gap: 0.3rem;
}

/* Responsive pour petits écrans en hauteur */
@media (max-height: 700px) {
  .current-score-display {
    margin-top: 0.375rem;
    padding: 0.25rem 0.625rem;
    min-height: 28px;
    border-radius: 10px;
  }
  
  .current-score-display__name {
    font-size: 0.8rem;
  }
  
  .current-score-display__value {
    font-size: 0.9rem;
  }
}

/* ✅ AMÉLIORATION 3 : Simplifier l'encart des jetons (sans bordure excessive) */
.player-tokens {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-weight: 700;
  font-size: 0.95rem;
  transition: all 0.3s ease;
}

.player-item--active .player-tokens {
  color: #3b82f6;
}

.player-tokens__icon {
  font-size: 1.1rem;
}

.player-tokens__value {
  font-variant-numeric: tabular-nums;
}

/* ✅ AMÉLIORATION 4 : Masquer l'historique inline (sera dans une modal) */
.sidebar--history {
  display: none !important;
}

.history-inline {
  display: none !important;
}

/* Bouton pour ouvrir l'historique */
/* Modal historique */
.modal--history .modal__content {
  max-width: 600px;
}

.modal--history .modal__body {
  max-height: 60vh;
  overflow-y: auto;
}

/* Accessibility */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/* Main Menu */
/* ==================== MENU EN GRILLE D'ICÔNES ==================== */
.icon-menu {
  display: grid;
  grid-template-columns: repeat(2, minmax(140px, 160px));
  gap: 1rem;
  max-width: 400px;
  margin: 0 auto;
  padding: 0 var(--spacing-md);
  justify-content: center;
}

.icon-menu-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 1.5rem 1rem;
  background: rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  color: var(--color-text);
  position: relative;
  overflow: hidden;
  min-height: 140px;
  box-shadow: var(--shadow-sm);
  
  pointer-events: auto !important;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  user-select: none;
}

.icon-menu-btn::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(59, 130, 246, 0.15),
    transparent
  );
  transition: left 0.5s;
}

/* Hover uniquement desktop */
@media (hover: hover) and (pointer: fine) {
  .icon-menu-btn:hover {
    transform: translateY(-4px) scale(1.03);
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.18);
    box-shadow: var(--shadow-lg);
  }
  
  .icon-menu-btn:hover::before {
    left: 100%;
  }
}

.icon-menu-btn:active {
  transform: translateY(-2px) scale(0.98);
}

/* ✅ SPRINT 1 - FOCUS CLAVIER VISIBLE (WCAG 2.4.7) */
.icon-menu-btn:focus-visible {
  outline: 3px solid #4A90E2;
  outline-offset: 2px;
  border-radius: var(--radius-lg);
  z-index: 10;
}

button:focus-visible,
.btn:focus-visible,
.header__btn:focus-visible,
.header-menu__item:focus-visible {
  outline: 3px solid #4A90E2;
  outline-offset: 2px;
}

.icon-menu-btn__icon {
  font-size: 2.5rem;
  margin-bottom: 0.25rem;
  animation: iconPulse 2s ease-in-out infinite;
}

@keyframes iconPulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.02); }
}


.icon-menu-btn__label {
  font-size: 1rem;
  font-weight: 700;
  text-align: center;
  line-height: 1.2;
  color: var(--color-text);
}

.icon-menu-btn__desc {
  font-size: 0.75rem;
  color: #B0B8C3; /* ✅ SPRINT 1: Était #B0B8C3 (3.2:1), maintenant 4.5:1 WCAG AA */
  text-align: center;
  line-height: 1.2;
  opacity: 0.9;
}

/* Responsive */
@media (min-width: 640px) and (max-width: 1024px) {
  .icon-menu {
    grid-template-columns: repeat(2, minmax(140px, 160px));
    max-width: 400px;
  }
}

@media (min-width: 1025px) {
  .icon-menu {
    grid-template-columns: repeat(2, minmax(140px, 180px));
    max-width: 450px;
  }
}

@media (max-width: 639px) {
  .icon-menu {
    grid-template-columns: repeat(2, 1fr);
    gap: 0.75rem;
    max-width: 100%;
  }
  
  .icon-menu-btn {
    min-height: 120px;
    padding: 1rem 0.5rem;
  }
  
  .icon-menu-btn__icon {
    font-size: 2rem;
  }
  
  .icon-menu-btn__label {
    font-size: 0.875rem;
  }
  
  .icon-menu-btn__desc {
    font-size: 0.7rem;
  }
}

/* ==================== ANCIEN MENU (gardé pour compatibilité) ==================== */

/* ✅ SPRINT 1 - CTA PRIMAIRE */
.primary-cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: 100%;
  max-width: 400px;
  margin: 0 auto var(--spacing-lg) auto;
  padding: 1.5rem 2rem;
  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
  border: 2px solid var(--color-mocha-light);
  border-radius: 1.5rem;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  color: white;
  font-weight: 700;
  box-shadow: var(--shadow-md);
  position: relative;
  overflow: hidden;
  min-height: 80px;
}

.primary-cta::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.2),
    transparent
  );
  transition: left 0.5s;
}

/* Hover uniquement desktop */
@media (hover: hover) and (pointer: fine) {
  .primary-cta:hover {
    transform: translateY(-6px) scale(1.02);
    border-color: var(--color-mocha-light);
    background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
    box-shadow: 
      0 12px 32px rgba(59, 130, 246, 0.5),
      0 0 60px rgba(168, 144, 128, 0.3),
      inset 0 1px 0 rgba(255, 255, 255, 0.3);
  }
  
  .primary-cta:hover::before {
    left: 100%;
  }
}

.primary-cta:active {
  transform: translateY(-2px) scale(0.98);
}

.primary-cta:focus-visible {
  outline: 4px solid #4A90E2;
  outline-offset: 3px;
}

.primary-cta__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  animation: ctaPulse 2s ease-in-out infinite;
}

.primary-cta__icon svg {
  width: 48px;
  height: 48px;
  stroke: white;
}

@keyframes ctaPulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}

.primary-cta__label {
  font-size: 1.25rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}

.primary-cta__hint {
  font-size: 0.8rem;
  color: #C9CDD6;
  font-weight: 400;
  opacity: 0.95;
}

/* ✅ SPRINT 1 - MENU SECONDAIRE (2 tuiles) */
.icon-menu--secondary {
  margin-top: var(--spacing-lg);
}

/* ✅ SPRINT 1 - MODAL SÉLECTION MODE */
.modal__content--play-mode {
  max-width: 550px;
  background: linear-gradient(135deg, rgba(13, 24, 33, 0.98) 0%, rgba(26, 35, 50, 0.98) 100%);
  border: 2px solid var(--color-mocha-light);
  box-shadow: 
    0 20px 60px rgba(0, 0, 0, 0.15),
    0 0 40px rgba(168, 144, 128, 0.3),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.modal__header--play-mode {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.5rem;
  border-bottom: 2px solid rgba(168, 144, 128, 0.3);
  background: rgba(168, 144, 128, 0.03);
}

.play-mode-header-icon {
  width: 2.5rem;
  height: 2.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--color-mocha-light) 0%, var(--color-mocha) 50%, #d4a574 100%);
  border-radius: 12px;
  box-shadow: 
    0 4px 12px rgba(168, 144, 128, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.3);
  /* Renommee : `pulse` est definie quatre fois (1230, 3815, 6927, 10085) et c'est
     celle de 10085 qui gagnait — une simple oscillation d'opacite 0,6 -> 1.
     Cette icone-ci attend un battement d'echelle avec son ombre. Mesure avant
     correction : opacity calculee = 0.6, transform = none. */
  animation: iconeModePulse 2s ease-in-out infinite;
}

.play-mode-header-icon svg {
  width: 1.5rem;
  height: 1.5rem;
  stroke: #0d1821;
  stroke-width: 2.5;
}

.modal__header--play-mode h2 {
  flex: 1;
  margin: 0;
  font-size: 1.5rem;
  background: linear-gradient(135deg, var(--color-mocha-light) 0%, var(--color-mocha) 50%, #d4a574 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  font-weight: 700;
}

.play-mode-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  padding: 1.5rem;
}

.play-mode-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 2rem 1rem;
  background: rgba(13, 24, 33, 0.5);
  border: 2px solid rgba(212, 165, 116, 0.3);
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  color: var(--color-text);
  min-height: 180px;
  position: relative;
  overflow: hidden;
}

.play-mode-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 50%;
  background: rgba(168, 144, 128, 0.03);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.play-mode-card:hover::before {
  opacity: 1;
}

.play-mode-card:hover {
  transform: translateY(-8px) scale(1.03);
  border-color: var(--color-mocha-light);
  box-shadow: 
    0 12px 32px rgba(168, 144, 128, 0.4),
    0 0 40px rgba(168, 144, 128, 0.2),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
  background: rgba(13, 24, 33, 0.5);
}

.play-mode-card:active {
  transform: translateY(-4px) scale(0.98);
}

.play-mode-card:focus-visible {
  outline: 3px solid var(--color-mocha-light);
  outline-offset: 3px;
}

.play-mode-card__icon {
  width: 4rem;
  height: 4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(168, 144, 128, 0.08);
  border: 2px solid rgba(168, 144, 128, 0.3);
  border-radius: 50%;
  transition: all 0.3s ease;
  position: relative;
  z-index: 1;
}

.play-mode-card__icon svg {
  width: 2.25rem;
  height: 2.25rem;
  stroke: var(--color-mocha-light);
  stroke-width: 2;
  transition: transform 0.3s ease;
}

.play-mode-card:hover .play-mode-card__icon {
  background: rgba(168, 144, 128, 0.08);
  border-color: var(--color-mocha-light);
  box-shadow: 
    0 4px 16px rgba(168, 144, 128, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.2);
  transform: scale(1.05);
}

.play-mode-card:hover .play-mode-card__icon svg {
  transform: scale(1.05);
  filter: drop-shadow(0 0 8px rgba(168, 144, 128, 0.6));
}

.play-mode-card__info {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  position: relative;
  z-index: 1;
}

.play-mode-card__title {
  font-size: 1.15rem;
  font-weight: 700;
  text-align: center;
  color: var(--color-cream);
  transition: color 0.3s ease;
}

.play-mode-card:hover .play-mode-card__title {
  color: var(--color-mocha-light);
  text-shadow: 0 0 10px rgba(168, 144, 128, 0.5);
}

.play-mode-card__desc {
  font-size: 0.875rem;
  color: rgba(245, 245, 220, 0.6);
  text-align: center;
  transition: color 0.3s ease;
}

.play-mode-card:hover .play-mode-card__desc {
  color: rgba(245, 245, 220, 0.8);
}

@keyframes iconeModePulse {
  0%, 100% {
    transform: scale(1);
    box-shadow: 0 4px 12px rgba(168, 144, 128, 0.4);
  }
  50% { 
    transform: scale(1.05);
    box-shadow: 0 4px 16px rgba(168, 144, 128, 0.6);
  }
}

@media (max-width: 639px) {
  .primary-cta {
    padding: 1.25rem 1.5rem;
    min-height: 70px;
  }
  
  .primary-cta__icon {
    font-size: 2.5rem;
  }
  
  .primary-cta__label {
    font-size: 1.1rem;
  }
  
  .play-mode-grid {
    grid-template-columns: 1fr;
    gap: 0.75rem;
    padding: 1rem;
  }
  
  .play-mode-card {
    min-height: 160px;
    padding: 1.5rem 0.75rem;
  }
  
  .play-mode-card__icon {
    width: 3.5rem;
    height: 3.5rem;
  }
  
  .play-mode-card__icon svg {
    width: 2rem;
    height: 2rem;
  }
}

.main-menu {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-sm);
  max-width: 600px;
  margin: 0 auto;
  padding: 0 var(--spacing-md);
}

/* Optimisation mobile */
@media (max-width: 768px) {
  .main-menu {
    gap: 0.75rem;
    padding: 0 var(--spacing-md) var(--spacing-md);
  }
}

.main-menu-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-xs);
  padding: var(--spacing-lg) var(--spacing-xl);
  background: var(--color-bg-light);
  border: 3px solid transparent;
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  color: var(--color-text);
  position: relative;
  overflow: hidden;
  min-height: 120px;
  
  pointer-events: auto !important;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  user-select: none;
}

/* Optimisation mobile - réduire hauteur et padding */
@media (max-width: 768px) {
  .main-menu-btn {
    min-height: 90px;
    padding: var(--spacing-md) var(--spacing-lg);
    gap: 0.375rem;
  }
}

.main-menu-btn::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    rgba(255, 255, 255, 0.1),
    transparent
  );
  transition: left 0.5s;
}

.main-menu-btn:hover {
  transform: translateY(-6px) scale(1.02);
  box-shadow: 
    0 12px 32px rgba(37, 99, 235, 0.4),
    0 0 50px rgba(37, 99, 235, 0.15);
}

.main-menu-btn:hover::before {
  left: 100%;
}

.main-menu-btn:active {
  transform: translateY(-3px) scale(0.98);
}

.main-menu-btn__icon {
  font-size: 3rem;
  margin-bottom: 0;
  animation: iconFloat 3s ease-in-out infinite;
}

@keyframes iconFloat {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-8px); }
}

.main-menu-btn__title {
  font-size: 1.25rem;
  font-weight: 700;
  text-align: center;
  letter-spacing: 0.3px;
  line-height: 1.2;
}

.main-menu-btn__desc {
  font-size: 0.9rem;
  color: #B0B8C3;
  text-align: center;
  line-height: 1.3;
  opacity: 0.95;
}

/* Advanced Link */
.advanced-link-container {
  text-align: center;
  margin-top: var(--spacing-md);
  padding: 0 var(--spacing-md);
}

.advanced-link {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-xs);
  padding: var(--spacing-sm) var(--spacing-md);
  background: transparent;
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-radius: var(--radius-md);
  color: var(--color-text-dim);
  cursor: pointer;
  transition: all 0.3s ease;
  font-size: 0.9rem;
  
  pointer-events: auto !important;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  user-select: none;
}

.advanced-link:hover,
.advanced-link:active {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.3);
  color: var(--color-text);
  transform: translateY(-2px);
}

/* Tutorial */
.tutorial {
  max-width: 700px;
  margin: 0 auto;
  padding: var(--spacing-xl);
  min-height: 70vh;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  /* ✅ FIX: Permettre le scroll du tutoriel */
  max-height: calc(100vh - 120px);
  overflow: hidden; /* Le parent ne scroll pas, seulement l'enfant */
}

.tutorial__content {
  background: var(--color-bg-light);
  padding: var(--spacing-xl);
  border-radius: var(--radius-lg);
  margin-bottom: var(--spacing-lg);
  min-height: 400px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: center;
  animation: uiEntreeParLeBas 0.5s ease-out;
  /* ✅ FIX CRITIQUE: Permettre le scroll du contenu */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch; /* Scroll fluide sur iOS */
  overscroll-behavior-y: contain; /* Évite le bounce sur le body */
  max-height: calc(100vh - 250px); /* Laisse de l'espace pour les boutons */
}

/* @keyframes slideInUp — definition morte retiree (A9) : une definition plus bas dans le
   fichier (l.7452 avant retraits) gagnait deja pour TOUT le document, donc
   celle-ci ne s'appliquait nulle part. Le rendu est inchange. */

.tutorial__actions {
  display: flex;
  gap: var(--spacing-md);
  justify-content: center;
}

.tutorial__actions .btn {
  flex: 1;
  max-width: 250px;
}

/* Responsive */
@media (max-width: 640px) {
  /* Tutoriel - Optimisé mobile */
  .tutorial {
    padding: var(--spacing-md) var(--spacing-sm);
    min-height: auto;
    /* ✅ FIX iOS: Adapter la hauteur pour mobile */
    max-height: calc(100vh - var(--safe-area-top) - var(--safe-area-bottom) - 80px);
  }
  
  .tutorial__content {
    padding: var(--spacing-lg) var(--spacing-md);
    margin-bottom: var(--spacing-md);
    min-height: auto;
    /* ✅ FIX iOS: Plus d'espace pour scroller sur mobile */
    max-height: calc(100vh - var(--safe-area-top) - var(--safe-area-bottom) - 200px);
    justify-content: flex-start; /* Ne pas centrer verticalement sur mobile */
    /* ✅ FIX: Zone tactile pour le scroll */
    touch-action: pan-y; /* Autorise uniquement le scroll vertical */
  }
  
  .tutorial__icon {
    font-size: 3rem;
    margin-bottom: 0.75rem;
  }
  
  .tutorial__title {
    font-size: 1.5rem;
    margin-bottom: 1rem;
  }
  
  .tutorial__text {
    font-size: 1rem;
    line-height: 1.6;
    margin-bottom: 1.5rem;
  }
  
  .tutorial__actions {
    flex-direction: column;
    gap: var(--spacing-sm);
  }
  
  .tutorial__actions .btn {
    max-width: none;
    width: 100%;
  }
  
  /* Setup modes */
  .setup__modes {
    grid-template-columns: 1fr;
  }
  
  .dice {
    /* ✅ AMÉLIORATION: Gap optimisé et padding augmenté */
    gap: 0.75rem;
    padding: 0.75rem;
  }
  
  .die {
    /* ✅ AMÉLIORATION MAJEURE: Taille augmentée de 70px à 85px (+21%) */
    min-width: 85px;
    min-height: 85px;
    font-size: 2.75rem;
    
    /* ✅ AMÉLIORATION: Border plus visible sur petit écran */
    border: 2px solid rgba(212, 165, 116, 0.3);
  }
  
  /* ✅ NOUVEAU: Zone de clic encore plus grande sur mobile */
  .die::before {
    inset: -15px; /* 15px au lieu de 12px pour les petits écrans */
  }
  
  /* ✅ NOUVEAU: Feedback encore plus prononcé sur mobile */
  .die.touching:not(.die--kept),
  .die:active:not(.die--kept) {
    transform: scale(0.88) !important; /* Encore plus de feedback */
    box-shadow: 
      0 4px 8px rgba(0, 0, 0, 0.12),
      0 0 25px rgba(168, 144, 128, 0.6),
      inset 0 2px 6px rgba(255, 255, 255, 0.6) !important;
  }
  
  .end__actions {
    flex-direction: column;
  }
}

/* Print */
@media print {
  .header__options-btn,
  .game__actions,
  .sidebar {
    display: none;
  }
}
/* ========================================
   CSS ADDITIONNEL (anciennement inline)
   ======================================== */

/* ✅ FIX iOS : Styles améliorés pour les appareils tactiles */
* {
  -webkit-tap-highlight-color: rgba(59, 130, 246, 0.3);
  -webkit-touch-callout: none;
}

button {
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  touch-action: manipulation;
}

/* ✅ FIX iOS : Désactiver le zoom sur les inputs */
input, select, textarea {
  font-size: 16px !important;
}

/* Styles additionnels pour une meilleure interface multijoueur */
/* ============================================ */
/* CONFIGURATION MULTIJOUEUR - DESIGN PREMIUM */
/* ============================================ */

/* Sélecteur de nombre de joueurs - Premium */
.player-count-selector {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.5rem;
  flex-wrap: wrap;
}

.player-count-btn {
  flex: 1;
  padding: 1rem;
  background: rgba(0, 0, 0, 0.3);
  backdrop-filter: blur(10px);
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-radius: var(--radius-md);
  color: white;
  font-size: 1.35rem;
  font-weight: 700;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  min-height: 56px;
  position: relative;
  overflow: hidden;
  touch-action: manipulation;
}

/* Effet de brillance au survol/tap */
.player-count-btn::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  background: rgba(168, 144, 128, 0.2);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  transition: width 0.5s, height 0.5s;
}

.player-count-btn:active::before {
  width: 200px;
  height: 200px;
}

.player-count-btn:active {
  transform: scale(0.95);
}

.player-count-btn.active {
  background: linear-gradient(135deg, var(--color-mocha-light) 0%, var(--color-mocha) 100%);
  border-color: transparent;
  color: #0d1821;
  box-shadow: var(--shadow-md);
}

/* Liste des joueurs - Premium */
.player-input-group {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-top: 1rem;
  padding: 0;
  background: transparent;
  border-radius: 0;
}

.player-input-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  animation: slideInPlayer 0.5s ease-out;
  animation-fill-mode: backwards;
}

/* Animation échelonnée pour chaque joueur */
.player-input-row:nth-child(1) { animation-delay: 0.1s; }
.player-input-row:nth-child(2) { animation-delay: 0.15s; }
.player-input-row:nth-child(3) { animation-delay: 0.2s; }
.player-input-row:nth-child(4) { animation-delay: 0.25s; }
.player-input-row:nth-child(5) { animation-delay: 0.3s; }
.player-input-row:nth-child(6) { animation-delay: 0.35s; }

@keyframes slideInPlayer {
  from {
    opacity: 0;
    transform: translateX(-10px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* Avatar premium avec effet 3D */
.player-num {
  width: 56px;
  height: 56px;
  min-width: 56px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.75rem;
  font-weight: 700;
  background: var(--gradient);
  box-shadow: var(--shadow-md);
  position: relative;
  transition: transform 0.3s;
}

/* Effet de lumière sur l'avatar */
.player-num::after {
  content: '';
  position: absolute;
  top: 4px;
  left: 4px;
  right: 4px;
  height: 50%;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, transparent 100%);
  border-radius: 50%;
  pointer-events: none;
}

.player-input-row:active .player-num {
  transform: scale(0.95);
}

/* Couleurs des avatars - 6 joueurs (ALIGNÉES AVEC LE JEU) */
.player-input-row:nth-child(1) .player-num { 
  --gradient: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); 
  --shadow: rgba(59, 130, 246, 0.5);
  color: white;
}
.player-input-row:nth-child(2) .player-num { 
  --gradient: linear-gradient(135deg, #10b981 0%, #059669 100%); 
  --shadow: rgba(16, 185, 129, 0.5);
  color: white;
}
.player-input-row:nth-child(3) .player-num { 
  --gradient: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); 
  --shadow: rgba(245, 158, 11, 0.5);
  color: white;
}
.player-input-row:nth-child(4) .player-num { 
  --gradient: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); 
  --shadow: rgba(239, 68, 68, 0.5);
  color: white;
}
.player-input-row:nth-child(5) .player-num { 
  --gradient: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); 
  --shadow: rgba(139, 92, 246, 0.5);
  color: white;
}
.player-input-row:nth-child(6) .player-num { 
  --gradient: linear-gradient(135deg, #ec4899 0%, #db2777 100%); 
  --shadow: rgba(236, 72, 153, 0.5);
  color: white;
}

/* Input premium avec glassmorphism */
.player-input-row input {
  flex: 1;
  padding: 1rem 1.25rem;
  background: rgba(0, 0, 0, 0.3);
  backdrop-filter: blur(10px);
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-radius: var(--radius-md);
  color: white;
  font-size: 1.05rem;
  min-height: 56px;
  transition: all 0.3s;
  font-weight: 500;
}

.player-input-row input::placeholder {
  color: rgba(255, 255, 255, 0.4);
}

.player-input-row input:focus {
  outline: none;
  border-color: var(--color-mocha);
  background: rgba(0, 0, 0, 0.1);
  box-shadow: 0 0 0 4px rgba(168, 144, 128, 0.15);
}

/* Compatibilité ancienne structure (label) */
.player-input-row label {
  display: none;
}

.setup__config label {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.setup__config label > span {
  font-weight: 500;
  color: #cbd5e1;
}

.setup__hint {
  font-size: 0.875rem;
  color: #B0B8C3;
  font-style: italic;
  margin-top: 0.5rem;
}

/* Badge premium pour la section multijoueur */
.setup-badge-premium {
  display: inline-block;
  background: rgba(168, 144, 128, 0.08);
  backdrop-filter: blur(10px);
  color: var(--color-mocha);
  padding: 6px 16px;
  border-radius: 20px;
  font-size: 0.75rem;
  font-weight: 700;
  margin-bottom: 1rem;
  border: 1px solid rgba(168, 144, 128, 0.4);
  text-transform: uppercase;
  letter-spacing: 1px;
}

/* Optimisations pour petits écrans - Multiplayer premium */
@media (max-width: 400px) {
  .player-count-btn {
    font-size: 1.25rem;
    min-height: 52px;
  }
  
  .player-num {
    width: 52px;
    height: 52px;
    min-width: 52px;
    font-size: 1.5rem;
  }
  
  .player-input-row input {
    padding: 0.875rem 1rem;
    font-size: 1rem;
    min-height: 52px;
  }
}

/* ✅ FIX iOS : Améliorer les boutons pour le tactile */
.btn {
  min-height: 44px;
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
  pointer-events: auto !important;
}

.btn:active {
  transform: scale(0.95);
}

.btn:disabled,
.btn[aria-disabled="true"],
.btn--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Un second bloc `.die` se trouvait ici. Ses cinq declarations
   (touch-action, -webkit-user-select, user-select, cursor, pointer-events)
   etaient toutes identiques a celles de la regle `.die` d'origine : il
   n'apportait rien, mais etant plus bas dans le fichier il gagnait a specificite
   egale et annulait silencieusement `.die--rolling { pointer-events: none }`.
   Retire (action A8). Voir la note en fin de fichier. */

.die:active:not(.die--kept) {
  transform: scale(0.95);
}

.game__hint {
  font-size: 1rem;
  font-weight: 500;
  color: #3b82f6;
  text-align: center;
  padding: 0.75rem;
  background: rgba(59, 130, 246, 0.1);
  border-radius: 8px;
  margin-top: 1rem;
}

/* ==================== PRIORITÉ 1 : Variantes régionales ==================== */
.options__section {
  margin-bottom: 2rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.options__section:last-child {
  border-bottom: none;
}

.options__select {
  width: 100%;
  padding: 0.75rem;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 6px;
  color: white;
  font-size: 1rem;
  cursor: pointer;
}

.options__select:focus {
  outline: none;
  border-color: #3b82f6;
  background: rgba(255, 255, 255, 0.15);
}

.options__select option {
  background: #1e293b;
  color: white;
}

.variant-description,
.mode-description {
  margin-top: 1rem;
  padding: 1rem;
  background: rgba(59, 130, 246, 0.1);
  border-left: 3px solid #3b82f6;
  border-radius: 6px;
}

.mode-description {
  background: rgba(34, 197, 94, 0.1);
  border-left-color: #22c55e;
}

.variant-description strong,
.mode-description strong {
  color: #3b82f6;
}

.mode-description strong {
  color: #22c55e;
}

.variant-option {
  margin-top: 1rem;
  padding: 1rem;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 6px;
}

.options__hint {
  font-size: 0.875rem;
  color: #B0B8C3;
  line-height: 1.5;
}

/* ==================== PRIORITÉ 3 : Historique amélioré ==================== */
.history__content {
  max-height: 500px;
  overflow-y: auto;
  padding-right: 0.5rem;
}

.history__content::-webkit-scrollbar {
  width: 6px;
}

.history__content::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 3px;
}

.history__content::-webkit-scrollbar-thumb {
  background: rgba(59, 130, 246, 0.5);
  border-radius: 3px;
}

.history__content::-webkit-scrollbar-thumb:hover {
  background: rgba(59, 130, 246, 0.7);
}

.history-item {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.75rem;
  margin-bottom: 0.5rem;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 6px;
  /* ⚠️ La couleur passe par une VARIABLE, posée en ligne par lib/ui-manager.js
     (`--histo-accent`), et non plus par un `border-left-color` en ligne. Le
     rendu LTR est identique — la variable résout la même couleur — mais c'est
     la seule façon pour la feuille de repeindre le bord DROIT en arabe : un
     style en ligne ne se lit pas depuis le CSS. Voir le bloc `[dir="rtl"]` en
     fin de fichier. */
  border-left: 3px solid var(--histo-accent, transparent);
  transition: all 0.2s;
  animation: slideInHistory 0.3s ease-out;
}

@keyframes slideInHistory {
  from {
    opacity: 0;
    transform: translateX(-20px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

.history-item:hover {
  background: rgba(255, 255, 255, 0.1);
}

.history-item--roll {
  border-left-color: #64748b;
}

.history-item--turn-end {
  border-left-color: #3b82f6;
  background: rgba(59, 130, 246, 0.1);
}

.history-item--special {
  border-left-color: #eab308;
  background: rgba(234, 179, 8, 0.1);
}

.history-item__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 0.875rem;
}

.history-item__content {
  font-size: 0.875rem;
  color: #cbd5e1;
}

.history-item__time {
  font-size: 0.75rem;
  color: #64748b;
  font-style: italic;
}

.history-item__player {
  font-weight: 600;
  color: #3b82f6;
}

.history-item__combo {
  font-weight: 600;
  color: #22c55e;
}

/* ==================== PRIORITÉ 4 : Aide/Tutoriel ==================== */
.header {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 1rem;
}

.header__title {
  text-align: center;
  margin: 0;
}

/* ==================== RESPONSIVE HEADER PROTOTYPE 2 ==================== */

@media (max-width: 480px) {
  .header {
    padding: 0.75rem 1rem;
    gap: 0.75rem;
  }
  
  .header__title {
    font-size: 1.25rem;
  }
  
  .header__title-emoji {
    font-size: 1.15rem;
  }
  
  .header__btn--back,
  .header__btn--menu {
    min-width: 44px;
    min-height: 44px;
    font-size: 1.35rem;
  }
}

@media (max-width: 375px) {
  .header {
    padding: 0.75rem 0.875rem;
  }
  
  .header__title {
    font-size: 1.15rem;
  }
  
  .header__btn--back,
  .header__btn--menu {
    min-width: 40px;
    min-height: 40px;
    font-size: 1.25rem;
  }
}


.modal__content--large {
  max-width: 700px;
  max-height: 80vh;
  overflow-y: auto;
}

.help-tabs {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
  border-bottom: 2px solid rgba(255, 255, 255, 0.1);
  flex-wrap: wrap;
}

.help-tab {
  background: none;
  border: none;
  color: #B0B8C3;
  padding: 0.75rem 1rem;
  cursor: pointer;
  font-size: 0.875rem;
  font-weight: 500;
  border-bottom: 2px solid transparent;
  transition: all 0.2s;
  margin-bottom: -2px;
}

.help-tab:hover {
  color: #cbd5e1;
}

.help-tab--active {
  color: #3b82f6;
  border-bottom-color: #3b82f6;
}

.help-content {
  position: relative;
}

.help-panel {
  display: none;
}

.help-panel--active {
  display: block;
  animation: uiFondu 0.3s ease-out;
}

/* @keyframes fadeIn — doublon strict retire (A9). Definition unique l.8252 avant retraits. */

.help-panel h3 {
  color: #3b82f6;
  margin: 1.5rem 0 0.75rem 0;
  font-size: 1.1rem;
}

.help-panel h3:first-child {
  margin-top: 0;
}

.help-panel ul,
.help-panel ol {
  margin: 0.5rem 0 1rem 1.5rem;
  color: #cbd5e1;
}

.help-panel li {
  margin: 0.5rem 0;
  line-height: 1.6;
}

.help-panel p {
  color: #cbd5e1;
  line-height: 1.6;
  margin: 0.5rem 0;
}

.combo-item {
  background: rgba(255, 255, 255, 0.05);
  padding: 1rem;
  border-radius: 8px;
  margin-bottom: 0.75rem;
  border-left: 3px solid #64748b;
}

.combo-item--best {
  border-left-color: #eab308;
  background: rgba(234, 179, 8, 0.1);
}

.combo-item--worst {
  border-left-color: #ef4444;
  background: rgba(239, 68, 68, 0.1);
}

.combo-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.5rem;
}

.combo-icon {
  font-size: 1.5rem;
}

.combo-name {
  font-weight: 600;
  color: white;
  flex: 1;
  margin-left: 0.75rem;
}

.combo-value {
  color: #22c55e;
  font-weight: 600;
  font-size: 1.1rem;
}

.combo-desc {
  color: #B0B8C3;
  font-size: 0.875rem;
  margin: 0;
}

/* ==================== PRIORITÉ 5 : Victoire améliorée ==================== */
.end__stats {
  background: rgba(255, 255, 255, 0.05);
  padding: 1.5rem;
  border-radius: 12px;
  margin: 1.5rem 0;
}

.end__stat-item {
  display: flex;
  justify-content: space-between;
  padding: 0.75rem 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.end__stat-item:last-child {
  border-bottom: none;
}

.end__stat-label {
  color: #B0B8C3;
  font-size: 0.875rem;
}

.end__stat-value {
  color: #3b82f6;
  font-weight: 600;
  font-size: 1rem;
}


/* ==================== PRIORITÉ 6 : Statistiques ==================== */
.header__stats-btn {
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: white;
  padding: 0.5rem 1rem;
  border-radius: 6px;
  cursor: pointer;
  font-size: 0.875rem;
  transition: all 0.2s;
}

.header__stats-btn:hover {
  background: rgba(255, 255, 255, 0.2);
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1rem;
  margin: 1.5rem 0;
}

.stats-card {
  background: rgba(255, 255, 255, 0.05);
  padding: 1.5rem;
  border-radius: 12px;
  text-align: center;
}

.stats-card__icon {
  font-size: 2rem;
  margin-bottom: 0.5rem;
}

.stats-card__value {
  font-size: 2rem;
  font-weight: 700;
  color: #3b82f6;
  margin: 0.5rem 0;
}

.stats-card__label {
  color: #B0B8C3;
  font-size: 0.875rem;
}

.stats-list {
  background: rgba(255, 255, 255, 0.05);
  padding: 1rem;
  border-radius: 12px;
  margin: 1rem 0;
}

.stats-list__item {
  display: flex;
  justify-content: space-between;
  padding: 0.75rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.stats-list__item:last-child {
  border-bottom: none;
}

.stats-reset-btn {
  margin-top: 1.5rem;
  background: rgba(239, 68, 68, 0.1);
  color: #ef4444;
  border: 1px solid #ef4444;
}

.stats-reset-btn:hover {
  background: rgba(239, 68, 68, 0.2);
}

/* ==================== Modal sélection niveau IA ==================== */
/* ==================== MODAL QUICK PLAY ==================== */

.modal__content--quick-play {
  max-width: 600px;
  background: rgba(13, 24, 33, 0.92);
  backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow: var(--shadow-xl);
}

.modal__header--quick-play {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.5rem;
  border-bottom: 1px solid rgba(168, 144, 128, 0.2);
  background: rgba(168, 144, 128, 0.05);
}

.quick-play-header-icon {
  width: 2.5rem;
  height: 2.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--color-mocha-light) 0%, var(--color-mocha) 100%);
  border-radius: 12px;
  box-shadow: var(--shadow-sm);
}

.quick-play-header-icon svg {
  width: 1.25rem;
  height: 1.25rem;
  stroke: #0d1821;
  stroke-width: 2.5;
  fill: #0d1821;
}

.modal__header--quick-play h2 {
  flex: 1;
  margin: 0;
  font-size: 1.5rem;
  background: linear-gradient(135deg, var(--color-mocha-light) 0%, var(--color-mocha) 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  font-weight: 700;
}

.quick-play-input-group {
  margin-bottom: 2rem;
}

.quick-play-label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
  color: var(--color-cream);
  font-weight: 600;
  font-size: 0.95rem;
}

.quick-play-label svg {
  stroke: var(--color-mocha-light);
  flex-shrink: 0;
}

.quick-play-input {
  width: 100%;
  padding: 0.875rem 1rem;
  background: rgba(13, 24, 33, 0.7);
  border: 2px solid rgba(212, 165, 116, 0.3);
  border-radius: 0.75rem;
  color: white;
  font-size: 1rem;
  transition: all 0.3s ease;
}

.quick-play-input:focus {
  outline: none;
  border-color: var(--color-mocha-light);
  box-shadow: 
    0 0 0 3px rgba(168, 144, 128, 0.1),
    0 4px 12px rgba(168, 144, 128, 0.2);
  background: rgba(13, 24, 33, 0.7);
}

.quick-play-input::placeholder {
  color: rgba(245, 245, 220, 0.4);
}

.quick-play-section-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
  padding: 0.75rem;
  background: rgba(168, 144, 128, 0.03);
  border-radius: 0.5rem;
  color: var(--color-cream);
  font-weight: 600;
  font-size: 1rem;
}

.quick-play-section-title svg {
  stroke: var(--color-mocha-light);
  flex-shrink: 0;
}

.ai-level-selector {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 1rem;
  margin-top: 1rem;
}

.ai-level-card {
  background: rgba(13, 24, 33, 0.5);
  border: 2px solid rgba(212, 165, 116, 0.3);
  border-radius: 12px;
  padding: 1.5rem 1rem;
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  position: relative;
  overflow: hidden;
}

.ai-level-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 50%;
  background: rgba(168, 144, 128, 0.03);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.ai-level-card:hover::before {
  opacity: 1;
}

.ai-level-card:hover {
  transform: translateY(-6px) scale(1.02);
  border-color: var(--color-mocha-light);
  box-shadow: 
    0 8px 24px rgba(168, 144, 128, 0.3),
    0 0 30px rgba(168, 144, 128, 0.15),
    inset 0 1px 0 rgba(255, 255, 255, 0.1);
  background: rgba(13, 24, 33, 0.5);
}

.ai-level-card.ai-level-card--active {
  background: rgba(168, 144, 128, 0.08);
  border-color: var(--color-mocha-light);
  box-shadow: 
    0 4px 16px rgba(168, 144, 128, 0.4),
    0 0 20px rgba(168, 144, 128, 0.2),
    inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

.ai-level-card.ai-level-card--active::before {
  opacity: 1;
}

.ai-level-card__icon {
  width: 3.5rem;
  height: 3.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(168, 144, 128, 0.08);
  border: 2px solid rgba(168, 144, 128, 0.3);
  border-radius: 50%;
  transition: all 0.3s ease;
  position: relative;
  z-index: 1;
}

.ai-level-card__icon svg {
  width: 2rem;
  height: 2rem;
  stroke: var(--color-mocha-light);
  stroke-width: 2;
  transition: transform 0.3s ease;
}

.ai-level-card:hover .ai-level-card__icon {
  background: rgba(168, 144, 128, 0.08);
  border-color: var(--color-mocha-light);
  box-shadow: 
    0 4px 12px rgba(168, 144, 128, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.2);
  transform: scale(1.05);
}

.ai-level-card.ai-level-card--active .ai-level-card__icon {
  background: rgba(168, 144, 128, 0.08);
  border-color: var(--color-mocha-light);
  box-shadow: 
    0 4px 12px rgba(168, 144, 128, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.3);
}

.ai-level-card:hover .ai-level-card__icon svg {
  transform: scale(1.05);
  filter: drop-shadow(0 0 6px rgba(168, 144, 128, 0.5));
}

.ai-level-card__name {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--color-cream);
  margin: 0.25rem 0;
  position: relative;
  z-index: 1;
  transition: color 0.3s ease;
}

.ai-level-card:hover .ai-level-card__name,
.ai-level-card.ai-level-card--active .ai-level-card__name {
  color: var(--color-mocha-light);
  text-shadow: 0 0 8px rgba(168, 144, 128, 0.4);
}

.ai-level-card__desc {
  font-size: 0.875rem;
  color: rgba(245, 245, 220, 0.6);
  line-height: 1.3;
  position: relative;
  z-index: 1;
  transition: color 0.3s ease;
}

.ai-level-card:hover .ai-level-card__desc,
.ai-level-card.ai-level-card--active .ai-level-card__desc {
  color: rgba(245, 245, 220, 0.8);
}

.modal__footer--quick-play {
  display: flex;
  gap: 1rem;
  padding: 1.5rem;
  border-top: 2px solid rgba(168, 144, 128, 0.2);
  background: linear-gradient(to top, rgba(168, 144, 128, 0.03) 0%, transparent 100%);
}

.btn--primary-gold {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  background: linear-gradient(135deg, var(--color-mocha-light) 0%, var(--color-mocha) 50%, #d4a574 100%);
  color: #0d1821;
  font-weight: 700;
  box-shadow: 
    0 4px 12px rgba(168, 144, 128, 0.4),
    inset 0 1px 0 rgba(255, 255, 255, 0.3);
  transition: all 0.3s ease;
}

.btn--primary-gold:hover {
  transform: translateY(-2px);
  box-shadow: 
    0 6px 20px rgba(168, 144, 128, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.4);
}

.btn--primary-gold svg {
  stroke: #0d1821;
  fill: #0d1821;
  width: 18px;
  height: 18px;
}

@media (max-width: 639px) {
  .ai-level-selector {
    grid-template-columns: 1fr;
    gap: 0.75rem;
  }
  
  .ai-level-card {
    padding: 1.25rem 1rem;
  }
  
  .ai-level-card__icon {
    width: 3rem;
    height: 3rem;
  }
  
  .ai-level-card__icon svg {
    width: 1.75rem;
    height: 1.75rem;
  }
  
  .modal__footer--quick-play {
    flex-direction: column;
  }
  
  .modal__footer--quick-play .btn {
    width: 100%;
  }
}

/* ==================== Sélection niveau IA ==================== */
.game-mode-selector {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1rem;
  margin-top: 1rem;
}

.game-mode-card {
  background: rgba(255, 255, 255, 0.05);
  border: 2px solid rgba(255, 255, 255, 0.2);
  border-radius: 12px;
  padding: 1.5rem 1rem;
  cursor: pointer;
  transition: all 0.3s;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}

.game-mode-card:hover {
  background: rgba(255, 255, 255, 0.1);
  transform: translateY(-4px);
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
}

.game-mode-card.game-mode-card--active {
  background: rgba(34, 197, 94, 0.2);
  border-color: #22c55e;
  box-shadow: 0 4px 12px rgba(34, 197, 94, 0.3);
}

.game-mode-card__icon {
  font-size: 2.5rem;
  line-height: 1;
}

.game-mode-card__name {
  font-size: 1.1rem;
  font-weight: 700;
  color: white;
  margin: 0.5rem 0 0.25rem;
}

.game-mode-card__desc {
  font-size: 0.85rem;
  color: #B0B8C3;
  line-height: 1.3;
}

/* ==================== Comparaison des scores (mode compétitif) ==================== */
.scores-comparison {
  margin-top: 1.5rem;
  padding: 1rem;
  background: rgba(59, 130, 246, 0.1);
  border: 2px solid rgba(59, 130, 246, 0.3);
  border-radius: 12px;
  animation: slideInDown 0.4s ease-out;
}

@keyframes slideInDown {
  from {
    opacity: 0;
    transform: translateY(-20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.scores-comparison__title {
  text-align: center;
  color: #3b82f6;
  font-size: 1.1rem;
  font-weight: 700;
  margin-bottom: 1rem;
}

.scores-comparison__list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.score-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.75rem 1rem;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 8px;
  border-left: 4px solid transparent;
  transition: all 0.3s;
}

.score-item--winner {
  background: rgba(34, 197, 94, 0.15);
  border-left-color: #22c55e;
}

.score-item--loser {
  background: rgba(239, 68, 68, 0.15);
  border-left-color: #ef4444;
}

.score-item__player {
  font-weight: 600;
  color: white;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.score-item__score {
  font-weight: 700;
  font-size: 1.1rem;
}

.score-item__score-label {
  color: #B0B8C3;
  font-size: 0.85rem;
  margin-right: 0.5rem;
}

/**
 * language-badges.css - Style pour les codes pays (FR, EN, ES...)
 * Alternative fiable aux emoji de drapeaux
 */

/* ==================== Badge de langue (FR, EN...) ==================== */

.lang-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 0.5rem;
  border-radius: 8px;
  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
  color: white;
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.5px;
  box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);
  transition: all 0.3s ease;
}

/* Badge dans le menu */
.lang-badge--menu {
  min-width: 36px;
  height: 28px;
  font-size: 0.7rem;
  flex-shrink: 0;
}

/* ==================== Conteneur Dropdown ==================== */

.language-dropdown {
  position: relative;
}

/* ==================== Bouton Toggle ==================== */

.language-dropdown__toggle {
  position: relative;
  padding: 0.5rem;
  transition: all 0.3s ease;
}

.language-dropdown__toggle:hover .lang-badge {
  transform: scale(1.05);
  box-shadow: 0 4px 8px rgba(59, 130, 246, 0.4);
}

.language-dropdown__toggle:active .lang-badge {
  transform: scale(0.95);
}

/* Flèche indicatrice */
.language-dropdown__toggle::after {
  content: '▼';
  position: absolute;
  bottom: 4px;
  right: 4px;
  font-size: 0.5rem;
  opacity: 0.7;
  color: white;
  transition: transform 0.3s ease;
}

.language-dropdown--open .language-dropdown__toggle::after {
  transform: rotate(180deg);
}

/* ==================== Menu Dropdown ==================== */

.language-dropdown__menu {
  position: absolute;
  top: calc(100% + 0.5rem);
  right: 0;
  background: white;
  border-radius: 12px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
  padding: 0.5rem;
  min-width: 180px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-10px) scale(0.95);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 1000;
  backdrop-filter: blur(10px);
}

.language-dropdown--open .language-dropdown__menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0) scale(1);
}

/* ==================== Items du Menu ==================== */

.language-dropdown__item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s ease;
  font-size: 1rem;
  color: #1f2937;
  width: 100%;
  border: none;
  background: none;
  text-align: left;
  position: relative;
}

.language-dropdown__item:hover {
  background: rgba(59, 130, 246, 0.1);
  transform: translateX(4px);
}

.language-dropdown__item:hover .lang-badge--menu {
  transform: scale(1.05);
  box-shadow: 0 3px 6px rgba(59, 130, 246, 0.4);
}

.language-dropdown__item:active {
  transform: translateX(2px) scale(0.98);
}

/* Item actif */
.language-dropdown__item--active {
  background: rgba(59, 130, 246, 0.1);
  font-weight: 600;
  color: #2563eb;
}

.language-dropdown__item--active .lang-badge--menu {
  background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
  box-shadow: 0 3px 8px rgba(37, 99, 235, 0.4);
}

/* Nom de la langue */
.language-dropdown__item-name {
  flex: 1;
}

/* Checkmark pour l'item actif */
.language-dropdown__check {
  color: #10b981;
  font-weight: bold;
  font-size: 1.2rem;
  margin-left: auto;
}

/* ==================== Variantes de couleurs par langue (optionnel) ==================== */

[data-lang="fr"] .lang-badge--menu {
  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
}

[data-lang="en"] .lang-badge--menu {
  background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
}

[data-lang="es"] .lang-badge--menu {
  background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
}

[data-lang="de"] .lang-badge--menu {
  background: linear-gradient(135deg, #000000 0%, #1f2937 100%);
}

[data-lang="it"] .lang-badge--menu {
  background: linear-gradient(135deg, #10b981 0%, #059669 100%);
}

[data-lang="pt"] .lang-badge--menu {
  background: linear-gradient(135deg, #10b981 0%, #059669 100%);
}

/* ==================== Mode Sombre ==================== */

[data-theme="dark"] .language-dropdown__menu {
  background: #1f2937;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12);
}

[data-theme="dark"] .language-dropdown__item {
  color: #f3f4f6;
}

[data-theme="dark"] .language-dropdown__item:hover {
  background: rgba(59, 130, 246, 0.12);
}

[data-theme="dark"] .language-dropdown__item--active {
  background: rgba(59, 130, 246, 0.12);
  color: #60a5fa;
}

/* ==================== Responsive ==================== */

@media (max-width: 768px) {
  .language-dropdown__menu {
    min-width: 160px;
    right: -0.5rem;
  }
  
  .language-dropdown__item {
    padding: 0.65rem 0.85rem;
    font-size: 0.95rem;
  }
  
  .lang-badge {
    min-width: 28px;
    height: 28px;
    font-size: 0.7rem;
  }
  
  .lang-badge--menu {
    min-width: 32px;
    height: 24px;
    font-size: 0.65rem;
  }
}

@media (max-width: 480px) {
  .language-dropdown__menu {
    right: -1rem;
  }
}

/* ==================== Accessibilité ==================== */

.language-dropdown__toggle:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

.language-dropdown__item:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: -2px;
}

/* Réduire les animations pour les utilisateurs qui préfèrent */
@media (prefers-reduced-motion: reduce) {
  .language-dropdown__toggle,
  .language-dropdown__menu,
  .language-dropdown__item,
  .lang-badge {
    transition: none;
    animation: none !important;
  }
  
  .language-dropdown__item:hover,
  .language-dropdown__toggle:hover .lang-badge {
    transform: none;
  }
}

/* ==================== Scroll pour beaucoup de langues ==================== */

.language-dropdown__menu {
  max-height: 400px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(59, 130, 246, 0.5) transparent;
}

.language-dropdown__menu::-webkit-scrollbar {
  width: 6px;
}

.language-dropdown__menu::-webkit-scrollbar-track {
  background: transparent;
}

.language-dropdown__menu::-webkit-scrollbar-thumb {
  background: rgba(59, 130, 246, 0.5);
  border-radius: 3px;
}

.language-dropdown__menu::-webkit-scrollbar-thumb:hover {
  background: rgba(59, 130, 246, 0.7);
}

/* ==================== Animation d'entrée ==================== */

@keyframes menuDeroulement {
  from {
    opacity: 0;
    transform: translateY(-10px) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.language-dropdown--open .language-dropdown__menu {
  animation: menuDeroulement 0.3s ease-out;
}

/* ==================== MODAL SÉLECTION DE LANGUE ==================== */
/* Responsive - Mobile/Tablet/Desktop */

/* Badge de langue actuelle dans le menu header */
.header-menu__badge {
  margin-left: auto;
  padding: 0.25rem 0.5rem;
  background: var(--gradient-gold);
  color: var(--color-primary-dark);
  font-weight: 700;
  font-size: 0.75rem;
  border-radius: 4px;
  letter-spacing: 0.5px;
}

/* Modal content spécifique pour les langues */
.modal__content--language {
  max-width: 600px;
  width: 90%;
}

/* Header du modal */
.language-modal__title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--color-text);
  margin: 0;
}

.language-modal__icon {
  font-size: 2rem;
  line-height: 1;
}

/* Grille de langues - Responsive */
.language-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
  margin: 1rem 0;
}

/* 2 colonnes sur tablette */
@media (min-width: 480px) {
  .language-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* 2 colonnes sur desktop */
@media (min-width: 768px) {
  .language-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem;
  }
}

/* Carte de langue */
.language-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
  background: rgba(255, 255, 255, 0.05);
  border: 2px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  position: relative;
  overflow: hidden;
  
  /* Reset button styles */
  width: 100%;
  text-align: left;
  font-family: inherit;
  color: inherit;
}

/* Effet de brillance au survol (desktop seulement) */
.language-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
  transition: left 0.5s;
}

/* Hover - Desktop uniquement */
@media (hover: hover) and (pointer: fine) {
  .language-card:hover {
    background: rgba(255, 255, 255, 0.1);
    border-color: var(--color-primary-light);
    transform: translateX(4px);
    box-shadow: 0 4px 12px rgba(26, 90, 138, 0.3);
  }
  
  .language-card:hover::before {
    left: 100%;
  }
}

/* Active - Mobile tactile */
.language-card:active {
  transform: scale(0.98);
  background: rgba(255, 255, 255, 0.15);
}

/* Langue actuellement sélectionnée */
.language-card--active {
  background: rgba(26, 90, 138, 0.2);
  border-color: var(--color-primary);
  /* ⚠️ `var(--shadow-gold-sm)` A ÉTÉ RETIRÉ : la variable n'est définie NULLE
     PART, et une seule `var()` invalide rend TOUTE la déclaration invalide au
     moment du calcul. Cette carte n'avait donc AUCUNE ombre — pas même l'ombre
     bleue écrite juste ici, qui est parfaitement valide. Mesuré le 10 août par
     le contrôle n° 8 de `verifier-inertes`.
     On restitue l'ombre écrite plutôt que d'inventer un halo doré qui n'a
     jamais existé à l'écran (piège n° 37 : corriger la mesure ne doit pas
     ajouter un élément que personne n'a demandé). */
  box-shadow: 0 4px 16px rgba(26, 90, 138, 0.4);
}

.language-card--active::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 4px;
  height: 100%;
  background: var(--gradient-gold);
}

/* Drapeau emoji */
.language-card__flag {
  font-size: 2.5rem;
  line-height: 1;
  flex-shrink: 0;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
}

/* Informations textuelles */
.language-card__info {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0; /* Permet le text-overflow */
}

.language-card__name {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.language-card__native {
  font-size: 0.85rem;
  color: var(--color-text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Checkmark pour la langue active */
.language-card__check {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--color-mocha-light);
  opacity: 0;
  transition: opacity 0.3s;
  flex-shrink: 0;
  filter: drop-shadow(0 2px 4px rgba(168, 144, 128, 0.5));
}

.language-card--active .language-card__check {
  opacity: 1;
  animation: checkBounce 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes checkBounce {
  0% {
    transform: scale(0);
    opacity: 0;
  }
  60% {
    transform: scale(1.05);
    opacity: 1;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

/* Footer avec information */
.language-modal__footer-info {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 1rem;
  margin-top: 1rem;
  background: rgba(59, 130, 246, 0.1);
  border: 1px solid rgba(59, 130, 246, 0.2);
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  color: var(--color-text-dim);
}

.language-modal__info-icon {
  font-size: 1.25rem;
  flex-shrink: 0;
}

.language-modal__info-text {
  line-height: 1.4;
}

/* Focus clavier visible (accessibilité) */
.language-card:focus-visible {
  outline: 3px solid var(--color-mocha-light);
  outline-offset: 2px;
  z-index: 1;
}

/* Navigation clavier - Highlight la carte */
.language-card:focus {
  z-index: 1;
}

/* Responsive - Petits écrans (< 360px) */
@media (max-width: 359px) {
  .language-card {
    padding: 0.75rem;
    gap: 0.75rem;
  }
  
  .language-card__flag {
    font-size: 2rem;
  }
  
  .language-card__name {
    font-size: 1rem;
  }
  
  .language-card__native {
    font-size: 0.75rem;
  }
}

/* Responsive - Grands écrans (> 768px) */
@media (min-width: 768px) {
  .modal__content--language {
    max-width: 700px;
  }
  
  .language-card {
    padding: 1.25rem;
  }
  
  .language-card__flag {
    font-size: 3rem;
  }
}

/* Animation d'ouverture du modal */
.modal[style*="display: flex"] .modal__content--language {
  animation: languageModalSlideIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes languageModalSlideIn {
  from {
    opacity: 0;
    transform: translateY(-30px) scale(0.9);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* Overlay fade-in */
.modal[style*="display: flex"] .modal__overlay {
  animation: overlayFadeIn 0.3s ease-out;
}

@keyframes overlayFadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* Support du mode sombre / clair */
@media (prefers-color-scheme: light) {
  .language-card {
    background: rgba(0, 0, 0, 0.03);
    border-color: rgba(0, 0, 0, 0.1);
  }
  
  .language-card--active {
    background: rgba(26, 90, 138, 0.1);
  }
}

/* Optimisation des performances - GPU acceleration */
.language-card,
.language-card__check {
  will-change: transform, opacity;
}

/* Désactivation des animations si l'utilisateur préfère */
@media (prefers-reduced-motion: reduce) {
  .language-card,
  .language-card::before,
  .language-card__check,
  .modal__content--language {
    animation: none !important;
    transition: none !important;
  }
}

/* Support RTL pour l'arabe */
.language-card[data-lang="ar"] .language-card__info {
  direction: rtl;
  text-align: right;
}

/* ==================== FIN DU MODAL LANGUE ==================== */
/**
 * tutorial-styles.css
 * Styles CSS pour le nouveau tutoriel amélioré
 * À ajouter à votre fichier styles.css principal
 */

/* ========================================
   PHASES (Étape 2)
   ======================================== */

.tutorial__phases {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin: 1.5rem 0;
}

.tutorial__phase-item {
  display: flex;
  gap: 1rem;
  padding: 1rem;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.05);
  border: 2px solid rgba(255, 255, 255, 0.1);
  transition: all 0.3s ease;
}

.tutorial__phase-item:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.tutorial__phase-item--charge {
  border-color: rgba(59, 130, 246, 0.3);
  background: rgba(59, 130, 246, 0.05);
}

.tutorial__phase-item--decharge {
  border-color: rgba(16, 185, 129, 0.3);
  background: rgba(16, 185, 129, 0.05);
}

.tutorial__phase-icon {
  font-size: 2rem;
  min-width: 3rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

.tutorial__phase-info {
  flex: 1;
}

.tutorial__phase-info strong {
  display: block;
  font-size: 1.1rem;
  margin-bottom: 0.5rem;
  color: var(--color-text-primary, #ffffff);
}

.tutorial__phase-info p {
  margin: 0;
  color: var(--color-text-secondary, rgba(255, 255, 255, 0.8));
  font-size: 0.95rem;
  line-height: 1.5;
}

.tutorial__phase-arrow {
  text-align: center;
  font-size: 1.5rem;
  padding: 0.5rem 0;
  opacity: 0.6;
}

/* ========================================
   EXEMPLES (Étape 4)
   ======================================== */

.tutorial__example {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin: 1.5rem 0;
}

.tutorial__example-step {
  display: flex;
  gap: 1rem;
  padding: 1rem;
  background: rgba(255, 255, 255, 0.03);
  border-radius: var(--radius-sm);
  border-left: 4px solid rgba(59, 130, 246, 0.5);
  transition: all 0.3s ease;
}

.tutorial__example-step:hover {
  background: rgba(255, 255, 255, 0.05);
  border-left-color: rgba(59, 130, 246, 0.8);
}

.tutorial__example-icon {
  font-size: 1.8rem;
  min-width: 3rem;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 0.2rem;
}

.tutorial__example-content {
  flex: 1;
}

.tutorial__example-content strong {
  display: block;
  margin-bottom: 0.5rem;
  color: var(--color-text-primary, #ffffff);
  font-size: 1.05rem;
}

.tutorial__example-content p {
  margin: 0.5rem 0;
  line-height: 1.6;
}

.tutorial__example-arrow {
  text-align: center;
  font-size: 1.5rem;
  opacity: 0.5;
  margin: 0.5rem 0;
}

/* Dés dans l'exemple */
.tutorial__demo-dice {
  display: flex;
  gap: 0.75rem;
  justify-content: center;
  margin: 1rem 0;
}

.tutorial__demo-die {
  width: 3rem;
  height: 3rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  font-weight: bold;
  background: linear-gradient(145deg, #ffffff, #e0e0e0);
  color: #1a1a1a;
  border-radius: 8px;
  box-shadow: 
    0 2px 8px rgba(0, 0, 0, 0.2),
    inset 0 1px 2px rgba(255, 255, 255, 0.8);
  transition: transform 0.2s ease;
}

.tutorial__demo-die:hover {
  transform: scale(1.05) rotate(5deg);
}

/* ========================================
   COMBINAISONS (Étape 3)
   ======================================== */

.tutorial__combinations {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin: 1rem 0;
}

.tutorial__combo-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.875rem 1rem;
  background: rgba(255, 255, 255, 0.03);
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  transition: all 0.2s ease;
}

.tutorial__combo-item:hover {
  transform: translateX(5px);
  background: rgba(255, 255, 255, 0.05);
}

.tutorial__combo-name {
  font-weight: 600;
  font-size: 0.95rem;
}

.tutorial__combo-value {
  font-size: 0.875rem;
  color: var(--color-text-dim, rgba(255, 255, 255, 0.7));
  font-weight: 500;
}

/* Badge "mauvaise combinaison" pour la Nénette */
.tutorial__combo-item--bad {
  opacity: 0.8;
  border-color: rgba(239, 68, 68, 0.2);
  background: rgba(239, 68, 68, 0.05);
}

.tutorial__combo-item--bad .tutorial__combo-name {
  color: #ef4444;
}

.tutorial__combo-item--bad .tutorial__combo-value {
  color: rgba(239, 68, 68, 0.8);
}

/* ========================================
   PRÊT À JOUER (Étape 5)
   ======================================== */

.tutorial__ready {
  text-align: center;
  padding: 2rem;
  background: linear-gradient(135deg, rgba(16, 185, 129, 0.1), rgba(59, 130, 246, 0.1));
  border-radius: 16px;
  border: 2px dashed rgba(16, 185, 129, 0.3);
  margin: 1.5rem 0;
}

.tutorial__ready p {
  margin: 0;
}

/* ========================================
   ANIMATIONS
   ======================================== */

/* @keyframes slideInUp — definition morte retiree (A9) : une definition plus bas dans le
   fichier (l.7452 avant retraits) gagnait deja pour TOUT le document, donc
   celle-ci ne s'appliquait nulle part. Le rendu est inchange. */

/* Animation pour les phases */
.tutorial__phase-item {
  animation: uiEntreeParLeBas 0.4s ease-out;
}

.tutorial__phase-item:nth-child(2) {
  animation-delay: 0.1s;
}

/* Animation pour les étapes d'exemple */
.tutorial__example-step {
  animation: uiEntreeParLeBas 0.4s ease-out;
}

.tutorial__example-step:nth-child(3) {
  animation-delay: 0.1s;
}

.tutorial__example-step:nth-child(5) {
  animation-delay: 0.2s;
}

/* ========================================
   RESPONSIVE - OPTIMISÉ MOBILE v1.1
   ======================================== */

@media (max-width: 640px) {
  /* Réduire les marges globales du tutoriel sur mobile */
  .tutorial__phases,
  .tutorial__example {
    margin: 1rem 0;
  }
  
  /* Phases - Optimisé mobile */
  .tutorial__phase-item {
    flex-direction: column;
    text-align: center;
    padding: 1.25rem 1rem;
    gap: 0.75rem;
  }
  
  .tutorial__phase-icon {
    font-size: 2.8rem;
    min-width: auto;
  }
  
  .tutorial__phase-info strong {
    font-size: 1.15rem;
    margin-bottom: 0.75rem;
  }
  
  .tutorial__phase-info p {
    font-size: 0.9rem;
    line-height: 1.6;
  }
  
  /* Flèche entre les phases plus petite */
  .tutorial__phase-arrow {
    font-size: 1.2rem;
    padding: 0.25rem 0;
  }
  
  /* Exemples - Optimisé mobile */
  .tutorial__example-step {
    flex-direction: column;
    text-align: center;
    padding: 1.25rem 1rem;
    gap: 0.75rem;
  }
  
  .tutorial__example-icon {
    align-items: center;
    font-size: 2.2rem;
    min-width: auto;
    padding-top: 0;
  }
  
  .tutorial__example-content strong {
    font-size: 1rem;
    margin-bottom: 0.75rem;
  }
  
  .tutorial__example-content p {
    font-size: 0.9rem;
    line-height: 1.6;
    margin: 0.6rem 0;
  }
  
  /* Flèche entre exemples plus petite */
  .tutorial__example-arrow {
    font-size: 1.2rem;
    margin: 0.25rem 0;
  }
  
  /* Dés plus grands et mieux espacés sur mobile */
  .tutorial__demo-dice {
    gap: 1rem;
    margin: 1.25rem 0;
  }
  
  .tutorial__demo-die {
    width: 3.5rem;
    height: 3.5rem;
    font-size: 1.75rem;
    border-radius: var(--radius-sm);
  }
  
  /* Section "Prêt à jouer" optimisée mobile */
  .tutorial__ready {
    padding: 1.5rem 1rem;
    margin: 1rem 0;
  }
  
  .tutorial__ready p:first-child {
    font-size: 2.5rem !important;
  }
  
  /* Combinaisons plus lisibles sur mobile */
  .tutorial__combo-item {
    padding: 1rem 0.875rem;
    gap: 0.5rem;
  }
  
  .tutorial__combo-name {
    font-size: 1rem;
  }
  
  .tutorial__combo-value {
    font-size: 0.9rem;
    text-align: right;
  }
  
  /* Désactiver les animations sur mobile pour de meilleures performances */
  .tutorial__phase-item,
  .tutorial__example-step {
    animation: none;
  }
}

/* ========================================
   THÈME SOMBRE (si applicable)
   ======================================== */

[data-theme="dark"] .tutorial__demo-die {
  background: linear-gradient(145deg, #f0f0f0, #d0d0d0);
  color: #1a1a1a;
  box-shadow: 
    0 2px 8px rgba(0, 0, 0, 0.1),
    inset 0 1px 2px rgba(255, 255, 255, 0.6);
}

[data-theme="dark"] .tutorial__phase-item {
  background: rgba(255, 255, 255, 0.02);
  border-color: rgba(255, 255, 255, 0.08);
}

[data-theme="dark"] .tutorial__example-step {
  background: rgba(255, 255, 255, 0.02);
  border-left-color: rgba(59, 130, 246, 0.4);
}

/* ========================================
   NOUVELLES ANIMATIONS 421
   ======================================== */

/* Animation bounce des dés au lancer */
@keyframes roll-bounce {
  0% { transform: translateY(0) rotate(0deg); }
  20% { transform: translateY(-80px) rotate(180deg) scale(1.1); }
  40% { transform: translateY(0) rotate(360deg) scale(0.95); }
  50% { transform: translateY(-40px) rotate(540deg); }
  65% { transform: translateY(0) rotate(720deg) scale(0.98); }
  75% { transform: translateY(-20px) rotate(900deg); }
  85% { transform: translateY(0) rotate(1080deg) scale(0.99); }
  95% { transform: translateY(-10px) rotate(1260deg); }
  100% { transform: translateY(0) rotate(1440deg) scale(1); }
}

/* La culbute `roll-bounce` n'est plus souhaitee — decision du proprietaire.
   A noter : elle ne s'executait DEJA jamais. La classe `rolling-bounce` n'est
   posee nulle part, ni dans lib/, ni dans app.js, ni dans index.html — verifie.
   Elle n'apparait que dans le catalogue ANIMATIONS.html. Cette regle etait donc
   du code mort ; la desactiver ne change rien a ce qui s'affiche aujourd'hui.
   Pour la retablir il faudrait, en plus de decommenter ci-dessous, poser la
   classe depuis ui-manager.js comme le fait deja `die--rolling`.
.die.rolling-bounce {
  animation: roll-bounce 1.2s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
*/

/* Animation dorée pour le 421 */
@keyframes golden-pulse {
  0%, 100% { 
    box-shadow: 0 0 20px var(--color-mocha-light), 0 0 40px var(--color-mocha-light); 
    transform: scale(1);
    filter: brightness(1.2);
  }
  50% { 
    box-shadow: 0 0 30px var(--color-mocha-light), 0 0 60px var(--color-mocha-light); 
    transform: scale(1.05);
    filter: brightness(1.4);
  }
}

.die.golden-glow {
  background: var(--gradient-gold) !important;
  color: white !important;
  animation: golden-pulse 1s ease-in-out 3;
  border-color: var(--color-gold-dark) !important;
}

/* Popup de score */
.score-popup {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(13, 24, 33, 0.98);
  border: 3px solid var(--color-mocha-light);
  border-radius: 20px;
  padding: 25px 30px;
  text-align: center;
  animation: popup-appear 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
  z-index: 10000;
  backdrop-filter: blur(10px);
  box-shadow: 
    0 10px 40px rgba(0, 0, 0, 0.12),
    0 0 60px rgba(168, 144, 128, 0.3);
  pointer-events: none;
  max-width: 85vw;
}

.score-popup__title {
  font-size: 1.6em;
  font-weight: 700;
  background: var(--gradient-gold-text);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  margin-bottom: 8px;
  text-shadow: 0 0 20px rgba(168, 144, 128, 0.5);
  filter: drop-shadow(0 0 10px rgba(168, 144, 128, 0.5));
  white-space: nowrap;
  letter-spacing: 0.05em;
}

.score-popup__points {
  font-size: 1em;
  color: var(--color-text);
  font-weight: 600;
  white-space: nowrap;
}

@keyframes popup-appear {
  0% {
    transform: translate(-50%, -50%) scale(0);
    opacity: 0;
  }
  100% {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
  }
}

/* Animation de sortie du popup */
.score-popup--hiding {
  opacity: 0 !important;
  transform: translate(-50%, -50%) scale(0.8) !important;
  transition: all 0.3s ease !important;
}

/* ========================================
   ANIMATION IA RÉFLÉCHIT
   ======================================== */

.ai-indicator {
  display: inline-flex;
  gap: 12px;
  align-items: center;
  padding: 12px 20px;
  background: rgba(147, 51, 234, 0.2);
  border-radius: 12px;
  border: 2px solid #a855f7;
  margin: 10px 0;
}

.ai-indicator__spinner {
  display: flex;
  gap: 6px;
  align-items: center;
}

.ai-indicator__dot {
  width: 10px;
  height: 10px;
  background: #a855f7;
  border-radius: 50%;
  animation: thinking-pulse 1.4s ease-in-out infinite;
}

.ai-indicator__dot:nth-child(1) {
  animation-delay: 0s;
}

.ai-indicator__dot:nth-child(2) {
  animation-delay: 0.2s;
}

.ai-indicator__dot:nth-child(3) {
  animation-delay: 0.4s;
}

/* ⚠️ CE TEXTE PORTE MAINTENANT DE L'INFORMATION, PLUS SEULEMENT « ça travaille ».
   Depuis que la narration de l'IA y est branchée (lib/ai-narrator.js), on y lit
   ce que l'IA garde et ce qu'elle valide — son contraste cesse d'être cosmétique.
   Mesuré au navigateur sur le fond composité réel du bandeau
   (`rgba(147,51,234,.2)` sur l'écran de jeu, soit `rgb(50,38,87)` au pire) :
   `#a855f7` ne valait que **3,42:1**. Même teinte, même saturation, clarté
   relevée : `#ba78f9` donne **4,60:1**.
   ⚠️ Le versant CLAIR était déjà corrigé en vague 18 (`#6b21a8`, remesuré ici à
   **6,01:1**) — le sombre avait été laissé de côté parce qu'il n'était pas
   l'objet de la demande. `--color-*` n'entre pas en jeu : les deux valeurs sont
   écrites en dur, comme la bordure `#a855f7` qui, elle, est un élément
   graphique et reste au-dessus de son seuil de 3:1. */
.ai-indicator__text {
  color: #ba78f9;
  font-weight: 600;
}

@keyframes thinking-pulse {
  0%, 80%, 100% { 
    transform: scale(0.8); 
    opacity: 0.5; 
  }
  40% { 
    transform: scale(1.05); 
    opacity: 1; 
  }
}

/* ==================== OVERLAYS VICTOIRE/DÉFAITE ==================== */

/* Overlay de victoire */
.victory-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  animation: fade-in 0.3s ease-out;
  cursor: pointer;
}

.victory-content {
  text-align: center;
  animation: victory-zoom 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.victory-content h2 {
  font-size: 4em;
  color: var(--color-mocha-light);
  margin-bottom: 20px;
  text-shadow: 0 0 8px rgba(196, 181, 160, 0.4);
}

.victory-content p {
  font-size: 1.5em;
  color: white;
}

.trophy {
  font-size: 8em;
  animation: trophy-bounce 2s ease-in-out infinite;
}

/* Overlay de défaite */
.defeat-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.75);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  animation: fade-in 0.3s ease-out;
  cursor: pointer;
  overflow: hidden;
}

.defeat-content {
  text-align: center;
  animation: defeat-shake 0.8s ease-out;
  z-index: 1;
}

.defeat-content h2 {
  font-size: 4em;
  color: #ef4444;
  margin-bottom: 20px;
  text-shadow: 0 0 8px rgba(239, 68, 68, 0.4);
}

.defeat-content p {
  font-size: 1.5em;
  color: #cbd5e1;
}

.defeat-content p:last-child {
  font-size: 1.1em;
  color: #B0B8C3;
  margin-top: 10px;
}

.defeat-icon {
  font-size: 8em;
  animation: defeat-drop 1s ease-out;
  filter: grayscale(50%);
}

/* Effet de pluie pour la défaite */
.rain {
  position: absolute;
  width: 2px;
  height: 50px;
  background: linear-gradient(transparent, rgba(59, 130, 246, 0.5));
  animation: rain-fall 1s linear infinite;
}

/* Animations des overlays */
@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes victory-zoom {
  0% {
    transform: scale(0);
    opacity: 0;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes trophy-bounce {
  0%, 100% { transform: translateY(0) rotate(-5deg); }
  50% { transform: translateY(-20px) rotate(5deg); }
}

@keyframes defeat-shake {
  0%, 100% { transform: translateX(0); }
  10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
  20%, 40%, 60%, 80% { transform: translateX(10px); }
}

@keyframes defeat-drop {
  0% {
    transform: translateY(-100vh) rotate(0deg);
    opacity: 0;
  }
  60% {
    transform: translateY(20px) rotate(180deg);
    opacity: 1;
  }
  80% {
    transform: translateY(-10px) rotate(180deg);
  }
  100% {
    transform: translateY(0) rotate(180deg);
  }
}

@keyframes rain-fall {
  0% {
    transform: translateY(-100vh);
  }
  100% {
    transform: translateY(100vh);
  }
}

/* Responsive pour overlays */
@media (max-width: 768px) {
  .victory-content h2,
  .defeat-content h2 { 
    font-size: 2.5em; 
  }
  .trophy,
  .defeat-icon { 
    font-size: 5em; 
  }
  .victory-content p,
  .defeat-content p {
    font-size: 1.2em;
  }
}
/* ✅ P0-004 : Screen-reader only class */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/* ✅ P0-003 : Tap targets WCAG AA minimum 44×44px */
button,
a,
.btn,
.clickable,
.header__btn {
  min-width: var(--min-touch-target);
  min-height: var(--min-touch-target);
}

/* ✅ P1-004 : Focus visible ultra-marqué pour navigation clavier */
*:focus-visible {
  outline: 3px solid var(--color-mocha-light);
  outline-offset: 3px;
  box-shadow: 0 0 0 6px rgba(168, 144, 128, 0.3);
  transition: outline 0.2s ease, box-shadow 0.2s ease;
}

button:focus-visible,
a:focus-visible,
.btn:focus-visible {
  transform: scale(1.02);
  z-index: 10;
}

/* Focus visible pour inputs */
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 3px solid var(--color-mocha-light);
  outline-offset: 2px;
  box-shadow: 0 0 0 6px rgba(168, 144, 128, 0.2);
}

/* Skip to main content (ajout futur) */
.skip-to-main {
  position: absolute;
  left: -9999px;
  z-index: 999;
}

.skip-to-main:focus {
  left: 50%;
  transform: translateX(-50%);
  top: 10px;
  background: var(--color-mocha-light);
  color: var(--color-bg);
  padding: 1rem 2rem;
  border-radius: 8px;
  font-weight: bold;
}

/* ✅ P1-005 : Styles pour tutoriel interactif amélioré */
.tutorial__overlay {
  position: fixed;
  inset: 0;
  background: rgba(13, 24, 33, 0.95);
  z-index: 10000;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: uiFondu 0.3s ease;
}

.tutorial__spotlight {
  position: absolute;
  border: 3px solid var(--color-mocha-light);
  border-radius: 12px;
  box-shadow: 0 0 0 9999px rgba(13, 24, 33, 0.9), 
              0 0 30px rgba(168, 144, 128, 0.5),
              inset 0 0 20px rgba(168, 144, 128, 0.2);
  pointer-events: none;
  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 10001;
}

.tutorial__tooltip {
  position: absolute;
  background: linear-gradient(135deg, #1a5a8a 0%, #0d2847 100%);
  color: var(--color-text);
  padding: 1.5rem;
  border-radius: 12px;
  max-width: 400px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12),
              0 0 0 2px var(--color-mocha-light);
  z-index: 10002;
  animation: uiEntreeParLeBas 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

.tutorial__tooltip-arrow {
  position: absolute;
  width: 0;
  height: 0;
  border: 10px solid transparent;
}

.tutorial__tooltip-arrow--top {
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  border-bottom-color: var(--color-primary);
}

.tutorial__tooltip-arrow--bottom {
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border-top-color: var(--color-primary);
}

.tutorial__tooltip-title {
  font-size: 1.25rem;
  font-weight: bold;
  margin-bottom: 0.75rem;
  color: var(--color-mocha-light);
}

.tutorial__tooltip-text {
  font-size: 1rem;
  line-height: 1.6;
  margin-bottom: 1rem;
}

.tutorial__tooltip-actions {
  display: flex;
  gap: 0.75rem;
  justify-content: flex-end;
}

.tutorial__btn {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 8px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s;
  min-width: var(--min-touch-target);
  min-height: var(--min-touch-target);
}

.tutorial__btn--primary {
  background: var(--color-mocha-light);
  color: var(--color-bg);
}

.tutorial__btn--primary:hover {
  background: var(--color-gold-dark);
  transform: scale(1.05);
}

.tutorial__btn--secondary {
  background: transparent;
  color: var(--color-text);
  border: 2px solid var(--color-text-dim);
}

.tutorial__btn--secondary:hover {
  border-color: var(--color-text);
  background: rgba(255, 255, 255, 0.1);
}

/* @keyframes fadeIn — doublon strict retire (A9). Definition unique l.8252 avant retraits. */

/* @keyframes slideInUp — definition morte retiree (A9) : une definition plus bas dans le
   fichier (l.7452 avant retraits) gagnait deja pour TOUT le document, donc
   celle-ci ne s'appliquait nulle part. Le rendu est inchange. */

/* Pulse animation pour éléments à cliquer */
.tutorial__pulse {
  /* Renommee : `pulse` est definie quatre fois et celle de 10085 gagnait — une
     oscillation d'opacite. Cet element-ci attend un ANNEAU qui s'ecarte, produit
     par un box-shadow dont le rayon d'etalement passe de 0 a 20 px. C'est le cas
     signale par le document de reprise, ici confirme et corrige. */
  animation: tutorielAnneauPulse 2s infinite;
}

@keyframes tutorielAnneauPulse {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(168, 144, 128, 0.7);
  }
  50% {
    box-shadow: 0 0 0 20px rgba(168, 144, 128, 0);
  }
}

/* ✅ P1-006 : Écran Welcome (RGPD + Langue) */
/* ==================== ÉCRAN WELCOME MODERNE 2025-2026 ==================== */

.welcome-screen {
  position: fixed;
  inset: 0;
  z-index: 99999;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: uiFondu 0.5s ease;
}

.welcome-screen[hidden] {
  display: none !important;
}

.welcome-screen__overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(13, 24, 33, 0.98) 0%, rgba(26, 35, 50, 0.98) 100%);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}

.welcome-screen__content {
  position: relative;
  background: linear-gradient(to bottom, rgba(13, 24, 33, 0.95) 0%, rgba(26, 35, 50, 0.95) 100%);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(168, 144, 128, 0.3);
  border-radius: 24px;
  padding: 2.5rem;
  padding-top: max(2.5rem, calc(var(--safe-area-top) + 1.5rem));
  padding-bottom: max(2.5rem, calc(var(--safe-area-bottom) + 1.5rem));
  max-width: 650px;
  width: 90%;
  max-height: calc(90vh - var(--safe-area-top) - var(--safe-area-bottom));
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3),
              0 0 40px rgba(168, 144, 128, 0.2);
  animation: uiEntreeParLeBas 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Header avec icône SVG */
.welcome-screen__header {
  text-align: center;
  margin-bottom: 2.5rem;
}

.welcome-screen__icon {
  display: inline-flex;
  margin-bottom: 1.5rem;
}

.welcome-screen__icon .welcome-icon {
  width: 64px;
  height: 64px;
  stroke: var(--color-mocha-light);
  filter: drop-shadow(0 4px 12px rgba(168, 144, 128, 0.4));
  animation: diceFloat 3s ease-in-out infinite;
}

@keyframes diceFloat {
  0%, 100% {
    transform: translateY(0) rotate(0deg);
  }
  50% {
    transform: translateY(-10px) rotate(5deg);
  }
}

.welcome-screen__title {
  font-size: 2.25rem;
  font-weight: 700;
  background: var(--gradient-gold-text);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  margin-bottom: 0.75rem;
  line-height: 1.2;
}

.welcome-screen__subtitle {
  color: var(--color-text-dim);
  font-size: 1.1rem;
  font-weight: 400;
}

/* Sections */
.welcome-screen__section {
  margin-bottom: 2rem;
  padding-bottom: 2rem;
  border-bottom: 1px solid rgba(168, 144, 128, 0.15);
}

.welcome-screen__section:last-of-type {
  border-bottom: none;
  margin-bottom: 1.5rem;
}

.welcome-screen__section-title {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--color-mocha-light);
  margin-bottom: 1.5rem;
}

.welcome-screen__section-title .section-icon {
  width: 24px;
  height: 24px;
  stroke: var(--color-mocha-light);
  flex-shrink: 0;
}

/* Grille langue - Réutilise le design language-card */
.language-grid--welcome {
  grid-template-columns: 1fr;
  gap: 0.75rem;
  margin-bottom: 0;
}

@media (min-width: 480px) {
  .language-grid--welcome {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ============================================================
   ÉCRAN D'ACCUEIL COMPACT (premier lancement)
   Objectif : tout tenir en 1 écran, sans scroll interminable.
   Avant : 1872px de contenu = 3,5 écrans à faire défiler sur un
   Android 360x640, avec le bouton d'action invisible.
   Les 8 cartes de langue (82px chacune = 736px) deviennent des
   pastilles compactes : la langue de l'appareil est déjà
   présélectionnée, changer reste à un seul tap.
   ============================================================ */

/* Sections plus resserrées */
.welcome-screen__section {
  margin-bottom: 1.25rem;
  padding-bottom: 1.25rem;
}

.welcome-screen__section-title {
  font-size: 1.05rem;
  margin-bottom: 0.75rem;
}

/* Cartes de langue -> pastilles sur une grille de 4 colonnes */
.language-grid--welcome {
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
}

@media (min-width: 480px) {
  .language-grid--welcome {
    grid-template-columns: repeat(4, 1fr);
  }
}

.language-grid--welcome .language-card {
  flex-direction: column;
  justify-content: center;
  gap: 0.15rem;
  padding: 0.5rem 0.25rem;
  min-height: 56px; /* reste au-dessus de la cible tactile de 44px */
  position: relative;
}

/* Ne garder que le code langue + le nom court */
.language-grid--welcome .language-card__flag {
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.language-grid--welcome .language-card__info {
  align-items: center;
  text-align: center;
}

.language-grid--welcome .language-card__name {
  font-size: 0.7rem;
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* Le nom traduit et la coche pleine largeur n'apportent rien en mode pastille */
.language-grid--welcome .language-card__native {
  display: none;
}

.language-grid--welcome .language-card__check {
  position: absolute;
  top: 2px;
  right: 3px;
  opacity: 0;
}

.language-grid--welcome .language-card--active .language-card__check {
  opacity: 1;
}

/* Cookie Card moderne avec glassmorphism */
.cookie-card {
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-lg);
  padding: 1.5rem;
}

.cookie-card__intro {
  font-size: 1rem;
  margin-bottom: 1.25rem;
  color: var(--color-text);
  font-weight: 500;
}

/* Features avec icônes SVG */
.cookie-features {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

.cookie-feature {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0.875rem;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: var(--radius-md);
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.cookie-feature:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(168, 144, 128, 0.2);
  transform: translateX(4px);
}

.cookie-feature__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: rgba(168, 144, 128, 0.15);
  border-radius: var(--radius-md);
  flex-shrink: 0;
}

.cookie-feature__icon .lucide-icon {
  width: 20px;
  height: 20px;
  stroke: var(--color-mocha-light);
}

.cookie-feature__text {
  font-size: 0.95rem;
  color: var(--color-text);
  line-height: 1.5;
}

/* Toggle Switch moderne iOS-style */
.cookie-toggle {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.125rem;
  background: rgba(168, 144, 128, 0.1);
  border: 1px solid rgba(168, 144, 128, 0.2);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  margin-bottom: 1rem;
}

.cookie-toggle:hover {
  background: rgba(168, 144, 128, 0.15);
  border-color: rgba(168, 144, 128, 0.3);
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(168, 144, 128, 0.2);
}

.cookie-toggle input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.cookie-toggle__slider {
  position: relative;
  width: 52px;
  height: 32px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 32px;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  flex-shrink: 0;
}

.cookie-toggle__slider::before {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 26px;
  height: 26px;
  background: white;
  border-radius: 50%;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

.cookie-toggle input:checked + .cookie-toggle__slider {
  background: linear-gradient(135deg, var(--color-mocha-light), var(--color-mocha));
  box-shadow: 0 0 20px rgba(168, 144, 128, 0.5);
}

.cookie-toggle input:checked + .cookie-toggle__slider::before {
  transform: translateX(20px);
}

.cookie-toggle__label {
  flex: 1;
  font-weight: 600;
  color: var(--color-text);
  font-size: 1rem;
  user-select: none;
}

/* Lien politique avec icône SVG */
.cookie-card__link {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.875rem;
  color: var(--color-mocha-light);
  text-decoration: none;
  font-size: 0.9rem;
  border-radius: var(--radius-md);
  transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  font-weight: 500;
}

.cookie-card__link:hover {
  background: rgba(168, 144, 128, 0.1);
  transform: translateY(-2px);
}

.cookie-card__link .lucide-icon {
  width: 16px;
  height: 16px;
  stroke: currentColor;
}

/* Actions avec bouton moderne */
.welcome-screen__actions {
  text-align: center;
  margin-bottom: 1.25rem;
}

.btn--large {
  padding: 1.125rem 2.5rem;
  font-size: 1.125rem;
  min-width: 240px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  border-radius: var(--radius-lg);
}

.btn--glowing {
  position: relative;
  overflow: hidden;
  animation: pulseGlow 2s ease-in-out infinite;
}

@keyframes pulseGlow {
  0%, 100% {
    box-shadow: 0 4px 20px rgba(168, 144, 128, 0.4);
  }
  50% {
    box-shadow: 0 6px 30px rgba(168, 144, 128, 0.6),
                0 0 40px rgba(168, 144, 128, 0.3);
  }
}

.btn--glowing::before {
  content: '';
  position: absolute;
  top: -50%;
  left: -50%;
  width: 200%;
  height: 200%;
  background: linear-gradient(
    45deg,
    transparent,
    rgba(255, 255, 255, 0.1),
    transparent
  );
  transform: rotate(45deg);
  animation: boutonLueurBalayage 3s linear infinite;
}

@keyframes boutonLueurBalayage {
  0% {
    transform: rotate(45deg) translateX(-100%);
  }
  100% {
    transform: rotate(45deg) translateX(100%);
  }
}

.btn--glowing .lucide-icon {
  width: 20px;
  height: 20px;
  stroke: currentColor;
  z-index: 1;
}

.btn--glowing:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  animation: none;
}

.btn--glowing:disabled::before {
  animation: none;
}

/* Footer avec icône info */
.welcome-screen__footer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  text-align: center;
  font-size: 0.875rem;
  color: var(--color-text-dim);
  line-height: 1.5;
}

.welcome-screen__footer .lucide-icon {
  width: 14px;
  height: 14px;
  stroke: currentColor;
  flex-shrink: 0;
  opacity: 0.7;
}

/* Animations */
/* @keyframes fadeIn — doublon strict retire (A9). Definition unique l.8252 avant retraits. */

@keyframes uiEntreeParLeBas {
  from {
    opacity: 0;
    transform: translateY(30px) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
    transform: scale(1);
  }
  to {
    opacity: 0;
    transform: scale(0.95);
  }
}

/* Responsive */
@media (max-width: 600px) {
  .welcome-screen__content {
    padding: 1.75rem;
    padding-top: max(1.75rem, calc(var(--safe-area-top) + 1rem));
    padding-bottom: max(1.75rem, calc(var(--safe-area-bottom) + 1rem));
    width: 95%;
    max-height: calc(85vh - var(--safe-area-top) - var(--safe-area-bottom));
    border-radius: 20px;
  }
  
  .welcome-screen__title {
    font-size: 1.75rem;
  }
  
  .welcome-screen__subtitle {
    font-size: 1rem;
  }
  
  .welcome-screen__icon .welcome-icon {
    width: 52px;
    height: 52px;
  }
  
  .btn--large {
    min-width: auto;
    width: 100%;
  }
  
  .cookie-toggle {
    padding: 1rem;
  }
  
  .cookie-toggle__label {
    font-size: 0.9rem;
  }
}

/* Amélioration accessibilité et performance */
.cookie-toggle,
.btn--glowing,
.language-card {
  -webkit-tap-highlight-color: rgba(168, 144, 128, 0.2);
}

@media (prefers-reduced-motion: reduce) {
  .welcome-screen__icon .welcome-icon,
  .btn--glowing,
  .btn--glowing::before {
    animation: none !important;
  }
}
/* REPARATION 3/3 — ces deux lignes etaient orphelines : une declaration sans
   selecteur, suivie de l'accolade surnumeraire qui refermait par accident le
   `:root` de la reparation 2. Tant que :root avalait tout, `overscroll-behavior`
   s'appliquait de fait a l'element racine ; on le lui reattribue explicitement
   pour ne rien changer au comportement. */
html {
  /* Évite les problèmes de scroll rubber band */
  overscroll-behavior-y: contain;
}

/* ✅ FIX 4: Header - Éviter le chevauchement avec l'encoche */
@supports (padding-top: env(safe-area-inset-top)) {
  .header {
    /* Padding minimum de 1rem + safe area */
    padding-top: max(1rem, calc(env(safe-area-inset-top) + 0.5rem));
  }
}

/* ✅ FIX 5: Boutons du header - Zone tactile plus accessible */
@media (max-width: 768px) {
  .header__btn--back,
  .header__btn--menu {
    /* Encore plus grand sur petit écran */
    min-width: 56px;
    min-height: 56px;
    font-size: 1.8rem;
  }
  
  .header {
    /* Plus d'espace entre les éléments sur mobile */
    gap: 1rem;
    padding-left: max(1rem, calc(env(safe-area-inset-left) + 0.5rem));
    padding-right: max(1rem, calc(env(safe-area-inset-right) + 0.5rem));
  }
}

/* ✅ FIX 6: Désactiver les effets de zoom sur double-tap iOS */
button,
a,
input[type="checkbox"],
.welcome-screen__lang-btn,
.welcome-screen__checkbox {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* Mais garder un feedback visuel */
.welcome-screen__checkbox:active,
.welcome-screen__lang-btn:active {
  background: rgba(168, 144, 128, 0.15);
}

/* ✅ FIX 7: Amélioration de la visibilité des éléments interactifs */
.welcome-screen__btn {
  /* Shadow plus visible sur iOS */
  box-shadow: 0 6px 24px rgba(168, 144, 128, 0.5);
}

.welcome-screen__btn--primary:not(:disabled):active {
  transform: scale(0.98) translateY(0);
  box-shadow: 0 2px 12px rgba(168, 144, 128, 0.4);
}

/* ✅ FIX 8: Gestion des petits écrans iPhone (SE, Mini) */
@media (max-width: 400px) {
  .welcome-screen__content {
    padding: 1rem;
    width: 98%;
    max-height: calc(80vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));
  }
  
  .welcome-screen__title {
    font-size: 1.3rem;
  }
  
  .welcome-screen__section-title {
    font-size: 1.1rem;
  }
  
  .welcome-screen__btn {
    font-size: 1rem;
    padding: 0.875rem 1.5rem;
  }
}

/* ✅ FIX 9: Empêcher le scroll du body quand la modale est ouverte */
body:has(.welcome-screen) {
  overflow: hidden;
  position: fixed;
  width: 100%;
  height: 100%;
}

/* ✅ FIX 10: Amélioration du contraste pour la lisibilité */
@media (prefers-contrast: more) {
  .welcome-screen__content {
    border-width: 3px;
  }
  
  .welcome-screen__checkbox {
    border: 2px solid rgba(168, 144, 128, 0.3);
  }
}

/* ✅ FIX 11: Support du mode sombre iOS */
@media (prefers-color-scheme: dark) {
  .welcome-screen__overlay {
    background: linear-gradient(135deg, rgba(0, 0, 0, 0.98) 0%, rgba(13, 24, 33, 0.98) 100%);
  }
}

/* ✅ FIX 12: Animations plus fluides sur iOS */
@media (prefers-reduced-motion: no-preference) {
  .welcome-screen__content {
    will-change: transform, opacity;
  }
}

@media (prefers-reduced-motion: reduce) {
  .welcome-screen__content {
    animation: none;
  }
  
  .welcome-screen__icon {
    animation: none;
  }
}

/* ==================== PROGRESSION DU TUTORIEL ==================== */
.tutorial__progress {
  margin-bottom: var(--spacing-lg);
  text-align: center;
}

.tutorial__progress-dots {
  display: flex;
  gap: 0.75rem;
  justify-content: center;
  align-items: center;
}

.tutorial__progress-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.2);
  transition: all 0.3s ease;
}

.tutorial__progress-dot--active {
  background: var(--color-mocha-light);
  transform: scale(1.08);
  box-shadow: 0 0 12px var(--color-mocha-light);
}

.tutorial__progress-dot--completed {
  background: var(--color-success);
  box-shadow: 0 0 8px var(--color-success);
}

/* ==================== CORRECTIONS SCROLL MOBILE TUTORIEL ==================== */

/* S'assurer que le screen du tutoriel ne scroll pas, seulement le contenu */
#tutorialScreen {
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden; /* Important: le parent ne scroll pas */
}

/* Le contenu interne doit pouvoir scroller */
.tutorial__inner {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
}

/* Corrections spécifiques mobile */
@media (max-width: 768px) {
  /* Améliorer la zone tactile pour le scroll */
  .tutorial__content {
    touch-action: pan-y; /* Autorise le scroll vertical uniquement */
    -webkit-tap-highlight-color: transparent;
  }
  
  /* S'assurer que les éléments longs sont bien scrollables */
  .tutorial__demo,
  .tutorial__phases,
  .tutorial__combinations,
  .tutorial__example {
    max-width: 100%;
    overflow: visible;
  }
  
  /* Overlay interactif optimisé mobile */
  .tutorial__tooltip {
    max-width: 90vw;
    left: 5vw !important;
    right: 5vw;
  }
}

/* Pour les très petits écrans */
@media (max-width: 400px) {
  .tutorial__progress-dots {
    gap: 0.5rem;
  }
  
  .tutorial__progress-dot {
    width: 10px;
    height: 10px;
  }
}

/* ==================== INTERFACE MULTIPLAYER AMÉLIORÉE ==================== */
/* ⚠️ Section PURGÉE le 12 août 2026 : ~650 lignes de CSS mort retirées
   (.player-card et toute sa palette, .multiplayer-container/-grid/-help/
   -footer/-autosave/-btn/-quick-btn, leurs @media et leurs @keyframes
   cardEnter/cardExit/fadeInUp/slideInRight). Ces classes venaient de
   lib/multiplayer-enhanced.js, supprimé le 10 août (piège n° 40) : aucun
   HTML ni JS ne les créait plus (piège n° 31, revérifié avant la coupe).
   Ne survivent que les seules classes encore posées par index.html:1020. */

/* Section d'en-tête avec titre et actions rapides */
.multiplayer-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--spacing-md);
  margin-bottom: var(--spacing-md);
}

.multiplayer-header__title {
  font-size: 1.75rem;
  font-weight: 700;
  color: var(--color-mocha-light);
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
}

/* ⚠️ uiFondu N'EST PAS UN RESTE DE LA PURGE : c'est sa SEULE définition du
   fichier, et six règles vivantes l'utilisent (326, 403, 488, 4861, 6951,
   7085 au moment de la coupe). Son dernier voisin local est mort, pas lui. */
@keyframes uiFondu {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* Responsive Design */

@media (max-width: 768px) {
  .multiplayer-header {
    flex-direction: column;
    align-items: stretch;
  }
}

@media (max-width: 480px) {
  .multiplayer-header__title {
    font-size: 1.5rem;
  }
}





/* ==================== FIN INTERFACE MULTIPLAYER AMÉLIORÉE ==================== */

/* ==================== RESPONSIVE V2 ENHANCED ==================== */

/* Mobile phones (portrait) */
@media (max-width: 480px) {
  .game-logo {
    padding: 1rem;
  }
  
  .logo-text {
    font-size: 4rem;
  }
  
  .logo-dice-icon svg {
    width: 40px;
    height: 40px;
  }
  
  .logo-subtitle {
    font-size: 0.85rem;
  }
  
  .home-play-btn {
    font-size: 1rem;
    padding: 0.875rem;
  }
  
  .home-secondary-btn {
    font-size: 0.75rem;
    padding: 0.625rem 0.75rem;
  }
  
  .home-secondary-btn svg {
    width: 16px;
    height: 16px;
  }
}

/* Tablets (portrait) */
@media (min-width: 481px) and (max-width: 768px) {
  .game-logo {
    padding: 1.5rem;
  }
  
  .logo-text {
    font-size: 5rem;
  }
  
  .logo-dice-icon svg {
    width: 56px;
    height: 56px;
  }
  
  .home-bottom-nav {
    max-width: 450px;
  }
}

/* Tablets (landscape) and small desktops */
@media (min-width: 769px) and (max-width: 1024px) {
  .screen--setup {
    padding: 2rem;
  }
  
  .game-logo {
    padding: 2rem;
  }
  
  .logo-text {
    font-size: 6.5rem;
  }
  
  .home-bottom-nav {
    max-width: 500px;
  }
}

/* Large desktops */
@media (min-width: 1025px) {
  .screen--setup {
    padding: 2rem;
  }
  
  .game-logo {
    padding: 2rem;
  }
  
  .logo-text {
    font-size: 7rem;
  }
  
  .home-bottom-nav {
    max-width: 550px;
  }
}

/* Ultra-wide screens */
@media (min-width: 1440px) {
  .logo-text {
    font-size: 8rem;
  }
  
  .home-bottom-nav {
    max-width: 600px;
  }
}

/* Animations optimization for reduced motion */
@media (prefers-reduced-motion: reduce) {
  .logo-number,
  .logo-dice-icon,
  .particle {
    animation: none !important;
  }
  
  .logo-number {
    filter: drop-shadow(0 0 8px rgba(168, 144, 128, 0.2))
            drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
  }
}


/* ================================================
   MOBILE OPTIMIZATIONS + FIX BOUTON HISTORIQUE
   ================================================ */

.ai-level-selector {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 1rem;
  justify-content: center;
}

.ai-level-selector__label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  color: var(--color-mocha-light);
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.9;
}

.ai-level-selector__icon {
  width: 16px;
  height: 16px;
  stroke: var(--color-mocha-light);
}

.ai-level-selector__text {
  color: var(--color-mocha-light);
}

.ai-level-selector__buttons {
  display: flex;
  gap: 0.5rem;
  justify-content: center;
}

.ai-level-btn {
  flex: 1;
  padding: 0.625rem 0.75rem;
  background: rgba(255, 255, 255, 0.08);
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-radius: 0.5rem;
  color: var(--color-text);
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;
  -webkit-tap-highlight-color: transparent;
}

.ai-level-btn:active {
  transform: scale(0.95);
}

.ai-level-btn--active {
  background: var(--gradient-gold);
  border-color: var(--color-mocha-light);
  color: #0d1821;
  box-shadow: 0 4px 12px rgba(168, 144, 128, 0.3);
}

@media (max-width: 480px) {
  .screen--game {
    display: grid;
    grid-template-rows: auto 1fr auto;
    /* ⚠️ `height: 100%` ET SURTOUT PAS `min-height: 100dvh` — signalé en jeu :
       le bouton « Lancer » n'était pas visible sur téléphone.

       Deux erreurs se cumulaient, et il a fallu les deux mesures pour le voir.

       ① `100dvh` était la mauvaise référence. `.screen--game` vit dans
          `main.container`, qui vaut DÉJÀ la hauteur de l'écran moins l'en-tête
          (`flex: 1` dans le shell `body`). Lui demander la hauteur de l'écran
          ENTIER le faisait dépasser de la hauteur de l'en-tête à tout coup.

       ② `min-height` ne PLAFONNE rien. Même corrigé en `min-height: 100%`,
          l'écran continuait de grandir avec son contenu : mesuré à 375×560,
          `.game__info` réclame 325 px à lui seul — plus de la moitié de
          l'écran — et la grille poussait le bloc d'actions à 653 px, soit
          93 px sous le bas du viewport. `main.container` se mettait à défiler
          et le bouton « Lancer » attendait le joueur hors de l'écran.

       ➡️ `height: 100%` impose la contrainte, et `minmax(0, …)` autorise les
       deux premières rangées à se comprimer sous leur contenu — sans quoi
       `auto` et `1fr` refusent de descendre en dessous et la grille déborde
       de nouveau. Le bloc d'actions, en `auto` sur la dernière rangée, est
       alors TOUJOURS au bas de l'écran, quelle que soit la hauteur. */
    height: 100%;
    grid-template-rows: minmax(0, auto) minmax(0, 1fr) auto;
    padding: 0;
  }

  /* Le panneau d'information est ce qui déborde : il défile en interne plutôt
     que de pousser le reste dehors. Sur un grand écran il n'atteint jamais sa
     limite et rien ne change. */
  .game__info {
    overflow-y: auto;
    min-height: 0;
  }
  
  .game__info {
    padding: 0.75rem 1rem;
    padding-top: calc(0.75rem + var(--safe-area-top, 0px));
  }
  
  .game__dice-area {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem;
    flex: 1;
  }
  
  .die {
    min-width: 90px;
    min-height: 90px;
    font-size: 3.5rem;
  }
  
  .game__actions {
    position: sticky;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 1rem;
    padding-bottom: calc(1rem + var(--safe-area-bottom, 0px));
    background: rgba(13, 24, 33, 0.3);
    backdrop-filter: blur(5px);
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
    z-index: 100;
  }
  
  .btn--primary.btn--large,
  .btn--success.btn--large {
    width: 100%;
    min-height: 56px;
    font-size: 1.15rem;
    font-weight: 700;
    padding: 1rem;
    transition: transform 0.1s ease, box-shadow 0.1s ease;
  }
  
  .btn--primary.btn--large:active,
  .btn--success.btn--large:active {
    transform: scale(0.97);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  }
  
  .btn--secondary {
    min-height: 44px;
  }
  
  .ai-level-btn {
    font-size: 0.75rem;
    padding: 0.5rem 0.5rem;
    min-height: 40px;
  }
}


.die__value {
  text-shadow: 
    0 1px 2px rgba(0, 0, 0, 0.1);
}

[data-theme="dark"] .die,
[data-theme="custom"] .die {
  background: #fafafa !important;
  border-color: rgba(13, 24, 33, 0.9) !important;
}

.btn--primary:active,
.btn--success:active {
  transform: scale(0.96);
  filter: brightness(0.9);
}

.btn--secondary::before,
.header__btn::before {
  content: '';
  position: absolute;
  inset: -8px;
  background: transparent;
}

.btn--secondary,
.header__btn {
  position: relative;
}

.header {
  padding-top: var(--safe-area-top, 0px);
}

.game__actions,
.home-bottom-nav {
  padding-bottom: calc(1rem + var(--safe-area-bottom, 0px));
}

.modal__content {
  padding-bottom: calc(2rem + var(--safe-area-bottom, 0px));
}

.die,
.btn--primary,
.btn--success {
  will-change: transform;
  transform: translateZ(0);
}

.die:not(.die--rolling),
.btn:not(:active) {
  will-change: auto;
}

html {
  -webkit-overflow-scrolling: touch;
}

/* Masquer le badge Google AdSense */
.adsbygoogle-noablate {
  display: none !important;
}

/* FORCER l'affichage de la section historique */
#gameActionsSection {
  display: block !important;
  visibility: visible !important;
  opacity: 1 !important;
  min-height: 100px !important;
  overflow: visible !important;
  height: auto !important;
}

#gameActionsSection * {
  visibility: visible !important;
  opacity: 1 !important;
}

#gameActionsSection h3 {
  display: block !important;
  min-height: 30px !important;
  margin-bottom: 1rem !important;
}

#gameActionsSection button {
  display: block !important;
  min-height: 44px !important;
  width: 100% !important;
}
/* La notification de mise a jour PWA a ete RETIREE le 7 aout.
   Elle s'affichait a chaque arrivee apres un deploiement, et elle ne servait
   a rien : `sw.js` appelle deja `skipWaiting()` a l'installation et
   `clients.claim()` a l'activation, donc la nouvelle version prend la main
   toute seule. Le bandeau ne proposait qu'un rechargement immediat, au prix
   d'une interruption a chaque visite. Son texte etait en outre code en dur en
   francais, dans un jeu traduit en huit langues (piege n. 3).
   Le code qui le construisait est parti de update-manager.js. */

/* ============================================================
   🌞 LIGHT MODE - MODE CLAIR COMPLET
   ============================================================ */

body.light-mode {
  /* Variables redéfinies pour le mode clair */
  --color-bg: #f8f6f0;
  --color-bg-light: #ffffff;
  --color-text: #1a1a1a;
  --color-text-dim: #6b6b6b;
  
  /* Glassmorphism mode clair */
  --glass-bg: rgba(255, 255, 255, 0.7);
  --glass-border: rgba(0, 0, 0, 0.1);
  --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
  
  /* Ombres adaptées au clair */
  --shadow-xs: 0 1px 3px rgba(0, 0, 0, 0.08);
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.1);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.12);
  --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.15);
  --shadow-xl: 0 12px 48px rgba(0, 0, 0, 0.2);
  
  /* Background principal */
  background: linear-gradient(to bottom, #ffffff 0%, #f8f6f0 100%);
}

/* ✅ Correctif contraste : noms des joueurs lisibles en mode clair
   (.player-solo__name forçait var(--color-cream) beige sur fond clair) */
body.light-mode .player-solo__name {
  color: var(--color-text);
}

/* HTML & Body mode clair */
body.light-mode html {
  background: linear-gradient(to bottom, #ffffff 0%, #f8f6f0 100%);
}

/* ============================================================
   HEADER MODE CLAIR
   ============================================================ */

/* ⚠️ LE LISERE SOUS L'EN-TETE EST RETIRE — demande du proprietaire le 10 aout,
   « je ne souhaite pas qu'il soit visible ».
   Il n'existait QU'EN THEME CLAIR : mesure en sombre, `border-bottom` vaut
   `0px none`. Le retirer ne touche donc pas le theme sombre, et il n'y a rien
   a y compenser. Le trait valait `rgba(0,0,0,.08)` sur blanc, soit un
   #ebebeb a 1,2:1 — assez pour se voir, pas assez pour separer quoi que ce
   soit. L'ombre portee partait avec lui : garder l'une sans l'autre laisse une
   ligne grise floue, ce qui est precisement l'effet qu'on retire.
   Le fond translucide et le flou restent : c'est eux qui detachent l'en-tete
   du contenu quand la page defile. */
body.light-mode .header {
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-bottom: 0;
  box-shadow: none;
}

body.light-mode .header__title {
  color: #1a1a1a;
}

/* ⚠️ CES DEUX RÈGLES VISAIENT DES CLASSES QUI N'EXISTENT PAS.
   Elles ciblaient `.header__back-btn` et `.header__menu-btn` ; les boutons
   portent `.header__btn--back` et `.header__btn--menu`. Vérifié le 9 août
   2026 : les deux premiers noms apparaissent ZÉRO fois dans index.html.
   Le thème clair de l'en-tête n'a donc JAMAIS été appliqué à ces boutons —
   ils gardaient leur dégradé bleu foncé et leur icône crème du thème sombre,
   posés sur un en-tête devenu blanc.

   C'est le piège n° 28 transposé au CSS : un sélecteur qui ne correspond à
   rien ne produit aucune erreur, aucun avertissement, et ressemble en tout
   point à une règle qui fonctionne. La règle était juste, son intention
   claire — elle ne s'adressait simplement à personne.

   ➡️ Pour toute nouvelle règle de thème : vérifier que le sélecteur MORD,
   pas seulement qu'il est écrit. Le contrôle tient en une ligne :
       document.querySelectorAll('.header__btn--back').length   // doit être > 0
   ⚠️ La bordure faisait partie du même angle mort : `rgba(255,255,255,0.12)`
   est du blanc sur blanc. Elle dessinait le bouton en sombre et disparaissait
   en clair. */
body.light-mode .header__btn--back,
body.light-mode .header__btn--menu {
  color: #1a1a1a;
  background: rgba(0, 0, 0, 0.05);
  border-color: rgba(0, 0, 0, 0.12);
}

/* L'icône hérite de `currentColor`, mais on l'écrit quand même : le SVG est
   injecté par lib/icons.js APRÈS le rendu initial, et une couleur explicite
   évite de dépendre de l'ordre d'injection. */
body.light-mode .header__btn--back svg,
body.light-mode .header__btn--menu svg {
  color: #1a1a1a;
}

body.light-mode .header__btn--back:hover,
body.light-mode .header__btn--menu:hover {
  background: rgba(0, 0, 0, 0.1);
  border-color: rgba(0, 0, 0, 0.2);
}

/* Menu dropdown mode clair */
body.light-mode .header-menu__content {
  background: rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(30px);
  -webkit-backdrop-filter: blur(30px);
  border: 1px solid rgba(0, 0, 0, 0.1);
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.15);
}

body.light-mode .header-menu__item {
  color: #1a1a1a;
}

body.light-mode .header-menu__item:hover {
  background: rgba(0, 0, 0, 0.05);
}

body.light-mode .header-menu__icon svg {
  color: var(--color-mocha-dark);
}

body.light-mode .header-menu__separator {
  background: rgba(0, 0, 0, 0.1);
}

body.light-mode .header-menu__badge {
  background: var(--color-mocha);
  color: white;
}

/* ============================================================
   LOGO & ÉCRAN D'ACCUEIL MODE CLAIR
   ============================================================ */

body.light-mode .screen--setup {
  background: linear-gradient(to bottom, #ffffff 0%, #f8f6f0 100%);
}

body.light-mode .screen--setup::before {
  opacity: 0.04;
  color: rgba(0, 0, 0, 0.15);
}

body.light-mode .game-logo {
  filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.08));
}

/* Logo digits en mode clair - utiliser les variables inline mais adapter le filtre */
body.light-mode .logo-digit {
  filter: drop-shadow(0 0 8px rgba(164, 120, 100, 0.3)) 
          drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
}

body.light-mode .logo-subtitle {
  color: #6b6b6b;
}

body.light-mode .logo-dice-icon svg {
  stroke: var(--color-mocha);
}

/* ============================================================
   BOUTONS MODE CLAIR
   ============================================================ */

/* Bouton principal JOUER */
body.light-mode .home-play-btn {
  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
  box-shadow: 0 8px 24px rgba(59, 130, 246, 0.3);
}

body.light-mode .home-play-btn:hover {
  box-shadow: 0 12px 32px rgba(59, 130, 246, 0.4);
}

/* Boutons de difficulté */
body.light-mode .ai-level-btn {
  background: rgba(0, 0, 0, 0.04);
  border: 2px solid rgba(0, 0, 0, 0.1);
  color: #1a1a1a;
}

body.light-mode .ai-level-btn:hover {
  background: rgba(0, 0, 0, 0.08);
  border-color: rgba(0, 0, 0, 0.15);
}

body.light-mode .ai-level-btn--active {
  background: linear-gradient(135deg, var(--color-mocha) 0%, var(--color-mocha-light) 100%);
  border-color: var(--color-mocha-dark);
  color: white;
  box-shadow: 0 4px 12px rgba(168, 144, 128, 0.3);
}

body.light-mode .ai-level-btn--active .ai-level-icon {
  stroke: white;
}

body.light-mode .ai-level-selector__label {
  color: var(--color-mocha-dark);
}

body.light-mode .ai-level-selector__icon {
  stroke: var(--color-mocha-dark);
}

body.light-mode .ai-level-selector__text {
  color: var(--color-mocha-dark);
}

/* Boutons multijoueur */
body.light-mode .home-multiplayer-btn {
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(0, 0, 0, 0.12);
  color: #1a1a1a;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}

body.light-mode .home-multiplayer-btn:hover {
  background: rgba(255, 255, 255, 1);
  border-color: var(--color-mocha);
  box-shadow: 0 8px 24px rgba(168, 144, 128, 0.2);
}

body.light-mode .home-multiplayer-btn svg {
  stroke: var(--color-mocha);
}

body.light-mode .home-multiplayer-btn--disabled {
  background: rgba(0, 0, 0, 0.03);
  border-color: rgba(0, 0, 0, 0.08);
  color: #9ca3af;
}

body.light-mode .home-multiplayer-btn--disabled svg {
  stroke: #9ca3af;
}

/* Boutons secondaires (Tuto/Stats) */
body.light-mode .home-secondary-btn {
  background: rgba(0, 0, 0, 0.06);
  border: 1px solid rgba(0, 0, 0, 0.1);
  color: #1a1a1a;
}

body.light-mode .home-secondary-btn:hover {
  background: rgba(0, 0, 0, 0.1);
  border-color: rgba(0, 0, 0, 0.15);
}

body.light-mode .home-secondary-btn svg {
  stroke: var(--color-mocha);
}

/* ============================================================
   ÉCRAN DE JEU MODE CLAIR
   ============================================================ */

body.light-mode .screen--game {
  background: linear-gradient(to bottom, #f8f6f0 0%, #ede8df 100%);
}

body.light-mode .game__info {
  color: #1a1a1a;
}

body.light-mode .player__name {
  color: #1a1a1a;
}

body.light-mode .player__score {
  color: #6b6b6b;
}

body.light-mode .player--active .player__name {
  color: #1a1a1a;
  font-weight: 700;
}

/* Dés en mode clair */
body.light-mode .dice {
  background: linear-gradient(145deg, #ffffff 0%, #f5f5f5 100%);
  border: 2px solid rgba(0, 0, 0, 0.15);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1),
              inset 0 -3px 10px rgba(0, 0, 0, 0.05),
              inset 0 3px 10px rgba(255, 255, 255, 0.8);
}

body.light-mode .dice--locked {
  border-color: var(--color-mocha);
  box-shadow: 0 8px 24px rgba(168, 144, 128, 0.3);
}

body.light-mode .dice-dot {
  background: #1a1a1a;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}

/* Boutons d'action du jeu */
body.light-mode .btn {
  background: rgba(255, 255, 255, 0.9);
  color: #1a1a1a;
  border: 1px solid rgba(0, 0, 0, 0.15);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

body.light-mode .btn--primary {
  background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
  color: white;
  border: none;
  box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}

body.light-mode .btn--secondary {
  background: rgba(0, 0, 0, 0.05);
  color: #1a1a1a;
}

body.light-mode .btn:disabled {
  background: rgba(0, 0, 0, 0.03);
  color: #9ca3af;
  border-color: rgba(0, 0, 0, 0.1);
}

/* ============================================================
   MODALES & OVERLAYS MODE CLAIR
   ============================================================ */

body.light-mode .modal {
  background: rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(30px);
  -webkit-backdrop-filter: blur(30px);
  border: 1px solid rgba(0, 0, 0, 0.1);
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.2);
}

body.light-mode .modal__title {
  color: #1a1a1a;
}

body.light-mode .modal__content {
  color: #1a1a1a;
}

body.light-mode .modal__close {
  color: #6b6b6b;
  background: rgba(0, 0, 0, 0.05);
}

body.light-mode .modal__close:hover {
  background: rgba(0, 0, 0, 0.1);
  color: #1a1a1a;
}

/* Overlay de fond */
body.light-mode .screen-overlay {
  background: rgba(255, 255, 255, 0.95);
}

/* ============================================================
   STATISTIQUES MODE CLAIR
   ============================================================ */

body.light-mode .stats-card {
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid rgba(0, 0, 0, 0.1);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}

body.light-mode .stats-card__title {
  color: #1a1a1a;
}

body.light-mode .stats-card__value {
  color: var(--color-mocha);
}

body.light-mode .stats-card__label {
  color: #6b6b6b;
}

/* ============================================================
   TUTORIAL MODE CLAIR
   ============================================================ */

body.light-mode .tutorial-step {
  background: rgba(255, 255, 255, 0.95);
  border: 1px solid rgba(0, 0, 0, 0.1);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}

body.light-mode .tutorial-step__title {
  color: #1a1a1a;
}

body.light-mode .tutorial-step__content {
  color: #1a1a1a;
}

/* ============================================================
   SCROLLBAR MODE CLAIR
   ============================================================ */

body.light-mode ::-webkit-scrollbar {
  background: rgba(0, 0, 0, 0.05);
}

body.light-mode ::-webkit-scrollbar-thumb {
  background: rgba(0, 0, 0, 0.2);
}

body.light-mode ::-webkit-scrollbar-thumb:hover {
  background: rgba(0, 0, 0, 0.3);
}

/* ============================================================
   LOADER MODE CLAIR
   ============================================================ */

body.light-mode .loader {
  background: linear-gradient(135deg, #ffffff 0%, #f8f6f0 100%);
}

body.light-mode .loader__digit {
  background: linear-gradient(135deg, #a47864 0%, #c69c8a 50%, #d4b5a7 100%);
  box-shadow: 0 10px 40px rgba(164, 120, 100, 0.4),
              inset 0 -5px 20px rgba(0, 0, 0, 0.1),
              inset 0 5px 20px rgba(255, 255, 255, 0.5);
}

/* ============================================================
   ANIMATIONS ADAPTÉES MODE CLAIR
   ============================================================ */

body.light-mode .confetti {
  opacity: 0.8;
}

body.light-mode .emoji-celebration {
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.15));
}

/* ============================================================
   RESPONSIVE MODE CLAIR
   ============================================================ */

@media (max-width: 768px) {
  body.light-mode .header {
    background: rgba(255, 255, 255, 0.98);
  }
  
  body.light-mode .home-bottom-nav {
    background: transparent;
  }
}

@media (prefers-color-scheme: light) {
  /* Si l'utilisateur préfère le mode clair système, suggestion visuelle */
  body:not(.light-mode):not(.dark-mode-forced) .header-menu__item#themeToggleBtn::after {
    content: '';
    position: absolute;
    top: 50%;
    right: 0.5rem;
    width: 6px;
    height: 6px;
    background: var(--color-mocha);
    border-radius: 50%;
    transform: translateY(-50%);
  }
}

/* ==================== NETWORK STYLES ==================== */
/* À AJOUTER à la fin de styles.css */

/* Container principal */
.network-container {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(to bottom, #0d1821 0%, #1a2332 100%);
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

/* Écrans */
.network-screen {
  display: none;
  width: 100%;
  max-width: 400px;
  animation: networkFadeIn 0.3s ease;
}

.network-screen.active {
  display: block;
}

.network-screen__content {
  background: rgba(168, 144, 128, 0.08);
  border-radius: 1rem;
  padding: 2rem;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(196, 181, 160, 0.2);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
}

@keyframes networkFadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Titre */
.network-title {
  font-size: 1.5rem;
  font-weight: 600;
  text-align: center;
  margin-bottom: 1.5rem;
  color: var(--color-mocha-light, #C4B5A0);
}

/* Boutons menu */
.network-menu-buttons {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

/* Boutons */
.network-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 1rem 1.5rem;
  border-radius: 0.75rem;
  font-size: 1rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  border: none;
  width: 100%;
}

/* Voir le commentaire de `--color-mocha-plein` en tête de feuille : le mocha
   d'accent sous du texte blanc ne donnait que 3,01:1. */
.network-btn--primary {
  background: linear-gradient(135deg, var(--color-mocha-plein, #8a705e) 0%, var(--color-mocha-dark, #6B5D53) 100%);
  color: white;
  box-shadow: 0 4px 16px rgba(168, 144, 128, 0.3);
}

/* ⚠️ LE SURVOL ÉCLAIRCISSAIT LE FOND SOUS DU TEXTE BLANC — il était donc PIRE
   que l'état au repos : blanc sur `--color-mocha-light` (#C4B5A0) ne vaut que
   **2,01:1**. Un état de survol est une variante à part entière (piège n° 37).
   On garde l'effet « le bouton s'anime » par l'élévation et l'ombre, et le
   dégradé s'ÉCLAIRCIT d'un cran à l'intérieur de la plage lisible : les deux
   extrémités passent à `--color-mocha-plein`, donc le bouton s'éclaircit
   globalement (son extrémité sombre remonte de #6B5D53 à #8a705e) sans jamais
   descendre sous 4,61:1. */
.network-btn--primary:hover:not(:disabled) {
  background: linear-gradient(135deg, var(--color-mocha-plein, #8a705e) 0%, var(--color-mocha-plein, #8a705e) 100%);
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(168, 144, 128, 0.4);
}

.network-btn--primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.network-btn--secondary {
  background: rgba(168, 144, 128, 0.15);
  color: var(--color-mocha-light, #C4B5A0);
  border: 1px solid rgba(196, 181, 160, 0.3);
}

.network-btn--secondary:hover {
  background: rgba(168, 144, 128, 0.25);
  border-color: rgba(196, 181, 160, 0.5);
}

.network-btn--text {
  background: transparent;
  color: var(--color-mocha, #A89080);
  padding: 0.75rem;
}

.network-btn--text:hover {
  color: var(--color-mocha-light, #C4B5A0);
}

/* ⚠️ `--color-error` EST UN ROUGE D'ALERTE, PAS UN ROUGE DE TEXTE.
   `#c85a54` sur le fond du multijoueur (`rgb(37,44,56)`) ne vaut que **3,37:1** —
   c'est le bouton « Quitter » du salon. Même teinte, même saturation, clarté
   relevée : `#d88b87` donne **5,32:1** là, et **4,60:1** sur le fond le plus
   clair du salon (`rgb(50,54,63)`), donc conforme partout où le bouton apparaît.
   ➡️ Volontairement NON corrigé au niveau de `--color-error` : cette variable
   sert aussi de fond de badge et de bordure, où un rouge franc est le bon choix.
   C'est l'emploi en TEXTE qui demandait une nuance, pas la variable. */
.network-btn--danger {
  color: #d88b87;
}

.network-btn--full {
  width: 100%;
}

.network-btn__icon {
  font-size: 1.25rem;
}

/* Formulaires */
.network-form {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  margin-bottom: 1.5rem;
}

.network-form__group {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.network-form__group label {
  font-size: 0.875rem;
  color: var(--color-mocha, #A89080);
}

.network-input,
.network-select {
  padding: 0.875rem 1rem;
  border-radius: 0.5rem;
  border: 1px solid rgba(196, 181, 160, 0.3);
  background: rgba(168, 144, 128, 0.1);
  color: var(--color-text, #f5f5dc);
  font-size: 1rem;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.network-select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23C4B5A0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  padding-right: 2.5rem;
  cursor: pointer;
}

.network-select option {
  background: #1a2332;
  color: var(--color-text, #f5f5dc);
  padding: 0.75rem;
}

.network-input:focus,
.network-select:focus {
  outline: none;
  border-color: var(--color-mocha-light, #C4B5A0);
  box-shadow: 0 0 0 3px rgba(196, 181, 160, 0.2);
}

.network-input--code {
  font-family: 'Monaco', 'Consolas', monospace;
}

/* Indicateur de connexion */
.connection-indicator {
  position: absolute;
  top: 1rem;
  right: 1rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border-radius: 2rem;
  background: rgba(168, 144, 128, 0.15);
  border: 1px solid rgba(196, 181, 160, 0.2);
  font-size: 0.75rem;
  color: var(--color-mocha-light, #C4B5A0);
}

.connection-indicator__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-mocha, #A89080);
}

.connection-indicator--connected .connection-indicator__dot {
  background: var(--color-success, #5da283);
}

.connection-indicator--disconnected .connection-indicator__dot {
  background: var(--color-error, #c85a54);
}

.connection-indicator--error .connection-indicator__dot {
  background: var(--color-warning, #d4a574);
}

/* Loader */
.network-loader {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(13, 24, 33, 0.85);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  z-index: 10;
  color: var(--color-mocha-light, #C4B5A0);
}

.network-loader__spinner {
  width: 40px;
  height: 40px;
  border: 3px solid rgba(196, 181, 160, 0.2);
  border-top-color: var(--color-mocha-light, #C4B5A0);
  border-radius: 50%;
  animation: uiRotation 1s linear infinite;
}

/* @keyframes spin — definition morte retiree (A9) : une definition plus bas dans le
   fichier (l.10100 avant retraits) gagnait deja pour TOUT le document, donc
   celle-ci ne s'appliquait nulle part. Le rendu est inchange. */

/* Lobby */
.lobby-code-section {
  text-align: center;
  margin-bottom: 1.5rem;
}

.lobby-code-label {
  font-size: 0.875rem;
  color: var(--color-mocha, #A89080);
  margin-bottom: 0.75rem;
}

.lobby-code-display {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
}

.lobby-code {
  font-family: 'Monaco', 'Consolas', monospace;
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  color: var(--color-mocha-light, #C4B5A0);
  background: rgba(168, 144, 128, 0.15);
  padding: 0.75rem 1.5rem;
  border-radius: 0.5rem;
  border: 2px dashed rgba(196, 181, 160, 0.4);
}

.lobby-copy-btn {
  background: rgba(168, 144, 128, 0.2);
  border: 1px solid rgba(196, 181, 160, 0.3);
  padding: 0.75rem;
  border-radius: 0.5rem;
  cursor: pointer;
  font-size: 1.25rem;
  transition: all 0.2s ease;
  color: var(--color-mocha-light, #C4B5A0);
}

.lobby-copy-btn:hover {
  background: rgba(168, 144, 128, 0.3);
}

.lobby-copy-btn.copied {
  background: var(--color-success, #5da283);
  border-color: var(--color-success, #5da283);
}

.lobby-counter {
  text-align: center;
  font-size: 0.875rem;
  color: var(--color-mocha, #A89080);
  margin-bottom: 1rem;
}

/* Liste des joueurs */
.lobby-players {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-bottom: 1.5rem;
  max-height: 250px;
  overflow-y: auto;
}

.lobby-player {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  background: rgba(168, 144, 128, 0.1);
  border-radius: 0.5rem;
  border: 2px solid transparent;
  transition: all 0.2s ease;
}

.lobby-player--me {
  border-color: var(--lobby-player-color, var(--color-mocha-light));
  background: rgba(168, 144, 128, 0.15);
}

.lobby-player--ready {
  border-color: var(--color-success, #5da283);
}

.lobby-player__avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 1rem;
  flex-shrink: 0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

.lobby-player__info {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.lobby-player__name {
  font-weight: 600;
  font-size: 0.95rem;
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.lobby-player__you {
  font-weight: 400;
  font-size: 0.8rem;
  opacity: 0.7;
}

/* ⚠️ TROUVÉ PAR LE BALAYAGE, PAS PAR LA LIGNE DE PLAN — et c'est la ligne
   « Prêt » / « Pas prêt » / « Hôte », donc l'information qu'on vient lire dans
   ce salon. `--color-mocha` (#A89080) sur le fond composité réel `rgb(50,54,63)`
   ne vaut que **4,01:1**, dans les DEUX thèmes. `--color-mocha-light` (#C4B5A0)
   y vaut **6,03:1**. Même cause exactement que le nom d'un joueur déconnecté,
   corrigé dans lib/network-ui.js au même moment. */
.lobby-player__status {
  font-size: 0.75rem;
  color: var(--color-mocha-light, #C4B5A0);
  display: flex;
  align-items: center;
  gap: 0.25rem;
}

.lobby-player__ready-icon {
  color: var(--color-success, #5da283);
  font-weight: bold;
}

/* ===== Indicateur "En train de jouer" (3 points clignotants) ===== */
.lobby-player--playing .lobby-player__status::after,
.player-typing-indicator {
  content: '';
  display: inline-flex;
  gap: 3px;
  margin-left: 0.5rem;
}

.typing-dots {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 0.5rem;
}

.typing-dots__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--lobby-player-color, var(--color-mocha-light));
  animation: typingBounce 1.4s ease-in-out infinite;
}

.typing-dots__dot:nth-child(1) {
  animation-delay: 0s;
}

.typing-dots__dot:nth-child(2) {
  animation-delay: 0.2s;
}

.typing-dots__dot:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes typingBounce {
  0%, 60%, 100% {
    transform: translateY(0);
    opacity: 0.4;
  }
  30% {
    transform: translateY(-4px);
    opacity: 1;
  }
}

/* ===== État déconnecté ===== */
.lobby-player--disconnected {
  opacity: 0.5;
  filter: grayscale(70%);
  border-color: var(--color-error, #c85a54) !important;
  border-style: dashed;
  position: relative;
}

.lobby-player--disconnected .lobby-player__avatar {
  filter: grayscale(100%);
  opacity: 0.6;
}

.lobby-player__disconnected-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  color: var(--color-error, #c85a54);
  font-weight: 500;
}

.lobby-player__disconnected-badge svg {
  flex-shrink: 0;
}

.lobby-player__disconnected-icon {
  position: absolute;
  top: -6px;
  right: -6px;
  width: 24px;
  height: 24px;
  background: var(--color-error, #c85a54);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  box-shadow: 0 2px 6px rgba(200, 90, 84, 0.4);
  animation: disconnectedPulse 2s ease-in-out infinite;
}

@keyframes disconnectedPulse {
  0%, 100% {
    transform: scale(1);
    box-shadow: 0 2px 6px rgba(200, 90, 84, 0.4);
  }
  50% {
    transform: scale(1.1);
    box-shadow: 0 2px 12px rgba(200, 90, 84, 0.6);
  }
}

/* ===== 3 points clignotants - Zone de jeu ===== */
.player-typing-dots {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: 0.5rem;
  padding: 4px 8px;
  background: rgba(0, 0, 0, 0.2);
  border-radius: 12px;
}

.player-typing-dots__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--dot-color, var(--color-mocha-light));
  animation: playerTypingBounce 1.4s ease-in-out infinite;
}

.player-typing-dots__dot:nth-child(1) {
  animation-delay: 0s;
}

.player-typing-dots__dot:nth-child(2) {
  animation-delay: 0.2s;
}

.player-typing-dots__dot:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes playerTypingBounce {
  0%, 60%, 100% {
    transform: translateY(0);
    opacity: 0.4;
  }
  30% {
    transform: translateY(-5px);
    opacity: 1;
  }
}

/* ===== Joueur déconnecté - Zone de jeu ===== */
.player-multi--disconnected {
  opacity: 0.5;
  filter: grayscale(60%);
  border: 2px dashed var(--color-error, #c85a54) !important;
  background: rgba(200, 90, 84, 0.1) !important;
}

.player-multi--disconnected .player-multi__avatar {
  filter: grayscale(100%);
  opacity: 0.6;
}

.player-multi--disconnected .player-multi__name {
  text-decoration: line-through;
  opacity: 0.7;
}

.player-multi__disconnected-icon {
  position: absolute;
  bottom: -4px;
  right: -4px;
  width: 16px;
  height: 16px;
  background: var(--color-error, #c85a54);
  color: white;
  border-radius: 50%;
  font-size: 11px;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
  animation: disconnectedPulse 2s ease-in-out infinite;
}

/* ===== Indicateur "En attente" - remplace le bouton Lancer ===== */
.waiting-indicator {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  padding: 1rem 1.5rem;
  background: rgba(168, 144, 128, 0.15);
  border: 1px solid rgba(196, 181, 160, 0.3);
  border-radius: 0.75rem;
  color: var(--color-mocha-light, #C4B5A0);
  font-size: 1rem;
}

.waiting-indicator__text {
  color: var(--color-mocha-light, #C4B5A0);
}

.waiting-indicator__dots {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.waiting-indicator__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--waiting-color, var(--color-mocha-light));
  animation: playerTypingBounce 1.4s ease-in-out infinite;
}

.waiting-indicator__dot:nth-child(1) {
  animation-delay: 0s;
}

.waiting-indicator__dot:nth-child(2) {
  animation-delay: 0.2s;
}

.waiting-indicator__dot:nth-child(3) {
  animation-delay: 0.4s;
}

/* ⚠️ L'ÉTAT « PRÊT » AVAIT ÉCHAPPÉ AU BALAYAGE, PARCE QU'IL FAUT LA CLASSE
   `--ready` SUR LA LIGNE POUR L'OBTENIR — piège n° 37, mot pour mot : un état
   est une variante à part entière, et c'est celui-là qui passe en dernier à
   travers les mailles. Trouvé seulement en créant une VRAIE partie en ligne.
   `--color-success` (#5da283) sur le fond composité du salon : **4,01:1** au
   pire des deux empilements. Même teinte, même saturation, clarté relevée :
   `#6fac91` donne **4,62:1**.
   ➡️ Corrigé ici et non sur `--color-success` : la variable sert aussi de fond
   de badge et de bordure, où le vert franc est juste. */
.lobby-player--ready .lobby-player__status {
  color: #6fac91;
}

/* La coche qui accompagne « Prêt » est du même vert, dans le même composant. */
.lobby-player__ready-icon { color: #6fac91; }

/* Actions du lobby */
.lobby-actions {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

/* Bouton Prêt */
#readyBtn {
  transition: all 0.3s ease;
}

#readyBtn.ready {
  background: var(--color-success, #5da283);
  color: white;
  border-color: var(--color-success, #5da283);
}

/* Responsive */
@media (max-width: 480px) {
  .network-screen__content {
    padding: 1.5rem;
  }

  .lobby-code {
    font-size: 1.5rem;
    padding: 0.5rem 1rem;
  }

  .network-title {
    font-size: 1.25rem;
  }
}

/* Support RTL (Arabe) — le gros du miroir est en fin de feuille, section
   « ARABE — LE MIROIR DE L'INTERFACE ».

   ⚠️ DEUX RÈGLES ONT ÉTÉ RETIRÉES D'ICI LE 10 AOÛT, ET IL NE FAUT PAS LES
   REMETTRE : `[dir="rtl"] .network-btn` et `[dir="rtl"] .lobby-player`
   posaient `flex-direction: row-reverse`. Une rangée flex SE MIROITE DÉJÀ
   SEULE sous `direction: rtl` — ces deux règles la retournaient une SECONDE
   fois et la remettaient donc dans l'ordre LTR. Mesuré au salon, 3 joueurs,
   1280 px : 18 éléments hors miroir avec, 2 sans (et ces 2 sont un artefact
   de sonde, cf. la section de fin). Le « support RTL » existant était le bug.

   Celle qui reste est légitime : un élément en position absolue n'hérite
   d'aucun miroir, il faut donc déplacer son ancre à la main. */
[dir="rtl"] .connection-indicator {
  right: auto;
  left: 1rem;
}

/* ==================== MODAL DEMANDE DE REVANCHE ==================== */

.modal--rematch .modal__content {
  max-width: 400px;
}

.rematch-votes {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 1rem 0;
  padding: 1rem;
  background: var(--glass-bg, rgba(168, 144, 128, 0.1));
  border-radius: 12px;
  border: 1px solid var(--glass-border, rgba(168, 144, 128, 0.2));
}

.rematch-vote {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.5rem 0.75rem;
  border-radius: 8px;
  background: var(--glass-bg, rgba(255, 255, 255, 0.05));
  transition: all 0.3s ease;
}

.rematch-vote--me {
  border: 1px solid var(--color-mocha, #A47764);
}

.rematch-vote__name {
  font-weight: 500;
  color: var(--text-primary-dark, #f5f0eb);
}

/* Light mode */
[data-theme="light"] .rematch-vote__name {
  color: var(--text-primary, #3d3029);
}

.rematch-vote__status {
  font-size: 1.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Icônes SVG Lucide */
.rematch-icon {
  width: 20px;
  height: 20px;
}

.rematch-icon--waiting {
  color: var(--color-mocha, #A47764);
  animation: uiRotation 2s linear infinite;
}

.rematch-icon--accepted {
  color: var(--color-success, #5da283);
}

.rematch-icon--declined {
  color: var(--color-error, #e74c3c);
}

@keyframes uiRotation {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.rematch-vote--waiting {
  opacity: 0.7;
}

.rematch-vote--accepted {
  background: rgba(93, 162, 131, 0.15);
}

.rematch-vote--declined {
  background: rgba(231, 76, 60, 0.15);
}

.rematch-waiting {
  text-align: center;
  color: var(--text-secondary, #6b5c52);
  font-style: italic;
  margin-top: 1rem;
  animation: revanchePulse 2s ease-in-out infinite;
}

@keyframes revanchePulse {
  0%, 100% { opacity: 0.6; }
  50% { opacity: 1; }
}

/* Actions du modal */
.modal--rematch .modal__footer {
  display: flex;
  gap: 0.75rem;
  justify-content: center;
  flex-wrap: wrap;
}

/* Bouton annuler centré */
#rematchWaitingActions {
  justify-content: center;
}

/* Dark mode */
[data-theme="dark"] .rematch-votes {
  background: rgba(0, 0, 0, 0.2);
  border-color: rgba(255, 255, 255, 0.1);
}

[data-theme="dark"] .rematch-vote {
  background: rgba(255, 255, 255, 0.05);
}

[data-theme="dark"] .rematch-vote__name {
  color: var(--text-primary-dark, #f5f0eb);
}

[data-theme="dark"] .rematch-waiting {
  color: var(--text-secondary-dark, #a89080);
}



/* ============================================================
   ÉCRAN D'ACCUEIL — COMPACTION DU BLOC COOKIES
   Placé en fin de fichier pour passer APRÈS les règles d'origine
   (même spécificité : c'est la dernière déclarée qui gagne).
   Le bloc cookies pesait 533px, soit 47% de l'écran d'accueil.
   ============================================================ */
.welcome-screen .cookie-card {
  padding: 0.9rem;
}

.welcome-screen .cookie-card__intro {
  font-size: 0.88rem;
  margin-bottom: 0.55rem;
}

.welcome-screen .cookie-features {
  gap: 0.3rem;
  margin-bottom: 0.75rem;
}

.welcome-screen .cookie-feature {
  gap: 0.55rem;
  padding: 0.35rem 0.55rem;
}

.welcome-screen .cookie-feature__text {
  font-size: 0.8rem;
  line-height: 1.25;
}

.welcome-screen .cookie-feature__icon svg {
  width: 15px;
  height: 15px;
}

.welcome-screen .cookie-toggle {
  padding: 0.5rem 0;
  gap: 0.6rem;
}

.welcome-screen .cookie-card__link {
  font-size: 0.8rem;
  padding: 0.35rem 0;
}

.welcome-screen .welcome-screen__footer {
  font-size: 0.75rem;
  margin-top: 0.5rem;
}

.welcome-screen .welcome-screen__icon svg {
  width: 40px;
  height: 40px;
}

/* CTA toujours visible : l'utilisateur voit immédiatement quoi faire,
   sans devoir deviner la longueur de l'écran ni scroller à l'aveugle. */
.welcome-screen .welcome-screen__actions {
  position: sticky;
  bottom: 0;
  z-index: 2;
  margin: 0 -0.5rem;
  padding: 0.75rem 0.5rem calc(0.5rem + var(--safe-area-bottom, 0px));
  background: linear-gradient(to top,
    rgba(13, 24, 33, 0.97) 0%,
    rgba(13, 24, 33, 0.92) 60%,
    rgba(13, 24, 33, 0) 100%);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

body.light-mode .welcome-screen .welcome-screen__actions {
  background: linear-gradient(to top,
    rgba(248, 246, 240, 0.97) 0%,
    rgba(248, 246, 240, 0.92) 60%,
    rgba(248, 246, 240, 0) 100%);
}

/* Le pied de page passe au-dessus du CTA collant pour ne pas être masqué */
.welcome-screen .welcome-screen__footer {
  order: 99;
}

/* ============================================================
   SÉLECTEUR DE NIVEAU IA — lisibilité
   Trois signaux redondants pour lever toute ambiguïté :
   1. un cerveau (= intelligence artificielle)
   2. une couleur (vert / orange / rouge)
   3. un libellé texte (indispensable en cas de daltonisme,
      la couleur seule ne devant jamais porter l'information)
   ============================================================ */
.ai-level-selector__buttons {
  gap: 0.4rem;
}

.ai-level-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  padding: 0.5rem 0.35rem;
  min-height: 60px; /* au-dessus de la cible tactile de 44px */
}

.ai-level-btn__text {
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.ai-level-btn svg {
  transition: transform 0.2s ease;
}

/* --- Couleurs par niveau (état non sélectionné : teinte discrète) --- */
.ai-level-btn--easy {
  color: #4ade80;
  border-color: rgba(74, 222, 128, 0.35);
  background: rgba(74, 222, 128, 0.10);
}

.ai-level-btn--medium {
  color: #fbbf24;
  border-color: rgba(251, 191, 36, 0.35);
  background: rgba(251, 191, 36, 0.10);
}

.ai-level-btn--hard {
  color: #f87171;
  border-color: rgba(248, 113, 113, 0.35);
  background: rgba(248, 113, 113, 0.10);
}

/* --- État sélectionné : couleur pleine, contraste fort --- */
.ai-level-btn--easy.ai-level-btn--active {
  background: #16a34a;
  border-color: #22c55e;
  color: #ffffff;
  box-shadow: 0 4px 14px rgba(22, 163, 74, 0.45);
}

.ai-level-btn--medium.ai-level-btn--active {
  background: #d97706;
  border-color: #f59e0b;
  color: #ffffff;
  box-shadow: 0 4px 14px rgba(217, 119, 6, 0.45);
}

.ai-level-btn--hard.ai-level-btn--active {
  background: #dc2626;
  border-color: #ef4444;
  color: #ffffff;
  box-shadow: 0 4px 14px rgba(220, 38, 38, 0.45);
}

.ai-level-btn--active svg {
  transform: scale(1.1);
}

/* --- Mode clair : mêmes teintes, assombries pour le contraste --- */
body.light-mode .ai-level-btn--easy {
  color: #15803d;
  background: rgba(22, 163, 74, 0.12);
  border-color: rgba(22, 163, 74, 0.4);
}

body.light-mode .ai-level-btn--medium {
  color: #b45309;
  background: rgba(217, 119, 6, 0.12);
  border-color: rgba(217, 119, 6, 0.4);
}

body.light-mode .ai-level-btn--hard {
  color: #b91c1c;
  background: rgba(220, 38, 38, 0.12);
  border-color: rgba(220, 38, 38, 0.4);
}

body.light-mode .ai-level-btn--active {
  color: #ffffff;
}

/* ============================================================
   AUDIT UX — surcharges ajoutées en fin de fichier (cf. CLAUDE.md §5.6)
   ============================================================ */

/* AUDIT #A03 — zone tactile des boutons radio « Valeur du 421 » (Paramètres).
   Mesuré à 26x21 px et 35x21 px : très en dessous des 48 dp recommandés par
   Material Design, et les trois cibles sont contiguës (risque de faux appuis).
   Les deux cases à cocher voisines ne sont PAS concernées : leur <label>
   cliquable fait déjà 303x61 px. */
.options__radios label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  min-width: 48px;
  min-height: 48px;
  padding: 0 0.5rem;
  cursor: pointer;
}

/* ---- BACKLOG #B28 — les modales doivent couvrir l'en-tête ----
   .header et .modal étaient tous deux en z-index: 100. À égalité, l'en-tête
   (plus loin dans le DOM ou empilé après) restait visible AU-DESSUS des modales :
   les boutons « retour » et « menu » débordaient sur Paramètres, Aide, Stats…
   et restaient cliquables, permettant de quitter l'écran par accident. */
.modal {
  z-index: 200;
}

/* ---- BACKLOG #B18 — contrastes sous le seuil WCAG AA (confirmes par axe-core) ----
   Trois cas mesures, impact "serious", presents sur 19 ecrans. */

/* « Moyen » : blanc sur #d97706 = 3,18:1 pour un texte de 11,5 px gras (min 4,5).
   #b45309 remonte le rapport a ~5,2:1 sans changer la teinte orange. */
.ai-level-btn--medium.ai-level-btn--active,
.ai-level-btn--active.ai-level-btn--medium {
  background-color: #b45309;
}

/* « Reinitialiser les stats » : #ef4444 sur #241c25 = 4,4:1 (min 4,5). */
#resetStatsBtn span[data-i18n="stats.reset"],
#resetStatsBtn {
  color: #f87171;
}

/* « Classique : » et les autres intitules gras des encarts d'aide :
   #3b82f6 sur #122336 = 4,32:1 (min 4,5). */
.options__hint strong {
  color: #60a5fa;
}

/* ---- BACKLOG #B19 — cibles tactiles portees a 48 px ----
   Une vingtaine de controles mesuraient 44x44 : conforme aux 44 pt d'Apple mais
   sous le minimum de 48 dp de Material Design, donc juge trop petit pour Android.
   Concerne l'en-tete, les fermetures de modales, les onglets Stats et Aide. */
.header__btn,
.modal__close,
.help-tab,
[id^="statsTab_"],
.lobby-copy-btn {
  min-width: 48px;
  min-height: 48px;
}

/* ---- BACKLOG #B29 — infobulles « ? » lisibles en theme sombre ----
   Le contenu s'affichait en fond clair avec texte sombre au milieu d'une
   interface sombre, et chevauchait les boutons radio voisins. */
.tooltip-content {
  background: #1a2332;
  color: #f5f5dc;
  border: 1px solid rgba(245, 245, 220, 0.18);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
}

/* ---- BACKLOG #B28 / #B19 — 2e tentative : priorite explicite ----
   La 1re tentative (regles simples en fin de fichier) etait perdue dans la
   cascade : styles.css fait 220 Ko et des regles concurrentes plus specifiques
   fixaient z-index et dimensions. !important est ici assume et documente,
   conformement a la mise en garde de CLAUDE.md sur la cascade. */
.modal {
  z-index: 200 !important;
}

.header__btn,
.modal__close,
.help-tab,
[id^="statsTab_"],
.lobby-copy-btn {
  min-width: 48px !important;
  min-height: 48px !important;
}

/* ---- BACKLOG #V05 / #B19 — zone tactile des cases a cocher des Parametres ----
   La case « Sons du jeu » ajoutee avec le son ne faisait que 100x21 px de zone
   cliquable, sous le minimum de 48 dp. On aligne toutes les cases a cocher des
   Parametres sur la meme regle. Les deux cases preexistantes faisaient deja
   303x61 : min-height ne les reduit pas. */
.options__checkbox {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 48px;
  width: 100%;
  cursor: pointer;
}

/* ---- BACKLOG #B09 — indicateur « a qui le tour » ----
   Discret mais lisible, place sous le badge de phase. */
.game__turn {
  margin: 0.35rem auto 0;
  padding: 0.3rem 0.85rem;
  border-radius: 999px;
  font-size: 0.9rem;
  font-weight: 600;
  text-align: center;
  width: fit-content;
  max-width: 92%;
  color: #f5f5dc;
  background: rgba(245, 245, 220, 0.09);
  border: 1px solid rgba(245, 245, 220, 0.16);
}
.game__turn[hidden] { display: none; }

/* ---- BACKLOG #V06 — cibles tactiles restantes portees a 48 px ----
   La regle #B19 avait traite l'en-tete, les fermetures de modales et les onglets.
   Restaient les controles du tutoriel (lecture/pause, precedent, suivant, fermer,
   mesures a 44x44) et les deux boutons secondaires de l'accueil (151x44). */
.tutorial-btn,
.home-secondary-btn {
  min-width: 48px !important;
  min-height: 48px !important;
}

/* ---- BACKLOG #B27 — l'ecran d'accueil doit rester atteignable jusqu'au bout ----
   Constat : la barre d'action collante (.welcome-screen__actions) recouvrait la
   fin du bloc cookies, dont la case « J'accepte » — et l'ecran ne defilait pas.
   Or le bouton « Commencer a jouer » reste DESACTIVE tant que cette case n'est
   pas cochee : sur un ecran de 390x844, le joueur ne pouvait pas demarrer le jeu.
   On rend l'ecran defilable et on reserve la hauteur de la barre collante. */
.welcome-screen {
  overflow-y: auto !important;
  overscroll-behavior: contain;
  padding-bottom: 5.5rem !important;
}
.welcome-screen__actions {
  position: sticky;
  bottom: 0;
}

/* ---- BACKLOG #V08 — bandeau de consentement non bloquant ----
   Il remplace la case a cocher obligatoire de l'ecran de bienvenue, qui
   empechait de lancer une partie. Pose en bas, il ne recouvre aucune action :
   z-index sous les modales (200) et marge basse respectant la zone sure iOS. */
.consent-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 150;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.6rem 1rem;
  padding: 0.9rem 1rem calc(0.9rem + env(safe-area-inset-bottom, 0px));
  background: #12202e;
  border-top: 1px solid rgba(245, 245, 220, 0.16);
  box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.4);
}
.consent-banner[hidden] { display: none; }

.consent-banner__text {
  margin: 0;
  flex: 1 1 240px;
  font-size: 0.86rem;
  line-height: 1.35;
  color: #d8d4c8;
}
.consent-banner__actions {
  display: flex;
  gap: 0.6rem;
  flex: 0 0 auto;
}
.consent-banner__btn {
  min-height: 48px !important;
  min-width: 96px !important;
  padding: 0 1rem;
  border-radius: 10px;
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
  color: #f5f5dc;
  background: transparent;
  border: 1px solid rgba(245, 245, 220, 0.32);
}
.consent-banner__btn--primary {
  color: #0d1821;
  background: #e8d5c4;
  border-color: #e8d5c4;
}
.consent-banner__link {
  flex: 1 1 100%;
  text-align: center;
  font-size: 0.78rem;
  color: #a8bdd0;
}

/* ---- BACKLOG #V07 — dernier contraste sous le seuil AA ----
   #3b82f6 sur #122336 = 4,32:1 pour un gras de 14 px (minimum 4,5).
   La regle .options__hint strong de #B18 perdait la cascade face a une regle
   plus specifique portant l'id. On remonte donc la specificite. */
#variantDescription .options__hint strong,
#modeDescription .options__hint strong,
.options__hint strong {
  color: #60a5fa !important;
}

/* ---- Badge de score rogne en hauteur ----
   Signale par le joueur : le badge affichant la valeur du coup (ex. « 10 » pour
   un 421) etait coupe en haut et en bas. Cause : .game__dice-area est un flex en
   colonne et le badge gardait flex-shrink: 1 — des que la hauteur manquait, il
   etait comprime SOUS son min-height et son contenu se retrouvait rogne.
   On lui interdit de retrecir. */
.current-score-display {
  flex-shrink: 0;
  overflow: visible;
}

/* ---- BACKLOG #B32 — donner de la presence au logo « 421 » ----
   Mesure prealable dans le navigateur, avant toute modification : le logo n'a
   JAMAIS ete un defaut d'accessibilite. Son degrade va de #C4B5A0 a #A89080 sur
   un fond #0D1821 -> #1A2332, soit 5,24:1 au point le plus sombre pour un texte
   de 72 px en gras — le seuil AA du grand texte est de 3,0. Le constat d'origine
   (« beige delave sur bleu nuit ») est donc esthetique, pas normatif : le logo
   manque de presence, il n'est pas illisible.

   On remonte le degrade dans la MEME famille beige, sans introduire de teinte
   nouvelle : #F5F5DC est deja la couleur de texte du jeu, #C4B5A0 est
   --color-mocha-light. Le point le plus sombre passe de #A89080 a #C4B5A0,
   soit 5,24:1 -> 7,87:1 sur le bas du fond.

   Le halo n'est volontairement pas touche : @keyframes shimmer anime `filter` en
   boucle infinie, un filter statique serait ecrase en permanence. Seul le
   degrade est modifie — background-image et non background, pour conserver le
   background-size: 200% auto dont depend l'animation `shine`.

   !important est REQUIS (piege n°10 de CLAUDE.md, dans sa version litterale) :
   sans lui, la regle est bien la derniere de styles.css, bien parsee, de meme
   specificite que celle qu'elle vise — et pourtant la valeur calculee ne bouge
   pas. A noter, la vue CSSOM de styles.css est trompeuse : document.styleSheets
   n'y expose que 473 regles et la regle .logo-digit d'origine (ligne 1012) en
   est absente, alors qu'elle s'applique. Verifiez toujours la valeur CALCULEE. */
.logo-digit {
  background-image: linear-gradient(
    135deg,
    #F5F5DC 0%,
    var(--color-mocha-light) 50%,
    #F5F5DC 100%
  ) !important;
}

/* ---- BACKLOG #V06b — dernieres cibles tactiles sous 48 px ----
   Mesure faite ecran par ecran en 375x812, un seul conteneur visible a la fois,
   en activant chaque etat comme le fait l'application (classe screen--active,
   classe active pour le reseau, display inline pour les modales).

   Le backlog annoncait 61 cibles. Apres verification de la zone cliquable REELLE
   — ce que l'entree demandait explicitement — il n'en restait que 3 familles :
   les 6 cases a cocher et boutons radio comptes par la sonde sont des faux
   positifs. Leur input natif fait bien 13x13, mais il est enveloppe dans un
   <label> de 48 a 72 px de haut, qui est la vraie surface touchee.

   Restent trois cas reels, tous a quelques pixels du seuil :
     .options__select        46 px  (Mode de jeu, Variante)
     .tooltip-trigger        44 px  (min-height figee a 44)
     .consent-banner__link   44 px  (introduit par #V08)

   !important necessaire sur deux des trois, meme piege que #B32 : le CSSOM de
   styles.css est trompeur — document.styleSheets n'expose que 473 regles pour
   11 400 lignes, et affirme qu'AUCUNE autre regle ne pose min-height sur ces
   elements, alors que la valeur calculee reste bloquee a 44 px sans !important.
   Ne jamais conclure depuis document.styleSheets sur ce fichier : mesurer. */
.options__select {
  min-height: 48px;
}

.tooltip-trigger {
  min-height: 48px !important;
}

.consent-banner__link {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 48px !important;
}

/* ---- Nenette annoncee en toutes lettres ----
   Remplace la pluie de 15 emojis qui tombait du haut de l'ecran a chaque 2-2-1.
   Demande explicite du proprietaire, et remise en conformite avec la regle
   « zero emoji » du projet (CLAUDE.md section 4).
   Le libelle vient de t('combos.nenette'), presente dans les 8 langues. */
.nenette-annonce {
  position: fixed;
  top: 38%;
  left: 50%;
  transform: translate(-50%, -50%) scale(1);
  z-index: 9999;
  pointer-events: none;

  padding: 0.7rem 1.6rem;
  border-radius: 14px;
  background: rgba(18, 32, 46, 0.94);
  border: 1px solid rgba(232, 213, 196, 0.35);
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45);

  font-size: clamp(1.6rem, 8vw, 2.6rem);
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  color: #e8d5c4;

  animation: nenetteEntree 260ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

.nenette-annonce--sortie {
  animation: nenetteSortie 300ms ease-in both;
}

@keyframes nenetteEntree {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.82); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

@keyframes nenetteSortie {
  from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  to   { opacity: 0; transform: translate(-50%, -60%) scale(0.94); }
}

/* Le mot reste lisible et centre, seul le mouvement disparait. */
@media (prefers-reduced-motion: reduce) {
  .nenette-annonce,
  .nenette-annonce--sortie {
    animation: none !important;
  }
}

/* ---- Contrastes de la modale de langue, apres reparation des accolades ----
   La reparation a rendu leurs variables de couleur a nouveau definies, et ces
   quatre elements ont donc recupere le gris sourdine #A8A39A prevu par le
   design. Probleme : ce gris est pense pour un fond sombre, or ces elements-la
   reposent sur des fonds BLEUS. Mesure apres reparation, modale de langue seule
   ouverte :
     .language-card__native        #A8A39A sur rgb(26,90,138)  = 2,91:1
     .language-modal__footer-info  #A8A39A sur rgb(59,130,246) = 1,47:1
     .language-modal__info-text    idem                        = 1,47:1
     .language-modal__info-icon    idem                        = 1,47:1
   Le seuil AA est de 4,5:1. Avant reparation ces textes heritaient du beige
   clair faute de variable definie, ce qui etait conforme par accident. On
   retablit donc explicitement le beige : c'est la couleur juste sur ces fonds. */
.language-card__native {
  color: #f5f5dc;   /* sur rgb(26,90,138) : 2,91 -> 6,60:1 */
}

/* Ces trois-la reposent sur un bleu vif rgb(59,130,246). Le beige n'y atteint
   que 3,32:1 — c'etait deja le cas AVANT la reparation, donc un defaut
   preexistant et non une regression. Aucun texte clair ne peut passer 4,5:1 sur
   ce fond ; on inverse donc en texte sombre, comme le fait deja le bouton
   principal sur son fond beige. */
.language-modal__footer-info,
.language-modal__info-text,
.language-modal__info-icon {
  color: #0d1821;
}

/* ---- BACKLOG #B30 — ecran de jeu sur mobile : le bouton dans la zone du pouce ----
   Mesure du constat en 375x812, partie reelle en cours, AVANT modification :
     .game__info        96 -> 410   (314 px)
     .game__dice-area  426 -> 599   (173 px)
     .game__actions    607 -> 695   ( 88 px)
     bas de #rollBtn : 679  ->  133 px de vide sous le bouton principal (16 % de
     la hauteur), et 117 px sous le dernier bloc.
   Le backlog annoncait « un tiers d'ecran vide ». C'est 16 %, pas 33 % — mais le
   fond du constat tient : le bouton s'arrete loin du bord bas, hors de la zone
   naturelle du pouce.

   Cause : #gameScreen est en `display: block`, les trois blocs s'empilent en haut
   et le reliquat tombe en bas, inutilise. .game__actions est pourtant deja en
   `position: sticky; bottom: 0` — mais sticky n'a rien a quoi se coller tant que
   le contenu est plus court que le conteneur.

   POURQUOI CE SELECTEUR, ET PAS .screen--game.screen--active :
   showScreen() (lib/ui-manager.js:191) ne pose PAS la classe screen--active, il
   ecrit un `display` INLINE — toujours exactement 'block' ou 'none'. Verifie en
   direct : pendant une partie, #gameScreen porte style="display: block" et garde
   la classe screen (sans --active), tandis que #setupScreen garde --active avec
   display:none. Une regle visant .screen--active ne s'appliquerait donc jamais ici.
   On cible le style inline : la regle ne mord QUE quand l'ecran est reellement
   affiche, et disparait des que showScreen() repasse a 'none' — l'ecran de jeu ne
   peut pas rester visible par accident.
   !important est indispensable : sans lui, un style inline bat toute feuille.

   GARDE-FOU DE HAUTEUR — min-height: 660px, valeur trouvee a la mesure.
   En dessous, le contenu ne tient plus : la colonne pousse le bloc d'actions sous
   la ligne de flottaison et il faut defiler pour atteindre le bouton. Mesure en
   375x560 : bas du bouton a 636 pour un viewport de 560, soit 76 px hors champ.
   Sous ce seuil on garde donc la mise en page d'origine, ou le sticky maintient le
   bouton visible — au prix d'un recouvrement des des, mais ce defaut-la EXISTE
   DEJA et n'est pas introduit ici.

   Releve complet apres correction (marge = ecart entre le bas des des et le haut
   du bloc d'actions ; vide = espace sous le bouton principal) :
     375x660  marge 48 px   bouton visible   vide 24 px   defilement 12 px
     375x700  marge 62 px   bouton visible   vide 36 px   defilement 0
     375x740  marge 63 px   bouton visible   vide 36 px   defilement 0
     375x812  marge 99 px   bouton visible   vide 36 px   defilement 0
   Le seuil a 660 couvre l'iPhone SE (667 px) et tout ce qui est au-dessus. */
@media (max-width: 768px) and (min-height: 660px) {
  #gameScreen[style*="display: block"] {
    display: flex !important;
    flex-direction: column;
    /* LA VRAIE CAUSE DU VIDE. #gameScreen etait en min-height: 100vh alors qu'il
       vit dans .container, lequel ne fait que 100vh moins l'en-tete (72 px).
       L'ecran depassait donc TOUJOURS son conteneur de 72 px et .container
       (overflow: auto) devait defiler. C'est ce decalage que le sticky des
       actions masquait. On le dimensionne sur son conteneur reel, pas sur le
       viewport : plus de depassement, et margin-top: auto pose alors le bloc
       d'actions exactement au bas de la zone visible. */
    min-height: 100%;
  }

  #gameScreen[style*="display: block"] > .game__info {
    flex: 0 0 auto;
  }

  /* La zone de des absorbe le vide en restant centree : les des respirent au
     milieu au lieu d'aller coller le bas. */
  #gameScreen[style*="display: block"] > .game__dice-area {
    flex: 1 1 auto;
    justify-content: center;
    /* min-height: 0 est INDISPENSABLE. Par defaut un item flex vaut
       min-height: auto et refuse de descendre sous la taille de son contenu :
       mesure en 375x560, il debordait alors de 8 px et les trois des passaient
       SOUS le bloc d'actions, qui est en position: sticky. */
    min-height: 0;
  }

  /* Le bloc d'actions descend dans la zone du pouce, encoche basse iOS respectee.
     position: static est la cle. En sticky, le bloc SORT du calcul de hauteur de
     la colonne : la zone de des se voyait attribuer tout l'espace, actions
     comprises, et les des passaient dessous des que la hauteur se reduisait
     (3 px de marge seulement en 375x740, 2 px en 375x700). En static, la colonne
     lui reserve reellement ses 84 px et le recouvrement devient impossible,
     quelle que soit la hauteur. Le sticky ne servait de toute facon a rien ici :
     il n'a rien a quoi se coller tant que le contenu ne deborde pas. */
  #gameScreen[style*="display: block"] > .game__actions {
    position: static;
    margin-top: auto;
    padding-bottom: max(0.75rem, env(safe-area-inset-bottom, 0px));
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   INFOBULLES DES PARAMETRES — elles etaient affichees en permanence
   ═══════════════════════════════════════════════════════════════════════════
   Constate dans Parametres > Regles personnalisees : les trois infobulles « ? »
   restaient visibles en continu, en boites posees par-dessus les libelles.

   MESURE AVANT CORRECTION (getComputedStyle, modale ouverte) :
     .tooltip-content   display: inline · position: static · opacity: 1
                        541 px, 390 px et 490 px de large, en plein flux
     .tooltip-trigger   background rgb(240,240,240) · border 2px outset
                        = le rendu natif du bouton, aucun style projet

   Il ne manquait donc pas une regle de survol : il manquait TOUT l'etat masque.
   Le systeme d'infobulles n'a jamais eu d'affichage conditionnel. Verifie
   pre-existant : empreinte identique avant et apres la reparation des blocs
   :root du 5 aout, ce bug ne vient pas de la.

   POURQUOI L'ANCRAGE SE FAIT SUR LA LIGNE ET NON SUR LE BOUTON
   .modal__content est en overflow: auto. Une infobulle ancree au bouton et
   centree dessus deborderait a droite pour les boutons de fin de ligne — mesure :
   le 2e « ? » est a x=411 dans une modale de 783 px, une bulle de 480 px sortirait
   de 108 px — et le conteneur repondrait par une barre de defilement horizontale.
   Ancree a la ligne (left: 0 / right: 0 / max-width), elle ne peut deborder sur
   aucun des deux axes, a n'importe quelle largeur d'ecran.
   Contrepartie assumee : sur le <fieldset> « Valeur du 421 », qui fait 122 px de
   haut, la bulle apparait sous les boutons radio et non colle au « ? ». Elle
   reste dans le meme groupe visuel, et c'est le prix de l'absence de rognage.

   L'apparence de la bulle (fond sombre, bordure, ombre) vient du bloc #B29 plus
   haut dans ce fichier et n'est pas redefinie ici. */

.options__label,
.options__checkbox {
  position: relative;
}

/* Zone tactile de 48 px — mais transparente. Le rond visible est porte par
   .tooltip-icon, bien plus petit : un disque de 48 px ecraserait la ligne.
   Le min-height de 48 px vient de #V06b/#B19 et reste en vigueur. */
.tooltip-trigger {
  appearance: none;
  -webkit-appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin-left: 0.15rem;
  font: inherit;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 48px;
  vertical-align: middle;
  flex: 0 0 auto;
  -webkit-tap-highlight-color: transparent;
}

.tooltip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: rgba(232, 213, 196, 0.14);
  border: 1px solid rgba(232, 213, 196, 0.45);
  color: #e8d5c4;
  font-size: 0.76rem;
  font-weight: 700;
  line-height: 1;
  transition: background-color 140ms ease, border-color 140ms ease;
}

.tooltip-trigger:hover .tooltip-icon,
.tooltip-trigger:focus-visible .tooltip-icon {
  background: rgba(232, 213, 196, 0.30);
  border-color: rgba(232, 213, 196, 0.80);
}

.tooltip-trigger:focus-visible {
  outline: 2px solid rgba(232, 213, 196, 0.9);
  outline-offset: 2px;
  border-radius: 10px;
}

/* L'etat masque, celui qui manquait. visibility en plus de opacity : sans elle
   la bulle resterait dans l'ordre de lecture des technologies d'assistance et
   resterait cliquable alors qu'on ne la voit pas. */
.tooltip-content {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 20;
  width: auto;
  max-width: 34rem;
  padding: 0.6rem 0.75rem;
  font-size: 0.82rem;
  font-weight: 400;
  line-height: 1.4;
  text-align: left;
  white-space: normal;
  pointer-events: none;

  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 150ms ease, transform 150ms ease, visibility 0s linear 150ms;
}

/* :focus, et pas seulement :focus-visible — sur mobile il n'y a pas de survol,
   et un appui sur un <button> lui donne le focus. C'est ce qui rend l'infobulle
   ouvrable au doigt sans une seule ligne de JavaScript. */
.tooltip-trigger:hover .tooltip-content,
.tooltip-trigger:focus .tooltip-content {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition-delay: 0s;
}

/* Theme clair. Sans ce bloc l'icone restait beige (#e8d5c4) sur le fond clair
   de la modale (rgb(248,246,240)) : contraste mesure 1,32:1, le « ? » etait
   litteralement invisible. Le projet utilise #1a1a1a pour le texte en clair
   (--color-text) — on s'aligne dessus, ce qui remonte le contraste a ~16:1.
   La bulle elle-meme n'a pas besoin de variante : elle est sombre dans les deux
   themes (bloc #B29), ce qui reste lisible sur fond clair. */
body.light-mode .tooltip-icon {
  background: rgba(26, 26, 26, 0.06);
  border-color: rgba(26, 26, 26, 0.38);
  color: #1a1a1a;
}

body.light-mode .tooltip-trigger:hover .tooltip-icon,
body.light-mode .tooltip-trigger:focus-visible .tooltip-icon {
  background: rgba(26, 26, 26, 0.14);
  border-color: rgba(26, 26, 26, 0.70);
}

body.light-mode .tooltip-trigger:focus-visible {
  outline-color: rgba(26, 26, 26, 0.80);
}


/* ═══════════════════════════════════════════════════════════════════════════
   ANIMATIONS RETENUES PAR LE PROPRIETAIRE — portage du catalogue ANIMATIONS.html
   ═══════════════════════════════════════════════════════════════════════════
   Tous les noms de @keyframes ajoutes ici sont PREFIXES. C'est une regle absolue
   dans ce fichier : 13 noms y sont deja definis plusieurs fois et la derniere
   definition du document gagne pour tout le document — c'est ce qui a fait
   tourner le logo « 421 » a 45 degres, et detourne quatre autres animations. */


/* ---- 14. Atterrissage elastique du de ----
   Remplace la secousse `diceWobble`, retiree a la demande du proprietaire. Sans
   elle le lancer n'avait plus aucun retour visuel : le de changeait de valeur et
   c'est tout. Ici il « se pose », avec un ecrasement puis un rebond.
   La classe est posee par ui-manager.js quand `die--rolling` est retiree. */
/* Amplitude revue apres retour du proprietaire : « l'effet sur les des ne se voit
   pas trop ». La premiere version tombait de 8 px et s'ecrasait de 6 % — sur un de
   de 90 px, l'ecrasement ne faisait que 5 px, invisible juste apres un lancer ou
   l'oeil suit les chiffres qui defilent.
   Desormais : chute de 26 px (29 % de la hauteur du de), ecrasement de 14 %, et un
   second rebond amorti. L'assouplissement passe d'un bezier a depassement a une
   simple deceleration : le rebond est deja porte par les etapes elles-memes, le
   cumul des deux brouillait le mouvement. */
@keyframes deAtterrissage {
  0%   { transform: translateY(-26px) scale(1.12, 0.88); }
  38%  { transform: translateY(0)     scale(0.86, 1.14); }
  56%  { transform: translateY(-9px)  scale(1.06, 0.94); }
  74%  { transform: translateY(0)     scale(0.95, 1.05); }
  88%  { transform: translateY(-3px)  scale(1.02, 0.98); }
  100% { transform: translateY(0)     scale(1); }
}

.die--atterrissage {
  animation: deAtterrissage 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) 1;
}


/* ---- 15. Garder un de : il se souleve au lieu de sauter ----
   Le deplacement existait deja : .die--kept l.2886 pose
   `transform: translateY(-8px) scale(1.05)`. Ce qui manquait, c'est la
   TRANSITION. La regle .die d'origine (l.2813) fait transiter background,
   border, box-shadow et opacity — mais pas transform. Le de sautait donc
   d'un coup a sa position haute.
   Premiere partie du correctif : ajouter transform a la liste des transitions,
   avec une detente elastique.

   ⚠️ SECONDE PARTIE — le soulevement ne marchait PAS DU TOUT, et pas seulement
   « sans transition » comme le supposait le catalogue. Mesure sur un de rendu,
   classe .die--kept posee : transform calcule = matrix(1, 0, 0, 1, 0, 0), soit
   l'identite. La cause est l.8741 :

       .die, .btn--primary, .btn--success { transform: translateZ(0); }

   une promotion GPU, de meme specificite que .die--kept (0,1,0) mais placee
   PLUS BAS dans le fichier : elle gagnait et aplatissait le soulevement. Le de
   garde ne se levait donc que pendant l'appui, ou le !important de la l.2910
   reprenait la main — d'ou l'impression d'un saut instantane qui retombe.

   On retablit le soulevement ici, en fin de fichier, en CONSERVANT le
   translateZ(0) : la promotion GPU etait voulue, et la retirer changerait le
   mode de composition du de. Les deux tiennent dans la meme declaration. */
.die {
  transition: background 0.15s, border 0.15s, box-shadow 0.15s, opacity 0.15s,
              transform 0.26s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.die--kept {
  transform: translateY(-8px) scale(1.05) translateZ(0);
}

/* ...mais l'appui, lui, doit rester instantane : une detente de 260 ms sous le
   doigt donnerait un bouton mou. Voir aussi le point 18 plus bas. */
.die.touching,
.die:active {
  transition: transform 60ms ease-out, box-shadow 60ms ease-out;
}


/* ---- 16. 421 : onde d'eclat ----
   Posee sur les trois des par game-logic.js quand la combinaison est un 421.

   ⚠️ L'onde est portee par ::after, PAS par le de lui-meme, et c'est deliberе.
   Un 421 pose aussi `golden-glow`, et .die.golden-glow (l.6469, specificite
   0,2,0) declare `animation: golden-pulse`. Une regle .die--eclat421 (0,1,0)
   aurait perdu la cascade et l'eclat n'aurait jamais joue — `animation` est une
   seule propriete, elle ne fusionne pas entre regles. Ecrire les deux dans une
   regle combinee n'aurait pas mieux marche : golden-pulse et l'eclat pilotent
   tous deux box-shadow et transform, et la derniere animation declaree l'emporte
   propriete par propriete. Sur ::after, l'onde a sa propre boite et son propre
   box-shadow : les deux effets cohabitent sans se disputer quoi que ce soit.
   ::before est deja pris par la zone de clic etendue (l.2841), ::after est libre. */
@keyframes deEclat421 {
  0%   { box-shadow: 0 0 0 0    rgba(232, 213, 196, 0.85); }
  100% { box-shadow: 0 0 0 34px rgba(232, 213, 196, 0); }
}

.die--eclat421::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  animation: deEclat421 0.9s ease-out 2;
}


/* ---- 18. Retour au toucher sur tous les boutons ----
   C'est le point le plus visible du lot. addTouchEvent() pose la classe
   `.touching` sur une cinquantaine d'elements (event-handlers.js:167), mais
   elle n'etait stylee QUE pour .die : sur tous les autres boutons, l'appui ne
   produisait strictement rien.
   `:active` est ajoute pour le bureau et la souris — `.touching` n'est posee que
   sur mobile, la fonction teste `isMobile` avant d'installer les ecouteurs.
   .die est exclu : il a deja son propre retour, plus marque (scale 0.95).
   .tooltip-trigger aussi : sa zone tactile de 48 px est transparente, la mettre
   a l'echelle ferait sautiller le petit rond « ? » sans rien signifier. */
.touching:not(.die):not(.tooltip-trigger),
button:active:not(.die):not(.tooltip-trigger),
.btn:active,
.header__btn:active,
.ai-level-btn:active,
.mode-card:active,
.language-card:active {
  transform: scale(0.97);
  filter: brightness(0.92);
  transition: transform 60ms ease-out, filter 60ms ease-out;
}


/* ---- 19. Le gain ou la perte de jetons s'envole ----
   animateTokenChange() (ui-manager.js:964) cree deja un element .token-change
   portant « +2 » ou « -3 »... qui n'avait AUCUN style dans ce fichier. Il etait
   donc ajoute au document, invisible, puis retire 1,5 s plus tard.
   Verifie : aucune occurrence de `token-change` dans styles.css avant ce bloc. */
@keyframes jetonFlottant {
  0%   { opacity: 0; transform: translate(-50%, 6px)   scale(0.9); }
  22%  { opacity: 1; transform: translate(-50%, -4px)  scale(1); }
  100% { opacity: 0; transform: translate(-50%, -38px) scale(1); }
}

.token-change {
  position: fixed;
  z-index: 9998;
  pointer-events: none;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
  /* left/top sont poses en ligne par le JS ; translate(-50%) recentre sur le point */
  animation: jetonFlottant 1.4s ease-out forwards;
}

.token-change--gain { color: #6ee7a8; }
.token-change--loss { color: #ff8f8f; }


/* ---- 20. La jauge du pot glisse ----
   La transition de `width` etait ecrite sur .game__pot-fill mais n'a jamais pu
   jouer : ui-manager.js recreait l'element par innerHTML a chaque rendu, et un
   element neuf n'a pas d'ancienne valeur depuis laquelle transiter. Le JS a ete
   corrige pour reutiliser l'element existant ; la transition est declaree ici. */
.game__pot-fill {
  transition: width 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}


/* ---- 21 et 22. Entree des ecrans et ouverture des fenetres ----
   showScreen() et openModal() pilotent par `style.display` en ligne. Une
   TRANSITION est en effet impossible dans ces conditions — display ne se
   transitionne pas. Une ANIMATION, si : quand un element passe de display: none
   a display: block/flex, il est monte a neuf et son animation se rejoue depuis
   le debut. Aucune modification du JS n'est donc necessaire. */
@keyframes ecranEntree {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes modaleEntree {
  from { opacity: 0; transform: translateY(10px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

.screen {
  animation: ecranEntree 0.26s ease-out;
}

.modal {
  animation: ecranEntree 0.18s ease-out;
}

.modal__content {
  animation: modaleEntree 0.26s cubic-bezier(0.34, 1.4, 0.64, 1);
}


/* ---- Bloc balai : mouvement reduit ----
   Le projet comportait neuf blocs prefers-reduced-motion disperses, qui
   laissaient une douzaine d'animations en boucle tourner malgre le reglage.
   Celui-ci est en fin de fichier et couvre TOUT, y compris ce qui sera ajoute
   plus tard : c'est plus sur que d'allonger les neuf blocs existants.

   Les transitions sont ramenees a 1 ms plutot qu'a 0 : un `transitionend` qui
   ne se declenche jamais peut laisser du code en attente indefiniment.

   La classe .animations-reduites est posee sur <html> par lib/animations-reglage.js
   quand le joueur coche « Animations reduites » dans les Parametres. Elle produit
   exactement le meme effet que le reglage systeme. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}

html.animations-reduites *,
html.animations-reduites *::before,
html.animations-reduites *::after {
  animation-duration: 1ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 1ms !important;
  scroll-behavior: auto !important;
}


/* ---- Le de restait cliquable PENDANT son propre lancer ----
   .die--rolling declare `pointer-events: none !important` (l.2911) pour empecher
   de toucher un de en plein tirage. Cette declaration ne s'appliquait jamais :
   `.die { pointer-events: auto !important }` est ecrite DEUX fois, l.2811 et
   l.4464, et la seconde est plus bas dans le fichier que .die--rolling. Meme
   specificite (0,1,0), toutes deux !important : la derniere gagne.

   Mesure sur un de rendu de 90x90, classe .die--rolling posee :
     pointer-events calcule = auto   (au lieu de none)

   Consequence : un joueur pouvait toucher un de pendant qu'il tournait et le
   garder sur une valeur encore en train de changer.

   Corrige en montant la specificite a (0,2,0), ce qui passe devant les deux
   regles `.die`.

   MISE A JOUR (action A8) — le doublon a depuis ete retire. Verification faite
   avant de le supprimer : ses cinq declarations etaient identiques a celles de
   la regle `.die` d'origine, et aucune regle intermediaire ne les redeclarait,
   a une exception pres — `.die--kept { cursor: not-allowed }`, que le doublon
   masquait. Ce `not-allowed` etait faux (un de garde se reclique pour etre
   relache) et a ete retire avec lui, ce qui laisse le rendu inchange.
   La regle ci-dessous devient donc redondante avec `.die--rolling` (l.2929).
   Elle est conservee : elle ne coute rien et protege du retour du meme piege. */
.die.die--rolling {
  pointer-events: none !important;
}


/* ---- Le de en mouvement PENDANT que son chiffre defile ----
   Signalement du proprietaire : « j'observe les numeros bouger puis les des
   bougent, il faut que ce soit en meme temps ».

   C'est un defaut de sequencement introduit en retirant la secousse `diceWobble` :
   le lancer se jouait alors en deux temps nettement separes.
     0 -> 600 ms   les chiffres defilent toutes les 50 ms, sur un de IMMOBILE
     600 ms        `die--rolling` retiree, `die--atterrissage` posee : le de bouge
   Le mouvement arrivait donc APRES le tirage, au lieu de l'accompagner.

   Ce n'est pas un retour de `diceWobble`, ecartee par le proprietaire : celle-ci
   secouait lateralement avec une rotation. Ici le de est simplement EN L'AIR,
   dans un mouvement vertical, ce qui prolonge naturellement l'atterrissage.

   RACCORD SANS RUPTURE. Le cycle part et revient a translateY(-26px), qui est
   exactement l'etape 0 % de `deAtterrissage`. Le de ne saute donc pas au moment
   ou l'on passe d'une animation a l'autre.
   La duree est calee sur le tirage : ui-manager.js fait defiler les chiffres
   pendant animationDuration = 600 ms, soit exactement DEUX cycles de 300 ms. Le
   cycle se termine donc pile a sa fin, en l'air, la ou l'atterrissage commence.
   ⚠️ Si animationDuration change dans ui-manager.js, cette duree doit suivre. */
@keyframes deLancerEnCours {
  0%   { transform: translateY(-26px) scale(1.06, 0.94); }
  50%  { transform: translateY(0)     scale(0.94, 1.06); }
  100% { transform: translateY(-26px) scale(1.06, 0.94); }
}

.die--rolling {
  animation: deLancerEnCours 0.3s ease-in-out infinite;
}


/* ═══════════════════════════════════════════════════════════════════════════
   ECRAN DE JEU SUR GRAND ECRAN — la mise en page mobile n'etait pas plafonnee
   ═══════════════════════════════════════════════════════════════════════════
   Signalement du proprietaire : « la mise en page sur ecran de PC n'est pas tres
   jolie, sur telephone c'est parfait ».

   MESURE AVANT CORRECTION, en 1920x1080 :
     un de              237 x 237 px   (90 x 90 sur telephone)
     une ligne joueur   718 px de large — d'ou le grand vide entre le nom et les jetons
     bouton « Lancer »  752 px de large
     barre du pot       632 px

   CAUSE. `.dice` est en `grid-template-columns: repeat(3, 1fr)` et `.die` porte
   `aspect-ratio: 1` : la grille prend toute la largeur disponible et les des
   deviennent aussi HAUTS que larges. Un bloc @media plafonne bien le de a 90 px
   (l.8647), mais il ne vise que le mobile — il n'existait aucun equivalent pour
   les grands ecrans. La mise en page telephone etait donc simplement etiree.

   PARTI PRIS. Le proprietaire juge le rendu telephone parfait : on le reproduit
   plutot que d'inventer une mise en page PC. La colonne de jeu est ramenee a une
   largeur de telephone et centree. C'est le motif habituel des jeux concus pour
   le mobile et joues au navigateur.

   ⚠️ Seuil a 768 px, et rien en dessous : le rendu telephone n'est pas touche. */
@media (min-width: 768px) {

  /* La colonne de jeu, ramenee a une largeur de telephone et centree.
     Tout ce qu'elle contient — badge de phase, barre du pot, lignes joueurs,
     des, bloc d'actions — se recale automatiquement dessus. */
  #gameScreen {
    max-width: 520px;
    margin-left: auto;
    margin-right: auto;
  }

  /* Les des sont plafonnes explicitement plutot que laisses en 1fr : sans cela
     ils reprendraient toute la largeur de la colonne, soit environ 160 px, ce qui
     reste deux fois la taille du telephone. `justify-content: center` recentre la
     grille, qui devient plus etroite que son conteneur. */
  .dice {
    grid-template-columns: repeat(3, minmax(0, 124px));
    justify-content: center;
  }

  /* Un bouton de 520 px de large pour un libelle de 20 caracteres est
     disproportionne. On le ramene a une largeur de pouce, centre.
     `width: 100%` est necessaire : .game__actions est un flex en COLONNE qui
     centre ses enfants, le bouton se dimensionnait donc sur son texte — 269 px,
     bien plus etroit que sur telephone ou il occupe presque toute la largeur.
     La combinaison des deux donne min(largeur de la colonne, 380 px). */
  #rollBtn,
  .game__actions .btn {
    width: 100%;
    max-width: 380px;
    margin-left: auto;
    margin-right: auto;
  }

  /* L'ecran de fin suit la meme colonne, sans quoi il repasserait a 752 px et le
     contraste entre les deux ecrans serait brutal. */
  #endScreen {
    max-width: 520px;
    margin-left: auto;
    margin-right: auto;
  }
}


/* ---- L'accueil coupait ses deux derniers boutons sur PC ----
   Signalement du proprietaire : « sur l'affichage desktop de l'accueil, on ne voit
   pas les boutons Tutoriel ou Statistiques ».

   CAUSE, ligne 1869 : un bloc @media (min-width: 1025px) pose `overflow: hidden`
   sur le conteneur de l'accueil, avec le commentaire « desactiver le scroll sur
   ecran d'ordinateur ». L'intention etait d'eviter une barre de defilement en
   supposant que l'accueil tienne toujours dans la fenetre. Il n'y tient pas des
   que la hauteur se reduit — ce qui est le cas courant : une fenetre de navigateur
   n'occupe pas tout l'ecran, et Windows applique souvent une mise a l'echelle de
   125 % qui ramene un ecran 1080p a 864 px de hauteur utile.

   MESURE en 1536x864 (soit un 1920x1080 a 125 %) :
     conteneur   scrollHeight 912 · clientHeight 800 · overflow-y hidden
                 -> 112 px de contenu coupes, SANS possibilite de defiler
     les boutons Tutoriel et Statistiques sont dans ces 112 px

   C'est le meme piege que #B27, ou la case de consentement se retrouvait hors
   d'atteinte sous une barre collante et bloquait le demarrage du jeu.

   CORRECTION RETENUE : reduire le bloc du logo, PAS ajouter une barre de
   defilement — demande explicite du proprietaire, « c'est moche ».

   Mesure du bloc logo en 1536x864 : 360 px sur 864, soit 42 % de la hauteur.
     .game-logo        336 px  (padding 32 haut + 32 bas) + 24 de marge basse
     .logo-text        192 px  a 128 px de fonte
     .logo-dice-icon    48 px  + 16 de marge

   Le dimensionnement est proportionnel a la hauteur de la fenetre (unites vh) et
   non par paliers : le bloc se reduit progressivement au lieu de sauter d'un coup
   a un seuil donne. Les bornes de clamp() gardent la main — jamais plus grand
   qu'aujourd'hui, jamais illisible sur une fenetre tres basse.

   `overflow-y: auto` est conserve en filet de securite. Ce n'est PAS une barre de
   defilement permanente : `auto` n'en affiche une que si le contenu deborde
   VRAIMENT. Comme le bloc logo se reduit desormais, il ne deborde plus aux
   hauteurs courantes et aucune barre n'apparait. Elle ne revient qu'aux hauteurs
   extremes, ou l'alternative serait de rendre des boutons inatteignables — ce qui
   est le defaut qu'on corrige.

   ⚠️ SEUIL ABAISSE DE 1025 A 769 PX LE 10 AOUT — SIGNALEMENT DU PROPRIETAIRE,
   « sur ordinateur, je ne veux pas de barre de defilement vertical ».
   Tout ce bloc etait borne a `min-width: 1025px`, et le raisonnement ci-dessus
   ne s'appliquait donc PAS entre 769 et 1024 px — c'est-a-dire sur une fenetre
   de navigateur NON MAXIMISEE, le cas le plus courant sur un ordinateur. Les
   deux mesures d'origine ont ete faites en 1536x864 et 1366x768, deux fenetres
   PLEIN ECRAN : la plage intermediaire n'a jamais ete regardee.

   Mesure a 961x910, fenetre non maximisee, une partie en cours :
     avant .... setup 921,6 px pour 845 disponibles -> 76,6 px, ascenseur visible
     apres .... 0 px de debordement, pied de page compris

   Mesure a 820x620, hauteur extreme :
     avant .... 400 px de debordement, le bouton JOUER hors de vue
     apres ....  68 px, bouton JOUER atteignable
   Le residu de 68 px est EXACTEMENT celui que le bloc >= 1025 px laisse a la
   meme hauteur (mesure a 1280x620 : 68 px lui aussi). L'extension aligne donc
   la plage 769-1024 sur le comportement de reference — elle ne l'approxime pas.

   ⚠️ Le bloc `@media (min-width: 769px) and (max-width: 1024px)` de la ligne
   ~8482 continue de poser `.logo-text { font-size: 6.5rem }`. Il ne gene pas :
   les regles ci-dessous visent `.screen--setup .logo-text`, de specificite
   superieure, et sont ecrites plus loin. Verifie a la mesure — 104 px avant,
   43,4 px apres. */
@media (min-width: 769px) {

  /* ⚠️ LA VRAIE CAUSE DU DEBORDEMENT, ET ELLE N'A RIEN A VOIR AVEC LE CONTENU.
     `.screen--setup` porte `min-height: 100vh` (l.1802) : il reclame TOUTE la
     hauteur de la fenetre. Or il est place DANS `.container`, qui commence deja
     64 px plus bas (l'en-tete) et ajoute 24 px de marge interieure haute et basse.
     L'ecran depasse donc de 64 + 48 = 112 px par construction, quel que soit son
     contenu — mesure : le dernier element visible s'arrete a 703 px, tres loin du
     bas, et le debordement etait pourtant de 112 px exactement.
     C'est ce debordement fantome qui poussait les boutons Tutoriel et
     Statistiques hors de la zone visible, et que `overflow: hidden` coupait.

     `100%` au lieu de `100vh` : l'ecran remplit l'espace REELLEMENT disponible.
     Le mobile n'est pas touche — il garde `100vh`, regle par #B30. */
  .screen--setup {
    min-height: 100%;
  }

  body:has(.screen--setup.screen--active) .container,
  .container:has(.screen--setup.screen--active) {
    overflow-y: auto;
  }

  .screen--setup .game-logo {
    padding-top: clamp(0.25rem, 1.5vh, 2rem);
    padding-bottom: clamp(0.25rem, 1.5vh, 2rem);
    margin-bottom: clamp(0.25rem, 1.5vh, 1.5rem);
  }

  .screen--setup .logo-text {
    font-size: clamp(2.75rem, 9.5vh, 8rem);
  }

  .screen--setup .logo-dice-icon {
    margin-bottom: clamp(0.2rem, 1.5vh, 1rem);
  }

  .screen--setup .logo-dice-icon svg {
    width: clamp(26px, 4.5vh, 48px);
    height: clamp(26px, 4.5vh, 48px);
  }

  /* La navigation basse se resserre elle aussi, mais bien moins : elle porte les
     boutons, dont les zones tactiles de 48 px ne doivent pas etre entamees. Seuls
     les espacements bougent. */
  .screen--setup .home-bottom-nav {
    padding-top: clamp(0.25rem, 1.5vh, 1rem);
    padding-bottom: clamp(0.25rem, 1.5vh, 1rem);
  }

  .screen--setup .ai-level-selector {
    margin-top: clamp(0.25rem, 1.5vh, 1rem);
    margin-bottom: clamp(0.25rem, 1.5vh, 1rem);
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   EN-TETE : LE BOUTON MENU PASSAIT A LA LIGNE EN MULTIJOUEUR EN LIGNE
   ═══════════════════════════════════════════════════════════════════════════
   Signalement du proprietaire : « sur mobile, les deux icones de menu sont en
   haut a gauche au lieu d'en avoir un a gauche et un a droite ».

   CAUSE. `.header` est une grille a TROIS colonnes (`auto 1fr auto`) mais
   contient QUATRE enfants : le bouton retour, l'indicateur reseau, un espaceur
   vide, et le bouton menu. Le quatrieme est donc place automatiquement sur une
   DEUXIEME LIGNE, en colonne 1 — c'est-a-dire juste sous le bouton retour.

   Pourquoi seulement en ligne : `#networkStatusIndicator` est en
   `display: none` hors partie reseau. Il ne compte alors pas comme element de
   grille, il ne reste que trois enfants, et la mise en page est correcte.
   `showGlobalNetworkIndicator()` le passe a `display: flex` — et c'est la que
   le quatrieme element apparait.

   MESURE, viewport 1280x720, ecran de jeu :
                        enfants   colonnes calculees      hauteur   bouton menu
     solo                  3      48px 1104px 48px         64 px    x=1208, ligne 1
     reseau                4      48px 1152px   0px       128 px    x=24,   LIGNE 2
   La troisieme colonne tombe a 0px et l'en-tete DOUBLE de hauteur.

   CORRECTION. On place explicitement chaque enfant dans sa colonne. L'espaceur
   et l'indicateur partagent la colonne 2 : l'espaceur est vide et sans fond,
   leur superposition ne se voit pas. Avec un placement explicite, plus aucun
   element ne peut etre reporte sur une deuxieme ligne, que l'indicateur soit
   affiche ou non. */
.header > .header__btn--back {
  grid-column: 1;
  grid-row: 1;
}

.header > #networkStatusIndicator,
.header > .header__spacer {
  grid-column: 2;
  grid-row: 1;
}

.header > .header__btn--menu {
  grid-column: 3;
  grid-row: 1;
}

/* L'indicateur ne doit pas pousser les boutons : il se centre dans sa colonne
   et se laisse comprimer plutot que de forcer la grille a s'elargir. */
.header > #networkStatusIndicator {
  justify-self: center;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* ═══════════════════════════════════════════════════════════════════════════
   REPRENDRE LA PARTIE
   Bouton d'accueil affiché seulement quand une partie interrompue attend.
   Volontairement moins appuyé que « JOUER MAINTENANT » : il ne doit pas lui
   voler la vedette, mais rester le premier élément atteint par le pouce.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ⚠️ SON COÛT VERTICAL COMPTE, ET IL A ÉTÉ SIGNALÉ EN VRAI.
   L'accueil tient tout juste dans la fenêtre : `.container` est en
   `overflow-y: auto` et le bloc logo se réduit déjà proportionnellement
   (clamp en vh, voir le bloc @media 1025px). La première version de ce bouton
   coûtait 76 px — 64 de hauteur plus 12 de marge — et faisait apparaître la
   barre de défilement sur l'accueil. Mesuré : nav de 346 px à 422 px.
   La hauteur est désormais gouvernée par `min-height` seul (48 px, la cible
   tactile du projet, non négociable) et la marge suit la fenêtre comme le
   reste : ~56 px au lieu de 76. */
/* La rangée : le bouton prend la place, la croix reste à sa droite. */
.home-resume {
  display: flex;
  align-items: stretch;
  gap: 0.375rem;
  margin-bottom: clamp(0.375rem, 1vh, 0.75rem);
}

.home-resume[hidden] {
  display: none;
}

/* Croix d'abandon. Elle jette la partie en attente : sans elle, le bouton
   restait affiché jusqu'à expiration (24 h) ou jusqu'à ce qu'une nouvelle
   partie l'écrase. Discrète au repos, franchement rouge au survol — c'est une
   action irréversible, elle doit se signaler au moment où on la vise. */
.home-resume-dismiss {
  flex: 0 0 auto;
  width: 48px;
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.875rem;
  border: 1px solid rgba(251, 191, 36, 0.35);
  background: rgba(251, 191, 36, 0.08);
  color: rgba(252, 211, 77, 0.75);
  cursor: pointer;
  transition: background 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}

.home-resume-dismiss:hover,
.home-resume-dismiss:focus-visible {
  border-color: rgba(200, 90, 84, 0.7);
  background: rgba(200, 90, 84, 0.18);
  color: #f2b8b5;
}

body.light-mode .home-resume-dismiss {
  border-color: rgba(180, 83, 9, 0.35);
  background: rgba(180, 83, 9, 0.06);
  color: rgba(146, 64, 14, 0.8);
}

body.light-mode .home-resume-dismiss:hover,
body.light-mode .home-resume-dismiss:focus-visible {
  border-color: rgba(155, 54, 48, 0.7);
  background: rgba(155, 54, 48, 0.12);
  color: #9b3630;
}

.home-resume-btn {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0 1rem;
  min-height: 48px;
  border-radius: 0.875rem;
  border: 1px solid rgba(251, 191, 36, 0.45);
  background: rgba(251, 191, 36, 0.14);
  color: #fcd34d;
  font-size: 0.9375rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.3s ease, transform 0.15s ease;
}

.home-resume-btn:hover {
  background: rgba(251, 191, 36, 0.22);
}

.home-resume-btn:active {
  transform: scale(0.98);
}

.home-resume-btn[hidden] {
  display: none;
}

/* Le jaune ambré passe mal sur fond clair : on descend la teinte pour rester
   lisible. Contrastes mesurés après composition alpha du fond semi-transparent
   du bouton sur le dégradé de l'écran (#ffffff → #f8f6f0), pire butée retenue :
   clair 5,76:1 · sombre 10,13:1, pour un seuil AA de 4,5. */
body.light-mode .home-resume-btn {
  border-color: rgba(180, 83, 9, 0.45);
  background: rgba(180, 83, 9, 0.10);
  color: #92400e;
}



/* ═══════════════════════════════════════════════════════════════════════════
   PASTILLE « MOI » — écran de configuration entre amis, en local
   Sans elle, le jeu ne sait pas lequel des quatre prénoms est celui du
   propriétaire de l'appareil : la partie n'est attribuée à personne, le taux
   de victoire reste à zéro et le face-à-face ne se remplit jamais.
   Un seul « moi » par table, d'où un bouton radio et non une case à cocher.
   ═══════════════════════════════════════════════════════════════════════════ */

.player-moi {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  /* 48 px de haut : cible tactile au standard du projet */
  min-height: 48px;
  min-width: 48px;
  padding: 0 0.625rem;
  border-radius: 999px;
  border: 1px solid rgba(148, 163, 184, 0.35);
  background: rgba(148, 163, 184, 0.10);
  color: rgba(255, 255, 255, 0.65);
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.25s ease, border-color 0.25s ease, color 0.25s ease;
}

/* Le bouton radio lui-même reste accessible au clavier et aux lecteurs
   d'écran, mais c'est la pastille qui est vue. Pas de display:none, qui le
   retirerait de l'ordre de tabulation. */
.player-moi input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  pointer-events: none;
}

.player-moi:has(input[type="radio"]:checked) {
  border-color: rgba(93, 162, 131, 0.85);
  background: rgba(93, 162, 131, 0.22);
  color: #a7e0c4;
}

.player-moi:has(input[type="radio"]:focus-visible) {
  outline: 2px solid #7dd3fc;
  outline-offset: 2px;
}

body.light-mode .player-moi {
  border-color: rgba(15, 23, 42, 0.22);
  background: rgba(15, 23, 42, 0.05);
  color: rgba(15, 23, 42, 0.60);
}

body.light-mode .player-moi:has(input[type="radio"]:checked) {
  border-color: rgba(47, 106, 79, 0.85);
  background: rgba(47, 106, 79, 0.14);
  color: #2f6a4f;
}

/* Le champ de saisie doit céder la place à la pastille plutôt que de la
   pousser hors de la rangée. */
.player-input-row input[type="text"] {
  min-width: 0;
  flex: 1 1 auto;
}

/* ═══════════════════════════════════════════════════════════════════════════
   INVITATION — QR code du salon
   Le QR est généré localement (lib/qrcode.js), sans aucune requête réseau :
   l'écran doit fonctionner dans un bar au réseau capricieux.
   Fond blanc obligatoire et non négociable : un lecteur de QR a besoin d'un
   contraste franc, et le thème sombre le tuerait.
   ═══════════════════════════════════════════════════════════════════════════ */

.lobby-qr {
  display: flex;
  justify-content: center;
  margin-top: 1rem;
}

.lobby-qr[hidden] {
  display: none;
}

.lobby-qr svg {
  display: block;
  width: 168px;
  height: 168px;
  border-radius: 12px;
  /* Marge blanche autour du motif : la « zone de silence » de la norme est
     déjà dans le SVG, ce padding la complète visuellement. */
  padding: 8px;
  background: #ffffff;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
}

.lobby-qr-hint {
  margin: 0.5rem 0 0;
  text-align: center;
  font-size: 0.8125rem;
  color: rgba(255, 255, 255, 0.6);
}

body.light-mode .lobby-qr-hint {
  color: rgba(15, 23, 42, 0.6);
}

/* Le bouton de partage prend la largeur, sous le code. */
#shareInviteBtn {
  margin-top: 0.875rem;
}

/* Fenêtres basses : on resserre les écarts de la zone du pouce plutôt que de
   laisser apparaître une barre de défilement sur l'accueil. Le bouton
   « Reprendre » rend cette marge nécessaire — sans lui l'accueil tenait de
   justesse, avec lui il faut récupérer une trentaine de pixels. */
@media (max-height: 780px) {
  .home-bottom-nav {
    gap: 0.5rem;
  }

  .home-resume { margin-bottom: 0.375rem; }

  .home-resume-btn,
  .home-resume-dismiss {
    min-height: 44px;
  }

  .home-resume-btn { font-size: 0.875rem; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   « AMIS EN LOCAL » / « AMIS EN LIGNE » — passage en ligne, plus compacts
   Signalé par le propriétaire : ces deux boutons étaient nettement plus hauts
   que le reste. Ils empilaient l'icône AU-DESSUS du texte (flex column), là où
   « Tutoriel » et « Statistiques », juste en dessous, les mettent côte à côte.
   On aligne sur la rangée du dessous : même disposition, même hauteur, et la
   zone du pouce y gagne une soixantaine de pixels — de quoi absorber le bouton
   « Reprendre » sans faire apparaître la barre de défilement.
   La cible tactile reste à 48 px, non négociable.
   ═══════════════════════════════════════════════════════════════════════════ */

.home-multiplayer-btn {
  flex-direction: row;
  gap: 0.5rem;
  padding: 0.75rem 0.875rem;
  min-height: 48px;
  font-size: 0.875rem;
  line-height: 1.2;
}

.home-multiplayer-btn svg {
  flex-shrink: 0;
}

/* Fenêtres basses : on resserre encore, sans passer sous la cible tactile. */
@media (max-height: 780px) {
  .home-multiplayer-btn {
    padding: 0.5rem 0.75rem;
    font-size: 0.8125rem;
  }
}

/* Très étroit : le libellé peut passer à la ligne plutôt que d'être rogné. */
@media (max-width: 360px) {
  .home-multiplayer-btn {
    padding: 0.5rem 0.5rem;
    font-size: 0.75rem;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   AUDIT INTERNATIONAL — action 9 : débordements et césure
   Surcharges en FIN de fichier, comme le veut la convention du projet.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Le badge de phase est dessiné pour « CHARGE » — six caractères, plus
   letter-spacing: 1px — et sa règle d'origine (ligne 2088) n'a ni max-width, ni
   overflow géré. En portugais il devait afficher CARREGAMENTO (12 caractères,
   2,00×) puis DESCARREGAMENTO (15, 1,88×) dans une pastille à coins arrondis de
   50 px, réduite à 0.8rem en petit écran (ligne 2206).

   Les libellés pt-BR sont raccourcis par ailleurs (CARGA / DESCARGA) : c'est le
   confort. Ceci est le garde-fou — une locale ajoutée demain débordera de
   nouveau, et le badge se contentera de tronquer.

   14ch laisse passer intact le plus long libellé actuel (DISCHARGE, 9 signes)
   et ne rogne que ce qui excède réellement. L'unité ch suit la taille de
   police, donc la contrainte reste proportionnelle en petit écran. */
.phase-badge-compact__text {
  max-width: 14ch;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* word-break, line-break, overflow-wrap et hyphens étaient à ZÉRO occurrence
   dans les 306 ko de cette feuille. Le thaï n'écrit pas d'espace entre les mots
   et sa césure repose sur un dictionnaire : sans règle explicite, le rendu est
   laissé entièrement au moteur, qui peut couper au milieu d'une syllabe.

   Ces propriétés sont héritées : les poser sur body suffit, aucun élément du
   jeu ne les redéfinit. break-word ne coupe QUE ce qui déborderait de toute
   façon ; word-break: normal et line-break: auto interdisent la coupure
   arbitraire partout ailleurs et laissent le moteur appliquer sa césure
   thaïe — c'est un choix, pas un oubli. */
body {
  overflow-wrap: break-word;
  word-break: normal;
  line-break: auto;
}

/* ═══════════════════════════════════════════════════════════════════════════
   INTERRUPTEUR DE SON DU MENU HEADER
   Surcharges en FIN de fichier, comme le veut la convention du projet.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Deux icônes empilées dans le même item, une seule visible à la fois.
   ⚠️ Volontairement en display et non en opacité comme .theme-icon juste
   au-dessus : ces deux-là sont en position: absolute et se superposent, ce qui
   marche pour un fondu croisé mais laisse l'icône masquée occuper la place.
   Ici l'item n'a qu'un état à montrer — le plus simple suffit et ne peut pas
   afficher les deux à la fois. */
#soundToggleBtn .sound-icon--off,
#soundToggleBtn.is-muet .sound-icon--on {
  display: none;
}

#soundToggleBtn.is-muet .sound-icon--off {
  display: block;
}

/* Son coupé : l'icône barrée dit déjà tout, mais elle est petite. L'atténuation
   donne le second signal, lisible sans lire — le même principe que le triple
   signal du sélecteur de niveau IA (icône + couleur + texte). */
#soundToggleBtn.is-muet .header-menu__icon svg {
  opacity: 0.55;
}

/* Le badge doré signale un état ACTIF (c'est celui de la langue courante).
   Coupé, il doit s'éteindre : un « Coupé » en doré se lirait comme une
   promotion du silence. */
#soundToggleBtn.is-muet .header-menu__badge {
  background: rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.75);
}

body.light-mode #soundToggleBtn.is-muet .header-menu__badge {
  background: rgba(0, 0, 0, 0.1);
  color: rgba(0, 0, 0, 0.65);
}

/* ══════════════════════════════════════════════════════════════════════════
   ÉCRAN « PORTE FERMÉE » — la partie a démarré, ou elle est pleine.
   Remplace un bandeau rouge effacé au bout de 3 secondes. Voir
   afficherPorteFermee() dans lib/network-ui.js.
   Placé en fin de fichier selon la convention du projet (piège n° 6).
   ══════════════════════════════════════════════════════════════════════════ */

/* L'icône de cadenas. Volontairement sobre et NON rouge : ce n'est pas une
   erreur du joueur, c'est un état du monde — ses amis ont commencé. Un signal
   d'alarme le culpabiliserait pour quelque chose qu'il n'a pas fait. */
.network-blocked__icon {
  display: flex;
  justify-content: center;
  margin-bottom: 0.75rem;
  color: var(--color-accent, #a89080);
  opacity: 0.7;
}

.network-blocked__text {
  margin: 0 0 1.5rem;
  text-align: center;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--color-text-secondary, #a89080);
}

/* Le bouton « Retour » vient juste après le bouton principal : sans cet
   espace, les deux se touchent et le secondaire se lit comme une partie du
   premier. */
#joinBlockedScreen .network-btn--text {
  margin-top: 0.75rem;
}

/* ══════════════════════════════════════════════════════════════════════════
   CANAL AUDIO — LA BARRE VOCALE                          (lib/voix-ui.js)
   ══════════════════════════════════════════════════════════════════════════

   ⚠️ PIÈGE N° 14 DU PROJET — les @keyframes n'ont pas de spécificité : un nom
   dupliqué et c'est la DERNIÈRE définition du document qui gagne, partout et
   pour tout le monde. Toutes les animations d'ici sont donc préfixées `voix`.
   Ne jamais introduire ici un nom générique (`pulse`, `fadeIn`, `shimmer`…).

   ⚠️ z-index 1100 — au-dessus de `.network-container` (1000), qui recouvre tout
   l'écran pendant le salon. En dessous, la barre serait invisible exactement là
   où le joueur décide d'allumer la voix. Et en dessous des surcouches de fin de
   partie (9999+), qui doivent rester au premier plan.

   ⚠️ La marge basse tient compte de `env(safe-area-inset-bottom)` : sur iPhone,
   la barre gestuelle du système mangerait sinon les boutons.                */

.voix-barre {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
  z-index: 1100;

  display: flex;
  align-items: center;
  gap: 0.45rem;
  max-width: calc(100vw - 1.5rem);
  padding: 0.35rem 0.5rem;

  border-radius: 999px;
  background: rgba(18, 26, 36, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.38);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);

  font-size: 0.85rem;
  color: #f4efe8;
  /* La barre flotte au-dessus du jeu : elle ne doit jamais capter un glissement
     destiné aux dés en dehors de ses propres commandes. */
  touch-action: manipulation;
}

.voix-barre[hidden] { display: none; }

/* ─── Boutons ─────────────────────────────────────────────────────────── */

.voix-barre__action {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 0.7rem;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.1);
  color: inherit;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  min-height: 2.2rem;
  transition: background 0.18s ease, color 0.18s ease, transform 0.12s ease;
}

.voix-barre__action:hover { background: rgba(255, 255, 255, 0.18); }
.voix-barre__action:active { transform: scale(0.96); }
.voix-barre__action:focus-visible {
  outline: 2px solid var(--color-primary, #d4a574);
  outline-offset: 2px;
}

.voix-barre__action--allumer {
  background: var(--color-primary, #d4a574);
  color: #1a1208;
}
.voix-barre__action--allumer:hover { background: var(--color-primary-light, #e8c199); }

/* Micro coupé : rouge sourd, pas criard. Ce n'est pas une erreur, c'est un choix
   du joueur — mais il doit être impossible de l'oublier. */
.voix-barre__action--coupe {
  background: rgba(220, 90, 80, 0.9);
  color: #fff;
}
.voix-barre__action--coupe:hover { background: rgba(232, 108, 98, 0.95); }

.voix-barre__action--quitter { padding: 0.4rem 0.55rem; }
.voix-barre__action--quitter:hover { background: rgba(220, 90, 80, 0.85); }

.voix-barre__action--attente {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.4rem 0.7rem;
  font-weight: 600;
  opacity: 0.85;
}

.voix-barre__icone { display: inline-flex; }
.voix-barre__icone svg { display: block; }
.voix-barre__icone--tourne { animation: voixRotation 1s linear infinite; }

.voix-barre__libelle { white-space: nowrap; }

/* Sur écran étroit, seules les icônes restent : deux libellés et quatre pseudos
   ne tiennent pas dans 360 px, et c'est l'icône qui porte le sens. */
@media (max-width: 480px) {
  .voix-barre { gap: 0.3rem; padding: 0.3rem 0.4rem; }
  .voix-barre__libelle { display: none; }
  .voix-barre__action { padding: 0.45rem 0.55rem; }
}

/* ─── Pastilles des participants ──────────────────────────────────────── */

.voix-barre__pairs {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  overflow-x: auto;
  scrollbar-width: none;
}
.voix-barre__pairs::-webkit-scrollbar { display: none; }

.voix-pastille,
.voix-barre__moi {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  background: var(--voix-couleur, #3498db);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  /* Le halo de parole est dessiné par box-shadow, donc animé sans reflow. */
  box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
  transition: box-shadow 0.12s ease, opacity 0.2s ease;
}

.voix-barre__moi { outline: 2px solid rgba(255, 255, 255, 0.55); outline-offset: 1px; }

/* Parle : anneau qui respire. C'est le seul retour instantané dont dispose le
   joueur pour savoir que son micro passe vraiment — d'où une animation continue
   plutôt qu'un simple changement de couleur. */
.voix-pastille--parle,
.voix-barre__moi.voix-pastille--parle {
  animation: voixHalo 1.1s ease-out infinite;
}

/* Micro coupé chez l'autre : on l'affiche éteint, pas absent. Savoir que
   quelqu'un est là mais muet est une information, pas un vide. */
.voix-pastille--coupe { opacity: 0.5; }

/* Lien en cours d'établissement : on ne promet rien tant que ça n'a pas abouti. */
.voix-pastille--attente { opacity: 0.55; animation: voixBattement 1.4s ease-in-out infinite; }

/* Lien impossible. Sans relais TURN, cela arrive sur certaines liaisons
   mobile↔mobile : le joueur doit le VOIR, sinon il croit que l'autre se tait. */
.voix-pastille--echec {
  background: #6b7280;
  opacity: 0.85;
  animation: none;
}

.voix-pastille__marque {
  position: absolute;
  right: -3px;
  bottom: -3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 0.95rem;
  height: 0.95rem;
  border-radius: 50%;
  background: #1b2430;
  color: #f4efe8;
  box-shadow: 0 0 0 1.5px rgba(18, 26, 36, 0.92);
}
.voix-pastille--echec .voix-pastille__marque { color: #f0b429; }
.voix-pastille__marque svg { display: block; }

/* ─── Thème clair ─────────────────────────────────────────────────────── */

[data-theme="light"] .voix-barre {
  background: rgba(255, 252, 248, 0.95);
  border-color: rgba(0, 0, 0, 0.1);
  color: #2b2118;
  box-shadow: 0 8px 28px rgba(80, 60, 40, 0.18);
}
[data-theme="light"] .voix-barre__action { background: rgba(0, 0, 0, 0.06); }
[data-theme="light"] .voix-barre__action:hover { background: rgba(0, 0, 0, 0.11); }
[data-theme="light"] .voix-pastille__marque {
  background: #fffcf8;
  color: #2b2118;
  box-shadow: 0 0 0 1.5px rgba(255, 252, 248, 0.95);
}

/* ─── Animations (toutes préfixées, cf. piège n° 14) ──────────────────── */

@keyframes voixHalo {
  0%   { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.55); }
  70%  { box-shadow: 0 0 0 0.5rem rgba(255, 255, 255, 0); }
  100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); }
}

@keyframes voixBattement {
  0%, 100% { opacity: 0.35; }
  50%      { opacity: 0.7; }
}

@keyframes voixRotation {
  to { transform: rotate(360deg); }
}

/* Réglage « Animations réduites » et préférence système : le halo disparaît,
   remplacé par un anneau fixe. L'information reste, le mouvement s'en va. */
@media (prefers-reduced-motion: reduce) {
  .voix-pastille--parle,
  .voix-barre__moi.voix-pastille--parle,
  .voix-pastille--attente,
  .voix-barre__icone--tourne { animation: none; }
  .voix-pastille--parle,
  .voix-barre__moi.voix-pastille--parle { box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.8); }
}

.animations-reduites .voix-pastille--parle,
.animations-reduites .voix-barre__moi.voix-pastille--parle,
.animations-reduites .voix-pastille--attente,
.animations-reduites .voix-barre__icone--tourne { animation: none; }
.animations-reduites .voix-pastille--parle,
.animations-reduites .voix-barre__moi.voix-pastille--parle {
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.8);
}

/* ══════════════════════════════════════════════════════════════════════════
   BARRE VOCALE — LA BARRE NE DOIT JAMAIS EMPÊCHER DE JOUER
   ══════════════════════════════════════════════════════════════════════════

   Signalé en jeu : la barre gênait l'action. Elle flotte en `position: fixed`
   au bas de l'écran, à z-index 1100 — c'est-à-dire exactement là où B30 a
   descendu le bloc d'actions pour le mettre dans la zone du pouce.

   Deux corrections, volontairement redondantes : la première rend le blocage
   IMPOSSIBLE, la seconde le rend INVISIBLE. On garde les deux parce qu'elles ne
   traitent pas le même défaut — un bouton qu'on ne voit pas reste inutilisable
   même s'il reçoit les appuis.                                              */

/* ① La barre ne capte plus les appuis : seuls ses propres boutons le font.
      Quoi qu'elle recouvre, l'appui traverse et atteint le jeu. */
.voix-barre { pointer-events: none; }

.voix-barre__action,
.voix-barre__pairs,
.voix-pastille,
.voix-barre__moi { pointer-events: auto; }

/* ② On réserve sa place, pour qu'elle ne se pose sur rien.
      La classe est posée par lib/voix-ui.js quand la barre s'affiche et retirée
      quand elle disparaît : l'espace n'est jamais réservé pour rien (partie solo,
      multijoueur local, menus).
      3,6 rem = hauteur de la barre (2,2 rem) + ses marges haute et basse.

      ⚠️ RÉSERVER DE LA PLACE EN BAS NE MARCHE PAS DANS CE PROJET — mesuré, deux
      tentatives à l'appui. `body` est un shell de hauteur fixe (`position: fixed`,
      `inset: 0`, `overflow: hidden`, flex en colonne) et `#gameScreen` porte
      `min-height: 100%` : l'écran de jeu remplit déjà exactement la hauteur
      disponible. Un `padding-bottom` sur `#gameScreen` l'a fait finir à 800 px
      pour un viewport de 742 ; déplacé sur `body`, il a bien rétréci
      `main.container` (742 → 684) mais `#gameScreen` a débordé de son conteneur
      en `overflow-y: auto`, et le bouton « Lancer » s'est retrouvé À MOITIÉ
      COUPÉ — tout en restant sous la barre.
      ➡️ On ne prend donc pas de place : on met la barre là où il n'y a rien.
      La classe `voix-barre-affichee` reste posée sur `<html>` : elle ne réserve
      plus d'espace, mais elle sert de point d'accroche à d'éventuelles
      adaptations par écran. */

/* ③ LA BARRE REMONTE EN HAUT À DROITE, SOUS L'EN-TÊTE.
      ⚠️ ~~étape intermédiaire~~ — REMPLACÉE LE 8 AOÛT par le bloc « LA BARRE
      VOCALE PREND SA PLACE » en fin de feuille : la pastille flottante recouvrait
      le badge de phase de 13 px (mesuré à 390x664), et fuir le bouton n'est pas
      avoir une place. Le bandeau a désormais sa hauteur réservée. Ce qui suit
      reste en place parce que le bloc final s'appuie dessus — il n'annule que
      `left`, `right`, `border-radius` et la largeur.
      Le bas de l'écran de jeu est occupé sur toute sa largeur par le bouton
      « Lancer » — c'est la zone du pouce, voulue ainsi par B30, et elle n'est pas
      négociable. Le coin haut droit, lui, est le vide du panneau d'information :
      le badge CHARGE est centré, rien n'y est cliquable.
      `left: auto` et `transform: none` annulent le centrage horizontal défini
      plus haut : sans eux, `left: 50%` continuerait de s'appliquer. */
.voix-barre {
  top: calc(3.9rem + env(safe-area-inset-top, 0px));
  right: 0.6rem;
  bottom: auto;
  left: auto;
  transform: none;
  max-width: calc(100vw - 1.2rem);
}

/* Compacte par défaut : à cette place, la largeur est comptée. Le libellé ne
   revient que sur les écrans assez larges pour l'accueillir sans repousser les
   pastilles. L'information n'est jamais perdue : `title` et `aria-pressed`
   portent l'état pour l'infobulle comme pour le lecteur d'écran. */
.voix-barre__libelle { display: none; }

@media (min-width: 720px) {
  .voix-barre__libelle { display: inline; }
}

/* ══════════════════════════════════════════════════════════════════════════
   JOUEUR ABSENT DONT C'EST LE TOUR                    (lib/tour-absent.js)
   ══════════════════════════════════════════════════════════════════════════

   ⚠️ CE BLOC N'EST PAS UNE ALERTE. Quelqu'un qui perd le réseau n'a rien fait
   de mal, et il est souvent en train d'écouter les autres par le canal audio.
   Ton sobre, ambre plutôt que rouge : c'est un état du monde, pas une faute.

   ⚠️ Il vit DANS le flux de `.game__actions`, pas en surcouche. Une surcouche
   se serait posée sur le bouton « Lancer » — le défaut qu'on vient justement de
   corriger. Ici il prend sa place dans la grille, qui la lui donne.          */

.tour-absent {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 0.9rem;
  border-radius: var(--radius-md, 10px);
  background: rgba(240, 180, 41, 0.1);
  border: 1px solid rgba(240, 180, 41, 0.3);
  text-align: center;
}

.tour-absent[hidden] { display: none; }

.tour-absent__texte {
  margin: 0;
  font-size: 0.92rem;
  font-weight: 600;
  color: #f0b429;
  /* Le décompte change chaque seconde : sans largeur stable, tout le bloc
     tremblerait à chaque tic. */
  font-variant-numeric: tabular-nums;
}

.tour-absent__bouton {
  width: 100%;
  /* Cible tactile confortable : on l'utilise dans un moment d'agacement. */
  min-height: 2.75rem;
  font-size: 0.95rem;
}

.tour-absent__bouton[hidden] { display: none; }

[data-theme="light"] .tour-absent {
  background: rgba(180, 120, 10, 0.1);
  border-color: rgba(180, 120, 10, 0.35);
}
[data-theme="light"] .tour-absent__texte { color: #8a5a00; }

/* ═══════════════════════════════════════════════════════════════════════════
   ÉCRAN COURT — LE TÉLÉPHONE COUCHÉ N'AVAIT AUCUNE MISE EN PAGE
   ═══════════════════════════════════════════════════════════════════════════
   Signalé le 8 août : « elle ne voit pas la barre de lancer ». Reproduit et
   mesuré — le portrait était bon, c'est le PAYSAGE qui n'était couvert par
   aucune règle.

   Les deux blocs qui mettent en page l'écran de jeu sont
     @media (max-width: 768px) and (min-height: 660px)   → styles.css:11449
     @media (max-width: 480px)                           → styles.css:8591
   Un téléphone couché échoue aux deux : trop large pour le second, trop court
   pour le premier. Il retombait donc sur la mise en page bureau, où
   `.game__actions` reste dans le flux — et le bloc partait sous la ligne de
   flottaison. Mesures, partie à 4 joueurs en cours, AVANT correction :
     844x390 (iPhone 14 couché)  bouton 681→737 pour 390 de haut   347 px dehors
     667x375 (iPhone SE couché)  bouton 743→799 pour 375 de haut   424 px dehors
   Le contenu réclamait 657 px pour 326 disponibles — exactement le double.

   POURQUOI DEUX COLONNES ET PAS UNE COLONNE COMPRIMÉE.
   Mesuré à 844x390 : `.game__info` veut 461 px (dont 266 pour les 4 joueurs),
   alors que dés + actions n'en font que 196. En une seule colonne il ne restait
   que 130 px à l'info, soit 28 % de ce qu'elle demande. En deux colonnes,
   l'info prend toute la hauteur à gauche et défile chez elle, pendant que la
   droite loge les dés au-dessus du bouton avec de la marge.
   ➡️ CONSÉQUENCE LA PLUS UTILE : le bouton est dans la colonne de DROITE, donc
   le nombre de joueurs n'a plus AUCUN effet sur lui. À six joueurs comme à deux,
   il est au même endroit — ce que la mise en page d'origine ne garantissait pas.

   `#gameScreen[style*="display: block"]` et `!important` : showScreen()
   (lib/ui-manager.js:193) écrit un `display` INLINE, jamais une classe. Voir
   l'explication complète en styles.css:11423 — même technique, mêmes raisons.

   `max-width: none` : `#gameScreen` est plafonné à 520 px et centré. En paysage
   ça laissait 324 px de vide de chaque côté et écrasait les deux colonnes.

   Relevé APRÈS correction (4 joueurs, partie réelle) :
     844x390  bouton 363,304 → 743,360   visible   page ne défile plus (326/326)
     667x375  bouton 250,289 → 633,345   visible   page ne défile plus (311/311)
   Dans les deux cas les dés tiennent dans leur cellule et l'info défile seule.

   ⚠️ Les marges gauche/droite passent par `--safe-area-left/right` : couché, un
   iPhone à encoche mange 44 px SUR UN CÔTÉ. En portrait la question ne se pose
   pas, c'est pour ça qu'elle n'avait jamais été traitée.                     */
@media (min-width: 481px) and (max-height: 659px) {
  #gameScreen[style*="display: block"] {
    display: grid !important;
    grid-template-columns: minmax(0, 34%) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) auto;
    grid-template-areas:
      "info dice"
      "info actions";
    height: 100%;
    min-height: 0;
    max-width: none;
    margin: 0;
    padding: 0;
    gap: 0 0.5rem;
  }

  /* La colonne qui déborde est celle des joueurs : elle défile chez elle
     plutôt que de pousser quoi que ce soit hors de l'écran. */
  #gameScreen[style*="display: block"] > .game__info {
    grid-area: info;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: calc(0.5rem + var(--safe-area-top, 0px)) 0.5rem 0.5rem
             calc(0.6rem + var(--safe-area-left, 0px));
  }

  #gameScreen[style*="display: block"] > .game__dice-area {
    grid-area: dice;
    min-height: 0;
    padding: 0.4rem;
    justify-content: center;
  }

  /* `position: static` : le sticky n'a plus rien à faire ici, la grille réserve
     la rangée. Même raisonnement qu'en styles.css:11487. */
  #gameScreen[style*="display: block"] > .game__actions {
    grid-area: actions;
    position: static;
    margin-top: 0;
    padding: 0.4rem calc(0.6rem + var(--safe-area-right, 0px))
             max(0.4rem, env(safe-area-inset-bottom, 0px)) 0.5rem;
  }

  /* 84 px au lieu de 124 : à trois dés côte à côte, la taille bureau ne tient
     pas dans la colonne de droite d'un iPhone SE couché (400 px utiles). */
  #gameScreen[style*="display: block"] .die {
    min-width: 84px;
    min-height: 84px;
    width: 84px;
    height: 84px;
    font-size: 2.6rem;
  }
}

/* Version affichée en bas des Paramètres — voir index.html, section VERSION.
   Discrète par construction : l'information sert au dépannage, pas au jeu. */
.options__version {
  margin: 1.25rem 0 0;
  padding-top: 0.75rem;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  text-align: center;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  opacity: 0.55;
  /* Sélectionnable : on demandera à un joueur de la recopier. Le reste de
     l'interface est en user-select: none pour le confort tactile. */
  -webkit-user-select: text;
  user-select: text;
}

[data-theme="light"] .options__version,
body.light-mode .options__version {
  border-top-color: rgba(0, 0, 0, 0.1);
}

/* ═══════════════════════════════════════════════════════════════════════════
   iPHONE + APPLI GOOGLE — LE BOUTON « LANCER » PASSAIT SOUS L'ÉCRAN
   ═══════════════════════════════════════════════════════════════════════════
   Signalé une deuxième fois le 8 août : « le bouton lancer disparaît quand il y
   a le son », en multijoueur, sur iPhone, dans le navigateur intégré de l'appli
   Google. Reproduit et mesuré à 6 joueurs — DEUX défauts indépendants se
   cumulent, et la barre vocale n'est coupable d'aucun des deux.

   ① LE PANNEAU D'INFORMATION REFUSAIT DE SE COMPRIMER.
      `#gameScreen > .game__info` était en `flex: 0 0 auto` (styles.css:11463) :
      il réclame la hauteur de son contenu et ne la rend jamais. Or ce contenu
      grandit avec le nombre de joueurs — et le multijoueur, c'est précisément là
      qu'on est nombreux. Mesures à 6 joueurs, dans un navigateur SANS aucune
      barre, donc avant même de parler d'iOS :
        390x664   le panneau réclame 517 px pour 556 disponibles
                  bouton « Lancer » à 691→747 : 83 px SOUS le bas de l'écran
        375x540   dés à 785 : 245 px sous le bas de l'écran, .container
                  défilait de 381 px
      Le bouton n'était pas recouvert par quelque chose. Il était dehors.

   ② LE SHELL SE DIMENSIONNAIT SUR LE MAUVAIS VIEWPORT — ET C'EST LE MICRO QUI
      RENDAIT LE DÉFAUT PERMANENT.
      `html { height: 100% }` et `body { min-height: 100vh }` : sur iOS, `100vh`
      vaut la hauteur de l'écran **barres du navigateur rétractées** (`lvh`).
      Les barres affichées, le bas de la page tombe donc hors de la zone visible,
      de toute leur hauteur. Mesuré en émulant un iPhone 14 (lvh 745, visible
      664), 4 joueurs : bouton à 653→709, soit 45 px dehors sur 56 de haut — les
      quatre cinquièmes.
      ➡️ LE LIEN AVEC « QUAND IL Y A LE SON » EST ICI. D'ordinaire un geste de
      défilement rétracte les barres et le bouton réapparaît : le défaut est
      intermittent et on le met sur le compte de la malchance. Micro ouvert, iOS
      maintient les barres déployées pendant toute la capture — et le bouton ne
      revient plus jamais. Le canal audio ne cachait pas le bouton : il
      supprimait le rattrapage accidentel qui masquait le défaut.
      `svh` est exactement l'unité qui manquait : la hauteur visible quand les
      barres sont là. Sur un écran sans barre rétractable — bureau, Android en
      plein écran, Capacitor — `svh == vh` et ce bloc ne change rien du tout.

   ③ AU PASSAGE : LA MISE EN PAGE DE LA VAGUE 10 NE S'APPLIQUAIT PAS.
      Le bloc `@media (max-width: 480px)` de styles.css:8592 pose
      `.screen--game { display: grid }` SANS `!important`, alors que showScreen()
      écrit un `display` INLINE (piège documenté en styles.css:11423). Le style
      inline gagne toujours : mesuré à 375x560, le `display` calculé valait
      `block`. Toute la grille `minmax(0, …)` du commit cf45f04 était donc
      inerte, et c'est le `position: sticky` d'origine qui sauvait le bouton —
      par accident. On unifie sur la mise en page en colonne de 11449, qui elle
      porte bien son `!important`, et on supprime la falaise des 660 px : elle
      n'existait que parce que le panneau ne savait pas se comprimer.

   RELEVÉ APRÈS CORRECTION, 6 joueurs, bandeau vocal affiché :
     390x664   panneau 239 (défile) · dés 418→508 · bouton 572→628 · marge 36 px
     375x540   panneau 115 (défile) · dés 294→384 · bouton 448→504 · marge 36 px
     844x390   panneau 234 (défile) · dés 136→220 · bouton 304→360 · marge 30 px
   Dans les trois cas `.container` ne défile plus du tout (0 px), et les dés ne
   sortent plus de leur cellule.

   CONTREPARTIE ASSUMÉE : à 5 ou 6 joueurs sur un écran court, la liste des
   joueurs défile chez elle au lieu de tout pousser dehors. Un panneau qui
   défile reste utilisable ; un bouton hors de l'écran, non.                  */

/* ⚠️ L'ORDRE DES DÉCLARATIONS EST LE MÉCANISME DE REPLI, PAS UN HASARD.
   `100vh` reste écrit plus haut dans la feuille : un navigateur qui ignore
   `svh` laisse tomber la déclaration ci-dessous et garde l'ancienne valeur. */
html { height: 100svh; }
body { min-height: 100svh; }
/* ⚠️ CETTE LIGNE ÉTAIT GLOBALE, ET ELLE ANNULAIT LA CORRECTION BUREAU.
   Mesuré le 9 août 2026 à 1440x900 : l'accueil affichait une barre de
   défilement pour 112 px de débordement — alors que son contenu ne réclame
   que 769 px pour 836 disponibles. La barre ne faisait défiler QUE DU VIDE.

   `styles.css:11224` pose déjà `.screen--setup { min-height: 100% }` sous
   `@media (min-width: 1025px)`, écrit précisément pour tuer ce débordement
   « par construction » : l'écran réclamait toute la fenêtre alors qu'il vit
   sous un en-tête de 64 px, dans un conteneur qui ajoute 48 px de marges —
   soit 112 px de trop, quel que soit le contenu.

   Écrite ici SANS média et PLUS BAS dans le fichier, `100svh` reprenait la
   main sur le bureau : même spécificité, dernière déclaration gagnante. Le
   débordement fantôme revenait donc à l'identique, 112 px à l'unité près.
   C'est le piège n° 6 dans sa forme la plus coûteuse — une surcharge de fin
   de fichier qui écrase une règle antérieure PLUS SPÉCIFIQUE EN INTENTION
   mais pas en spécificité CSS, parce qu'un `@media` n'en ajoute aucune.

   ➡️ `svh` ne sert qu'aux écrans à barres rétractables (piège n° 22). Le
   borner rend au bureau la règle qui lui était destinée, sans toucher d'un
   pixel la correction iOS.
   ⚠️ Le point de bascule doit rester le complément EXACT de son jumeau
   `@media (min-width: …)` plus haut. Les désolidariser rouvrirait le trou.

   ⚠️ BASCULE DÉPLACÉE DE 1024/1025 À 768/769 LE 10 AOÛT — ET L'AVERTISSEMENT
   CI-DESSUS A ÉTÉ VÉRIFIÉ EN VRAI, DANS LES DEUX SENS.
   Le jumeau est passé à `min-width: 769px` pour couvrir les fenêtres non
   maximisées. Le seuil d'ici n'a PAS suivi du même coup : les deux règles se
   sont retrouvées à se recouvrir entre 769 et 1024 px, à égalité de
   spécificité — et comme celle-ci est écrite plus bas, elle gagnait. Mesuré à
   936x892 : `min-height` calculé **892 px et non 100 %**, et le débordement
   fantôme intact à **113 px**. Exactement le trou que ce commentaire
   annonçait, rouvert par le geste qu'il déconseillait.

   ➡️ Entre 769 et 1024 px, c'est désormais `min-height: 100%` qui s'applique.
   Ce n'est pas une perte pour la tablette en paysage : `100 %` fait mieux que
   `100svh`, puisqu'il se cale sur le conteneur RÉEL — lequel tient déjà compte
   de l'en-tête — au lieu de la hauteur de la fenêtre. `svh` reste seul maître
   sous 769 px, où vit la cible du piège n° 22. */
@media (max-width: 768px) {
  /* ⚠️ `100%` ET PLUS `100svh` — LE MÊME DÉFAUT QUE CI-DESSUS VIVAIT ENCORE
     SOUS 769 px, ET IL Y A ÉTÉ MESURÉ LE 12 AOÛT.
     À 375×812 : `.container` fait 752 px avec 24 px de rembourrage, soit une
     boîte utile de 704 px, pendant que cette règle réclamait 812 px.
     812 − 704 = 108 — exactement le débordement constaté. Et le contenu, lui,
     s'arrêtait à 705 px : l'ascenseur ne faisait défiler QUE DU VIDE, 191 px
     de vide en bas du bloc. Signalé comme « l'accueil défile », diagnostiqué
     d'abord à tort comme un bloc de logo trop haut — le réduire de 166 à 60 px
     n'a pas retiré UN SEUL pixel au débordement, ce qui a tranché.

     ⚠️ CE N'EST PAS UN RETOUR EN ARRIÈRE SUR LE PIÈGE N° 22. La protection iOS
     est assurée EN AMONT par `body { min-height: 100svh }` (ligne 12459), et
     `.container` n'a aucune règle de hauteur propre : sa hauteur descend de ce
     `svh`, moins l'en-tête. `100%` hérite donc de la bonne base ET tient compte
     de l'en-tête, ce que `100svh` ne peut pas faire — c'est mot pour mot
     l'argument que le commentaire ci-dessus retient pour le bureau.

     ⚠️ Les deux seuils du couple (piège n° 38) disent désormais la même chose.
     C'est voulu : il n'y a plus de plage où ils peuvent se désolidariser. */
  .screen--setup { min-height: 100%; }
}
/* Même défaut, même cause : l'écran du tutoriel est en `overflow: hidden`, donc
   ce qui dépasse par le bas y est DÉFINITIVEMENT inatteignable. */
#tutorialScreen { height: 100svh; }

/* ⚠️ SEUIL DE LARGEUR RETIRÉ LE 10 AOÛT — LE BUREAU N'ÉTAIT COUVERT PAR AUCUN
   BLOC, ET C'EST CE TROU QU'ON PRENAIT POUR UN DÉFAUT DU COACH.
   La condition valait `(max-width: 480px), (max-width: 768px) and
   (min-height: 660px)`. Recensement des trois blocs qui rendent #gameScreen
   compressible :
     · styles.css:10686   (max-width: 768px) and (min-height: 660px)   → flex
     · styles.css:12209   (min-width: 481px) and (max-height: 659px)   → grille
     · celui-ci           (max-width: 480px) | (≤768 et ≥660)          → flex
   Aucun ne couvrait **largeur ≥ 769 ET hauteur ≥ 660**, c'est-à-dire une fenêtre
   d'ordinateur non maximisée. #gameScreen y restait donc en `display: block` —
   showScreen() l'écrit en inline — et TOUTE la machinerie de compression du
   piège n° 23 était inerte : `.game__info { flex: 0 100 auto }` ne veut rien
   dire dans un conteneur de bloc.
   Mesuré à 900x800, partie solo : `.container` débordait de **59 px** et
   `#rollBtn` finissait **19 px sous le bord visible** — le joueur devait faire
   défiler pour lancer les dés. Les mêmes règles appliquées à la main :
   panneau 354 → 288 px (il défile chez lui), débordement **0**, bouton entier.

   ⚠️ LE COACH N'ÉTAIT PAS LA CAUSE — mais il a fallu y revenir ensuite, et le
   bloc « ORDINATEUR — LE PANNEAU… » en fin de feuille raconte pourquoi : ce
   correctif-ci a rendu le bouton visible en déplaçant le manque de place DANS
   `.game__info`, qui s'est mis à afficher un ascenseur. La réserve du coach y
   est ramenée à 7,4 rem au-dessus de 768 px — pas à `auto`.
   Ce qui reste vrai et qui écarte `height: auto` définitivement : la réserve de
   8,5 rem est de 19 px de trop à 900 px de large (le plus long message des
   8 langues y tient en 117 px) mais de **12 px trop COURTE** à 375 px, où le
   même message en réclame 148. Une valeur unique ne peut pas être juste aux
   deux. Et `auto` rendrait au bloc son amplitude de **42 px (900 px de large) à
   74 px (375 px)** — exactement le saut des dés à chaque tour que cette hauteur
   fixe a été écrite pour supprimer. Les 19 px récupérés n'auraient de toute
   façon couvert que 19 des 59 px manquants ici.

   ⚠️ POURQUOI CE N'EST PAS LE PIÈGE N° 38 (deux `@media` qui se désolidarisent).
   Ce bloc n'a pas de jumeau : la bascule 768/769 documentée juste au-dessus
   appartient à `.screen--setup`, pas à `#gameScreen`. Et la nouvelle condition
   ne recouvre toujours PAS celle de styles.css:12209, qui exige `max-height:
   659px` là où celle-ci exige `min-height: 660px` — les deux restent
   complémentaires exactes. `(max-width: 480px), (min-height: 660px)` est
   l'union stricte de l'ancienne condition et de la région bureau. */
@media (max-width: 480px), (min-height: 660px) {
  #gameScreen[style*="display: block"] {
    display: flex !important;
    flex-direction: column;
    /* `height` ET SURTOUT PAS `min-height`. C'est la leçon du commit cf45f04,
       appliquée cette fois au bon endroit : `min-height` ne plafonne rien, la
       boîte grandit avec son contenu, et `flex-shrink` ne se déclenche donc
       jamais. C'est ce qui laissait le panneau pousser le bouton dehors. */
    height: 100%;
    min-height: 0;
  }

  /* Le panneau se comprime et défile chez lui.
     ⚠️ `flex-shrink: 100` et non 1 : la compression se répartit au prorata de
     la base de chaque élément, donc à 1 les dés en prendraient leur part —
     mesuré, 12 px, assez pour qu'ils débordent de leur cellule. À 100, le
     panneau absorbe la quasi-totalité et les dés gardent leur taille. */
  #gameScreen[style*="display: block"] > .game__info {
    flex: 0 100 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* Arrivé en bout de course, le geste ne doit pas emporter la page avec lui. */
    overscroll-behavior: contain;
  }

  #gameScreen[style*="display: block"] > .game__dice-area {
    flex: 1 1 auto;
    min-height: 0;
    justify-content: center;
  }

  /* Dernier de la colonne, en `static` : la colonne lui réserve réellement sa
     hauteur et plus rien ne peut le pousser dehors. Même raisonnement qu'en
     styles.css:11487, étendu à tous les téléphones. */
  #gameScreen[style*="display: block"] > .game__actions {
    flex: 0 0 auto;
    position: static;
    margin-top: auto;
    padding-bottom: max(0.75rem, env(safe-area-inset-bottom, 0px));
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LA BARRE VOCALE PREND SA PLACE AU LIEU DE FLOTTER DESSUS
   ═══════════════════════════════════════════════════════════════════════════
   « Il faut revoir la barre. » Elle était une pastille flottante posée en haut
   à droite (bloc styles.css:12888) pour fuir le bouton « Lancer » — mais fuir
   n'est pas avoir une place. Mesuré à 4 joueurs en 390x664 : la pastille
   occupait 62→109 et le badge de phase 96→141, soit 13 px de recouvrement sur
   sa barre de progression.

   ➡️ Elle devient un BANDEAU ancré sous l'en-tête, sur toute la largeur, et sa
   hauteur est RÉSERVÉE par `.container`. Elle ne peut donc plus rien recouvrir,
   ni être recouverte. Vérifié à la sonde `elementsFromPoint` : ses trois coins
   ne rencontrent plus que le rembourrage vide de `.container`.

   ⚠️ POURQUOI CETTE RÉSERVATION MARCHE, ALORS QUE CELLE D'EN BAS AVAIT ÉCHOUÉ
   (voir le constat en styles.css:12874). Deux raisons. D'abord elle est posée
   sur `.container` — le seul bloc de la chaîne qui possède du rembourrage et
   dont la hauteur ne dépend pas de son contenu — et non sur `body` ou
   `#gameScreen`. Ensuite, et surtout, elle n'est devenue possible qu'avec le
   correctif ci-dessus : tant que `.game__info` refusait de se comprimer, prendre
   45 px n'importe où revenait à jeter le bouton dehors de 45 px de plus.

   ⚠️ `--voix-barre-ancrage` et `--voix-barre-hauteur` sont MESURÉES et posées
   par lib/voix-ui.js, jamais devinées ici : la hauteur de l'en-tête change avec
   la langue et l'encoche, celle du bandeau avec le nombre de participants. Les
   valeurs de repli n'existent que pour le cas où le module ne s'est pas encore
   exécuté.                                                                  */
.voix-barre {
  top: var(--voix-barre-ancrage, 3.75rem);
  left: 0;
  right: 0;
  bottom: auto;
  transform: none;
  width: 100%;
  max-width: none;
  justify-content: center;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  min-height: 2.5rem;
  padding: 0.25rem max(0.5rem, env(safe-area-inset-right, 0px))
           0.25rem max(0.5rem, env(safe-area-inset-left, 0px));
}

html.voix-barre-affichee .container {
  padding-top: calc(var(--spacing-md) + var(--voix-barre-hauteur, 2.5rem));
}

/* Écran court : chaque pixel rendu au panneau des joueurs compte. Mesuré en
   844x390, le bandeau passe de 45 à 40 px. */
@media (max-height: 620px) {
  .voix-barre {
    min-height: 2.1rem;
    padding-top: 0.15rem;
    padding-bottom: 0.15rem;
  }
  .voix-barre__action { min-height: 1.9rem; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   COACH — les conseils pendant la partie (lib/coach.js, lib/coach-ui.js)

   ⚠️ HAUTEUR FIXE, ET C'EST LE POINT IMPORTANT.
   Le panneau reste dans le flux pendant toute la partie : seul son texte
   change. Une première version posait `min-height` — insuffisant, et mesuré :
   en 375×812 le panneau passait de 75 px (« Valide ton tour. ») à 136 px
   (« Yannis a fait Brelan d'as et donne 7 jetons à Marc. » + le pourquoi), et
   LES DÉS REMONTAIENT DE 56 PX À CHAQUE TOUR. C'est le défaut qui a coûté le
   bandeau de score sous les dés (démonstration dans lib/ui-manager.js), en
   pire : celui-là ne valait que 30 px.
   `height` fixe, calée sur le message le plus long des 8 langues, supprime le
   mouvement entièrement. Le prix est 136 px réservés en permanence — assumé :
   le coach ne vit que les trois premières parties, et un débutant a plus
   besoin d'une phrase stable que de 136 px de vide.
   `overflow-y: auto` n'est qu'une soupape : si une traduction future dépasse,
   elle défile au lieu d'être coupée ou de tout décaler.

   ⚠️ Noms d'animations préfixés `coach` — piège n° 14 de CLAUDE.md. Un nom
   générique gagnerait pour TOUT le document, pas seulement pour ce bloc.
   ═══════════════════════════════════════════════════════════════════════════ */

.coach {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: 0.65rem 0.8rem;
  border-radius: var(--radius-md, 10px);
  background: rgba(93, 162, 131, 0.1);
  border: 1px solid rgba(93, 162, 131, 0.28);
  /* Mesuré en 375×812 : le message le plus long tient en 136 px, bordures et
     rembourrage compris. Ne pas remplacer par min-height (voir l'entête). */
  height: 8.5rem;
  overflow-y: auto;
  text-align: left;
}

.coach[hidden] { display: none; }

.coach__entete {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.coach__icone {
  display: inline-flex;
  align-items: center;
  color: var(--color-success, #5da283);
  flex-shrink: 0;
}

.coach__titre {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-success, #5da283);
  /* Pousse le bouton de fermeture à droite sans une règle de plus. */
  margin-right: auto;
}

.coach__fermer {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44 px : la cible tactile minimale du projet. Le bouton est visuellement
     petit, sa zone de contact ne l'est pas — on le presse pour faire taire
     quelque chose, donc rater la cible est particulièrement agaçant. */
  min-width: 44px;
  min-height: 44px;
  margin: -0.5rem -0.4rem -0.5rem 0;
  padding: 0;
  border: none;
  background: none;
  color: var(--color-text-dim, #a8a39a);
  cursor: pointer;
  opacity: 0.65;
  transition: opacity 0.2s;
}

.coach__fermer:hover,
.coach__fermer:focus-visible { opacity: 1; }

.coach__texte {
  margin: 0;
  font-size: 0.92rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--color-text, #f2ede4);
}

.coach__detail {
  margin: 0;
  font-size: 0.84rem;
  line-height: 1.35;
  color: var(--color-text-dim, #a8a39a);
}

.coach__texte:empty,
.coach__detail:empty { display: none; }

/* Dé que le coach conseille de garder.
   Volontairement DIFFÉRENT de `.die--kept` : le joueur doit pouvoir distinguer
   « je l'ai gardé » de « on me conseille de le garder ». D'où un contour
   pointillé, là où le dé gardé porte un contour plein. */
.die--coach {
  outline: 2px dashed var(--color-success, #5da283);
  outline-offset: 3px;
  animation: coachRespire 2.4s ease-in-out infinite;
}

/* Une fois le dé réellement gardé, le conseil est suivi : on éteint le
   clignotement, qui n'aurait plus rien à demander. */
.die--coach.die--kept {
  animation: none;
  outline-style: solid;
}

@keyframes coachRespire {
  0%, 100% { outline-color: rgba(93, 162, 131, 0.85); }
  50%      { outline-color: rgba(93, 162, 131, 0.3); }
}

[data-theme="light"] .coach,
body.light-mode .coach {
  background: rgba(45, 110, 85, 0.09);
  border-color: rgba(45, 110, 85, 0.3);
}

[data-theme="light"] .coach__titre,
[data-theme="light"] .coach__icone,
body.light-mode .coach__titre,
body.light-mode .coach__icone { color: #2d6e55; }

[data-theme="light"] .coach__texte,
body.light-mode .coach__texte { color: #1a2332; }

[data-theme="light"] .coach__detail,
body.light-mode .coach__detail { color: #5a6472; }

/* Le bloc balai `prefers-reduced-motion` et le réglage « Animations réduites »
   couvrent déjà `coachRespire` : ils neutralisent toute animation du document.
   Rien à ajouter ici — c'est précisément ce que permet le balai. */

/* ═══════════════════════════════════════════════════════════════════════════
   LE COACH SUR UN PETIT ÉCRAN — DEUX MESURES, DEUX CORRECTIONS
   ═══════════════════════════════════════════════════════════════════════════
   Le panneau ci-dessus est calé sur du 375×812. Mesuré sur les deux tailles qui
   comptent pour le défaut signalé — un iPhone dans le navigateur intégré de
   l'appli Google — il produit deux défauts que rien n'annonce.

   ① À 375 PX DE LARGE, LE PANNEAU COUPE SON PROPRE MESSAGE LE PLUS LONG.
      `height: 8.5rem` vaut 136 px et l'entête dit qu'il est « calé sur le
      message le plus long des 8 langues ». Mesuré en reconstituant les vrais
      textes des 8 fichiers, variables interpolées : le pire — l'anglais,
      `resultatCharge` + `pourquoiCharge` — réclame **153 px à 375 px de large**.
      La soupape `overflow-y: auto` s'ouvrait donc en fonctionnement normal : il
      fallait faire défiler deux lignes de conseil. À 390 px, le même texte tient
      en 134 px — d'où la mesure d'origine, juste, mais faite trop large.
      ➡️ La typographie descend d'un cran SOUS 380 px seulement. Le texte
      redescend à 134 px et la boîte n'a pas besoin de grandir. Au-dessus de
      380 px, rien ne change : le texte y tenait déjà, et le réduire n'aurait
      rien gagné (vérifié à 390 px — nombre de lignes identique).

   ② SUR UN ÉCRAN COURT, 136 PX RÉSERVÉS EN PERMANENCE VIDAIENT LE PANNEAU
      DES JOUEURS. Mesuré à 375×540 — un iPhone SE dans l'appli Google : il
      restait **24 px pour 293 px de contenu**. Le pot, les jetons, la phase et
      l'adversaire tenaient dans un bandeau de 24 px qui défile. Les dés et le
      bouton étaient bien là — le correctif de mise en page fait son travail —
      mais l'état de la partie était devenu illisible.
      ➡️ ON NE PREND RIEN À PERSONNE : on récupère le rembourrage vide. La zone
      des dés portait **64 px de rembourrage vertical pour 90 px de dés**, et
      `#dice` en ajoutait 24. Aucun de ces pixels ne porte d'information sur un
      écran qui n'en a plus.
      Relevé à 375×540, 2 joueurs, coach affiché :
        panneau des joueurs   24 px  →  79 px   (contenu 293, défile)
        zone des dés         162 px  → 113 px
        taille des dés        90 px  →  90 px   INCHANGÉE
        bouton « Lancer »   448→504  → 448→504  INCHANGÉ, 36 px de marge
        défilement de .container        0  →  0
      Le coach garde sa hauteur fixe : les dés ne sautent toujours pas, ce qui
      était tout l'objet du choix documenté au-dessus.

   ⚠️ ~~Le seuil est `max-height: 620px`, le même que celui du bandeau vocal~~
   — CE SEUIL ÉTAIT LE DÉFAUT. Voir le bloc « ③ » ci-dessous.               */

@media (max-width: 380px) {
  .coach__texte { font-size: 0.86rem; }
  .coach__detail { font-size: 0.78rem; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   ③ LE SEUIL DE « ② » MANQUAIT LE TÉLÉPHONE DE 120 PX — piège n° 39
   ═══════════════════════════════════════════════════════════════════════════
   Signalé sur l'APPAREIL RÉEL (Redmi 25028RN03Y, 720×1640 densité 320, soit
   360×740 px CSS une fois les barres système déduites) : « on ne voit pas les
   joueurs, il faut utiliser l'ascenseur ». Capture à l'appui — le panneau
   s'arrêtait sur « Pot 20 » et la liste des joueurs était sous le pli.

   Le remède ② juste au-dessus est exactement le bon, avec le bon raisonnement
   et la bonne mesure. Il ne s'appliquait simplement JAMAIS sur ce téléphone :
   son seuil `max-height: 620px` avait été calé sur un iPhone SE à 540 px, et
   le déficit s'étend en réalité jusqu'à ~864 px de hauteur de fenêtre. Ce
   n'est pas le piège n° 38 (deux seuils désolidarisés qui se recouvrent) :
   c'est son symétrique, le n° 39 — une région que personne n'a jamais couverte.

   MESURÉ à 360×740, solo contre l'IA, coach affiché, thème sombre :
     .game__info réclame 314 px, en obtient 190      →  124 px cachés
     #players (les scores) visibles                   →    5 px sur 116
     badge « CHARGE 1/2 » à scrollTop 40              →   42 % visible
   Le badge tranché en deux N'EST PAS un second défaut : c'est ce qu'on voit
   quand on fait défiler le panneau pour atteindre les scores.

   ➡️ ON NE PREND TOUJOURS RIEN À PERSONNE. Comptabilité du blanc disponible
   pour un déficit de 124 px :
     rembourrage vertical de la zone des dés .......  64 px   (dés : 114)
     marges internes de .game__info ................  ~62 px  (dont 24 sous le pot)
     blanc réservé et jamais rempli par le coach ...  37 à 89 px selon le message
   163 px de blanc pur. Aucune information n'est retirée.

   RELEVÉ AVANT / APRÈS, même partie, même téléphone :
     .game__info caché      124 px  →   0 px      ascenseur : OUI → NON
     scores visibles       5 / 116  →  116 / 116
     dés (taille)            98 px  →  98 px      INCHANGÉE
     dés (saut entre deux tours)  0 →  0          la hauteur du coach reste FIXE

   ⚠️ POURQUOI CE BLOC NE TOUCHE PAS AU PRINCIPE DE LA HAUTEUR FIXE.
   Vérifié en le transgressant, à 360×740 : `height: auto` fait bouger les dés
   de 62 px entre le message le plus court et le plus long, et masquer le coach
   pendant le tour de l'IA les fait bouger de 138 px. Les trois remèdes qui
   venaient à l'esprit — masquer quand c'est vide, n'afficher que pendant le
   tour du joueur, passer en `min-height` — sont tous réfutés à la mesure.
   La boîte garde donc une hauteur constante ; c'est sa VALEUR qui change.

   ⚠️ LE BANDEAU VOCAL RESTE À 620 px, ET LES DEUX SEUILS DIVERGENT DÉSORMAIS.
   C'est assumé, pas un oubli : `.voix-barre` se compacte pour gagner 5 px sur
   un écran vraiment court, ce bloc-ci rend 124 px sur un téléphone ordinaire.
   Deux besoins, deux mesures, deux seuils — les confondre est ce qui a produit
   le défaut.

   ⚠️ Le seuil de 880 px est GÉNÉREUX À DESSEIN, et c'est sans risque : la zone
   des dés est `flex: 1 1 auto`, donc là où il reste de la place elle reprend
   par `grow` le rembourrage qu'on lui retire. Vérifié à 1280×800, où le bloc
   s'applique : `.game__info` cachait déjà 0 px avant, il en cache toujours 0.
   La borne de largeur écarte les grands écrans, où le déficit n'existe pas.
   ═══════════════════════════════════════════════════════════════════════════ */

@media (max-height: 880px) {
  /* Le sélecteur d'écran, avec sa spécificité — sinon le `padding: 2rem 1rem`
     du bloc téléphone gagne (piège documenté en styles.css:11423). */
  /* ⚠️ 0,25rem ET PAS 0,5 : LA PHASE DE DÉCHARGE AJOUTE UN BLOC.
     Calibré d'abord sur la charge, où le compte tombait juste. En passant en
     décharge, l'ascenseur est revenu : `#toBeatIndicator` (« À battre : 6-5-5
     (Emma) ») entre alors dans la zone des dés et coûte 24 px, que le panneau
     d'information paie seul. C'est le corollaire du piège n° 38 — un seuil de
     hauteur se cale sur l'état le PLUS CHARGÉ de l'écran, pas sur celui qu'on
     a sous les yeux — et il a fallu jouer une partie entière pour le voir.
     Taille des dés inchangée : 95 px par dé, avant comme après. */
  #gameScreen[style*="display: block"] > .game__dice-area {
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
  }

  #dice { padding: 4px; }

  #gameScreen[style*="display: block"] > .game__actions { gap: 0.6rem; }

  /* ---- Le blanc du panneau d'information ----
     Aucune de ces valeurs ne porte d'information. Relevé à 360×740 : 12 px de
     rembourrage haut et bas, 24 px sous le pot, 12 px de marge basse, 8 px sous
     le badge de phase et 5,6 px au-dessus de « C'est au tour de … ». */
  #gameScreen[style*="display: block"] > .game__info {
    padding-top: 6px;
    padding-bottom: 6px;
    margin-bottom: 4px;
  }

  #gameScreen[style*="display: block"] .game__pot { margin-bottom: 6px; }
  #gameScreen[style*="display: block"] .game__phase-indicator { margin-bottom: 4px; }
  #gameScreen[style*="display: block"] .game__turn { margin-top: 0; }

  /* ⚠️ Le rembourrage rendu ci-dessus s'arrête ici pour les grands écrans.
     Tout ce qui suit — le chrome du coach et le gabarit des indicateurs — est
     borné aux largeurs de téléphone par le bloc `and (max-width: 768px)` juste
     après. Motif mesuré : à 1280×800 le panneau d'information ne cachait
     RIEN, ni avant ni après. Y retirer le mot « COACH » serait une perte sans
     contrepartie, et c'est exactement ce que le piège n° 37 appelle un remède
     qui se voit plus que le mal. */
}

@media (max-height: 880px) and (max-width: 768px) {

  /* ---- Les 24 px qui restent, pris sur le chrome du coach ----
     La ligne d'entête — l'icône, le mot « COACH » et le bouton de fermeture —
     coûte 28 px + 5 px de gouttière et ne dit rien que la boîte ne dise déjà :
     teinte verte, bordure verte, et un conseil rédigé à la deuxième personne.
     Elle passe en absolu dans le coin : le bouton de fermeture reste là où le
     doigt le cherche, avec sa cible de 44 px intacte, et la boîte récupère
     33 px de contenu.

     ⚠️ C'EST CE QUI PERMET DE DESCENDRE À 7rem SANS RIEN ROGNER. La boîte de
     8,5rem coupait DÉJÀ son pire message : reconstitué avec les vrais textes
     des 8 fichiers, l'anglais (`resultatCharge` + `pourquoiCharge`) réclame
     148 px à 360 px de large, pour 136 réservés — la soupape `overflow-y`
     s'ouvrait donc en fonctionnement normal, ce que le commentaire ② croyait
     avoir réglé en descendant la typographie (mesuré juste à 375 px, faux à
     360). Après : 112 px de boîte, et les 8 langues tiennent, débordement 0.
     Le conseil le plus court n'occupe plus qu'un tiers de vide au lieu des
     deux tiers signalés sur l'appareil. */
  #gameScreen[style*="display: block"] .coach {
    height: 7rem;
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
    position: relative;
  }

  #gameScreen[style*="display: block"] .coach__entete {
    position: absolute;
    top: 0.5rem;
    right: 0.8rem;
    margin: 0;
  }

  /* On ne masque que ce qui est décoratif. Le bouton reste. */
  #gameScreen[style*="display: block"] .coach__icone,
  #gameScreen[style*="display: block"] .coach__titre { display: none; }

  #gameScreen[style*="display: block"] .coach__fermer { margin: 0; }

  /* Réserve la colonne du bouton pour que le texte ne passe pas dessous.
     44 px de cible tactile, pas la taille visuelle de la croix.
     ⚠️ LES DEUX PARAGRAPHES, PAS SEULEMENT LE PREMIER. Relevé au `Range`,
     glyphe par glyphe : avec un conseil d'UNE ligne, c'est la première ligne
     du DÉTAIL qui arrive à la hauteur de la croix (y 559-575 contre 549-565)
     et la traversait jusqu'à x 286 pour une croix qui commence à 276. Le cas
     n'apparaît pas avec un conseil de deux lignes — c'est exactement le genre
     de collision qu'un seul échantillon rate. */
  #gameScreen[style*="display: block"] .coach__texte,
  #gameScreen[style*="display: block"] .coach__detail { padding-right: 2.75rem; }

  /* ---- ⚠️ LE VOISIN QUI DÉFAISAIT TOUT : « L'IA RÉFLÉCHIT… » ----
     Trouvé en jouant un tour complet avec un mouchard sur la position des dés,
     APRÈS que tout ce qui précède soit en place. Le coach ne bougeait plus
     d'un pixel (112 px, relevé constant sur une partie entière) et les dés
     sautaient quand même.

     `.game__actions` est incompressible : ce qu'un de ses occupants prend, le
     panneau d'information le perd. Or l'indicateur de l'IA remplace le bouton
     et n'a JAMAIS eu son gabarit — `margin: 10px 0` autour d'une boîte qui
     grandit avec la phrase. `lib/ai-narrator.js:149` écrit qu'il est « déjà
     dimensionné » pour justifier d'y loger la narration : c'est faux, et c'est
     ce qui rendait le défaut invisible à la relecture.

     MESURÉ à 360×740, phrases reconstituées dans les 8 langues :
       bouton (rollBtn / endTurnBtn / continueBtn) ......  56 px
       « L'IA réfléchit… » au repos ..................... 72 px  (+16)
       « Marc garde : 1, 1, 4 (cherche le 1 pour 421) ».. 120 px  (+64, 3 lignes)
     Soit jusqu'à 64 px volés au panneau des joueurs, PLUSIEURS FOIS par tour
     d'IA — la narration change à chaque décision. C'est exactement le défaut
     que la hauteur fixe du coach existe pour empêcher, réintroduit par le bloc
     d'à côté.

     ➡️ Un occupant de `.game__actions` doit avoir le gabarit d'un bouton, sinon
     le bloc « stable » ne l'est pas. Hauteur fixe 3,5rem = 56 px, marges
     supprimées (la gouttière du conteneur suffit), typographie d'un cran plus
     bas pour que deux lignes tiennent.
     APRÈS : une seule hauteur observée, 56 px, et débordement 0 dans les
     8 langues — vérifié sur la phrase la plus longue de chacune.

     ⚠️ Le rembourrage vaut 5 px et pas 6 : à 6 px la boîte sortait à 57 px,
     donc 1 px de saut. Un gabarit se vérifie au pixel, sinon ce n'est pas un
     gabarit. */
  #gameScreen[style*="display: block"] .ai-indicator {
    height: 3.5rem;
    margin: 0;
    padding: 5px 12px;
    font-size: 0.85rem;
  }

  /* Même famille, même remède, mesuré à 58 px (mode en ligne). L'écart est de
     2 px seulement, mais c'est le principe qui compte : un occupant, un
     gabarit. `min-height` n'aurait rien fait — il faut RÉDUIRE, donc `height`.
     C'est la leçon « une correction s'applique à la CLASSE, pas à l'endroit où
     le défaut a été vu » : le défaut a été vu sur l'indicateur de l'IA. */
  #gameScreen[style*="display: block"] .waiting-indicator {
    height: 3.5rem;
    padding: 5px 12px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   MODALE « MES STATISTIQUES » — refonte vague 12
   ---------------------------------------------------------------------------
   Remplace le bloc « NOUVELLE MODALE DE STATISTIQUES » (823 lignes) et le bloc
   « FACE-À-FACE » (68 lignes), tous deux supprimés. Les anciennes classes
   `stats-*` ont disparu du HTML et du JS en même temps : rien ne les référence
   plus, et les mélanger au nouveau jeu `st-*` aurait rejoué la partie de
   cascade décrite au piège n° 6.

   Trois défauts de fond corrigés ici :

   ① LE MODE CLAIR N'EXISTAIT PAS. Tout le bloc était écrit en
     `rgba(245, 245, 220, …)` — du beige — codé en dur. Sur le fond blanc du
     mode clair, les libellés tombaient à 1,3:1 de contraste : illisibles. Seule
     une poignée de règles `.stats-h2h-*` avaient été traitées. Les couleurs
     passent désormais TOUTES par des jetons redéfinis plus bas pour le clair.

   ② DEUX ASCENSEURS IMBRIQUÉS. `.modal__content` et `#statsContent` étaient
     l'un et l'autre en `overflow-y: auto`, avec deux plafonds calculés sur des
     bases différentes (`80vh` contre `calc(85vh - 250px)`). Le pied de modale
     devenait inatteignable selon l'ordre dans lequel on faisait défiler. La
     coquille est maintenant un flex vertical : en-tête, onglets et pied fixes,
     un seul défilement au milieu.

   ③ `vh` AU LIEU DE `svh` (piège n° 22). Sur iOS, `85vh` est la hauteur
     barres rétractées : barres affichées, le pied de la modale tombait sous
     l'écran. Toutes les hauteurs sont en `svh`.

   ⚠️ Les trois animations sont préfixées `stats*` (piège n° 14).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ---------- Jetons de couleur, portés par la modale seule ---------- */

#statsModal {
  --st-surface:    rgba(255, 255, 255, 0.045);
  --st-surface-2:  rgba(255, 255, 255, 0.075);
  --st-border:     rgba(255, 255, 255, 0.10);
  --st-border-2:   rgba(255, 255, 255, 0.16);
  --st-text:       #f2eee4;
  --st-dim:        rgba(242, 238, 228, 0.66);
  /* 0,56 et non 0,42 : compose sur le fond sombre #0d1821, l'alpha d'origine
     ne donnait que 3,7:1 — sous le seuil AA de 4,5:1 pour du texte courant.
     Mesure faite en composant l'alpha, ce que la lecture brute de `color`
     ne fait pas. */
  --st-faint:      rgba(242, 238, 228, 0.56);
  --st-accent:     #dcc7a6;
  --st-accent-2:   #b99a72;
  --st-gold:       #f0c674;
  --st-win:        #5fb389;
  --st-loss:       #e0736c;
  --st-neutral:    #8d97a5;
  --st-track:      rgba(255, 255, 255, 0.10);
  --st-shadow:     0 10px 30px rgba(0, 0, 0, 0.35);
}

body.light-mode #statsModal {
  /* Contrastes visés sur le fond de modale (#ffffff) : texte 15,9:1, libellés
     secondaires 5,7:1, accent 4,8:1, victoire 5,4:1, défaite 5,3:1. Le seuil
     AA du texte courant est 4,5:1. */
  --st-surface:    rgba(15, 23, 42, 0.035);
  --st-surface-2:  rgba(15, 23, 42, 0.06);
  --st-border:     rgba(15, 23, 42, 0.10);
  --st-border-2:   rgba(15, 23, 42, 0.18);
  --st-text:       #17202b;
  --st-dim:        #5b6572;
  --st-faint:      #656e7a;   /* 4,8:1 sur le fond clair #f8f6f0 */
  --st-accent:     #7d6141;
  --st-accent-2:   #8a6d47;
  --st-gold:       #8a6410;
  --st-win:        #2f6a4f;
  --st-loss:       #9b3630;
  --st-neutral:    #64707f;
  --st-track:      rgba(15, 23, 42, 0.09);
  --st-shadow:     0 10px 30px rgba(15, 23, 42, 0.12);
}

/* ---------- Coquille ---------- */

.modal__content--stats {
  display: flex;
  flex-direction: column;
  /* `calc(100% - 2rem)` et non `100%` : mesure a 768 px, la modale collait
     exactement aux deux bords de l'ecran — une fenetre sans marge ne se lit
     plus comme une fenetre. Sous 600 px elle est volontairement plein ecran,
     et la regle plus bas reprend la main. */
  width: min(780px, calc(100% - 2rem));
  max-width: min(780px, calc(100% - 2rem));
  /* `svh` et non `vh` : voir ③ ci-dessus. Sur un écran sans barre rétractable
     (bureau, Android plein écran, Capacitor) les deux valent la même chose. */
  max-height: min(92svh, 940px);
  overflow: hidden;
  color: var(--st-text);
  box-shadow: var(--st-shadow);
}

.modal__content--stats .modal__header {
  flex: 0 0 auto;
  padding-top: max(var(--spacing-md), var(--safe-area-top));
  border-bottom-color: var(--st-border);
}

.st-title {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0;
  font-size: 1.1rem;
  color: var(--st-text);
}

.st-title .icon { color: var(--st-accent); flex-shrink: 0; }

/* ---------- Onglets ---------- */

.st-tabs {
  flex: 0 0 auto;
  display: grid;
  /* 2 × 2 sous 480 px : à 320 px de large chaque onglet dispose de ~150 px, de
     quoi afficher « Vue d'ensemble » en entier. L'ancienne version tassait les
     quatre onglets sur une ligne à 0,58rem. */
  grid-template-columns: repeat(2, 1fr);
  gap: 0.25rem;
  padding: 0.4rem 0.5rem 0;
  border-bottom: 1px solid var(--st-border);
}

/* ⚠️ INDISPENSABLE. `.st-tabs { display: grid }` est une règle d'auteur : elle
   bat le `[hidden] { display: none }` de la feuille du navigateur, qui n'a que
   la priorité « user agent ». Sans cette ligne, poser `hidden` sur la barre
   d'onglets ne la cache PAS — même famille de piège que le n° 23. */
.st-tabs[hidden] { display: none; }

.st-tab {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  /* 44 px : la cible tactile minimale du projet (--min-touch-target). */
  min-height: 44px;
  padding: 0.5rem 0.4rem;
  background: transparent;
  border: none;
  border-radius: 10px 10px 0 0;
  border-bottom: 2px solid transparent;
  color: var(--st-dim);
  font-size: 0.78rem;
  font-weight: 600;
  font-family: inherit;
  line-height: 1.15;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
  min-width: 0;
}

.st-tab > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.st-tab .icon { flex-shrink: 0; }

.st-tab:hover { background: var(--st-surface-2); color: var(--st-text); }

.st-tab:focus-visible {
  outline: 2px solid var(--st-accent);
  outline-offset: -2px;
}

.st-tab--active {
  background: var(--st-surface-2);
  border-bottom-color: var(--st-accent);
  color: var(--st-accent);
}

/* ---------- Zone défilante (unique) ---------- */

.modal__content--stats #statsContent {
  flex: 1 1 auto;
  min-height: 0;          /* sans quoi un enfant long empêche le flex de rétrécir */
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 0.85rem;
}

.st-panel {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  animation: statsApparition 0.28s ease both;
}

/* ---------- Bloc héros : anneau + victoires/défaites ---------- */

.st-hero {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: clamp(0.75rem, 4vw, 1.5rem);
  padding: clamp(0.85rem, 3vw, 1.25rem);
  background: linear-gradient(135deg, var(--st-surface-2), var(--st-surface));
  border: 1px solid var(--st-border);
  border-radius: 16px;
}

.st-ring {
  position: relative;
  width: clamp(104px, 30vw, 140px);
  aspect-ratio: 1;
  flex-shrink: 0;
}

.st-ring svg { width: 100%; height: 100%; display: block; }

.st-ring__track {
  fill: none;
  stroke: var(--st-track);
  stroke-width: 9;
}

.st-ring__fill {
  fill: none;
  stroke: var(--st-accent);
  stroke-width: 9;
  stroke-linecap: round;
  /* L'arc se remplit depuis zéro à l'ouverture : c'est la seule animation
     décorative de l'écran, et elle porte l'information (la progression). */
  animation: statsAnneauRemplissage 0.9s cubic-bezier(0.22, 1, 0.36, 1) both;
}

.st-ring--vide .st-ring__fill { display: none; }

.st-ring__center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1rem;
  text-align: center;
  pointer-events: none;
}

.st-ring__value {
  font-size: clamp(1.5rem, 6vw, 2.1rem);
  font-weight: 800;
  line-height: 1;
  color: var(--st-text);
  font-variant-numeric: tabular-nums;
}

.st-ring__pct { font-size: 0.55em; font-weight: 700; margin-inline-start: 0.05em; }

.st-ring__label {
  /* ⚠️ Le plancher etait a 0,58rem : mesure a 320 px de large, le libelle
     « Taux de victoire » tombait a 9,3 px. On ne lit pas un texte de 9 px sur
     un telephone, et c'est celui qui dit ce que represente le grand chiffre. */
  font-size: clamp(0.66rem, 2.4vw, 0.7rem);
  color: var(--st-dim);
  font-weight: 600;
  max-width: 86%;
  line-height: 1.15;
}

.st-hero__side {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
}

.st-hero__stat {
  display: grid;
  grid-template-columns: auto auto 1fr;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.7rem;
  background: var(--st-surface);
  border: 1px solid var(--st-border);
  border-radius: 11px;
  min-width: 0;
}

.st-hero__stat b {
  font-size: 1.25rem;
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.st-hero__stat span {
  font-size: 0.75rem;
  color: var(--st-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.st-hero__stat--win  { border-inline-start: 3px solid var(--st-win); }
.st-hero__stat--win  .icon, .st-hero__stat--win  b { color: var(--st-win); }
.st-hero__stat--loss { border-inline-start: 3px solid var(--st-loss); }
.st-hero__stat--loss .icon, .st-hero__stat--loss b { color: var(--st-loss); }

/* ---------- Tuiles ---------- */

.st-tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.5rem;
}

.st-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  padding: 0.7rem 0.5rem;
  background: var(--st-surface);
  border: 1px solid var(--st-border);
  border-radius: 13px;
  text-align: center;
  min-width: 0;
  transition: border-color 0.2s ease, transform 0.2s ease;
}

.st-tile:hover { border-color: var(--st-border-2); transform: translateY(-1px); }

.st-tile__icon { color: var(--st-accent-2); display: flex; }

.st-tile__value {
  font-size: clamp(1.15rem, 5vw, 1.5rem);
  font-weight: 800;
  line-height: 1.05;
  color: var(--st-text);
  font-variant-numeric: tabular-nums;
}

.st-tile__label {
  font-size: 0.68rem;
  color: var(--st-dim);
  line-height: 1.2;
  font-weight: 500;
}

.st-tile--or .st-tile__icon,    .st-tile--or .st-tile__value    { color: var(--st-gold); }
.st-tile--chaud .st-tile__icon, .st-tile--chaud .st-tile__value { color: var(--st-loss); }

/* ---------- Carte de section ---------- */

.st-card {
  padding: 0.85rem;
  background: var(--st-surface);
  border: 1px solid var(--st-border);
  border-radius: 14px;
}

.st-card__title {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.7rem;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--st-accent);
}

.st-card__title .icon { flex-shrink: 0; }

/* ---------- Barre empilée victoires / défaites / observées ---------- */

.st-split__bar {
  display: flex;
  height: 12px;
  border-radius: 99px;
  overflow: hidden;
  background: var(--st-track);
  gap: 2px;
}

.st-split__seg {
  display: block;
  height: 100%;
  min-width: 3px;
  animation: statsBarreEtirement 0.6s cubic-bezier(0.22, 1, 0.36, 1) both;
  transform-origin: left center;
}

[dir="rtl"] .st-split__seg { transform-origin: right center; }

.st-split__seg--win  { background: var(--st-win); }
.st-split__seg--loss { background: var(--st-loss); }
.st-split__seg--none { background: var(--st-neutral); }

.st-split__legend {
  list-style: none;
  margin: 0.7rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.st-split__legend li {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.78rem;
  color: var(--st-dim);
}

.st-split__legend b {
  color: var(--st-text);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

.st-split__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.st-split__pct {
  min-width: 3ch;
  text-align: end;
  font-variant-numeric: tabular-nums;
  color: var(--st-faint);
}

.st-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  display: block;
}

.st-dot--win  { background: var(--st-win); }
.st-dot--loss { background: var(--st-loss); }
.st-dot--none { background: var(--st-neutral); }

/* ---------- Lignes clé / valeur ---------- */

.st-rows { margin: 0; }

.st-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--st-border);
}

.st-row:last-child  { border-bottom: none; padding-bottom: 0; }
.st-row:first-child { padding-top: 0; }

.st-row dt {
  font-size: 0.8rem;
  color: var(--st-dim);
  min-width: 0;
}

.st-row dd {
  margin: 0;
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--st-text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---------- Records ---------- */

.st-records {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.4rem;
}

.st-record {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.7rem;
  background: var(--st-surface-2);
  border: 1px solid var(--st-border);
  border-radius: 11px;
}

.st-record__icon { color: var(--st-accent-2); display: flex; }
.st-record--vide .st-record__icon { color: var(--st-faint); }

.st-record__label {
  font-size: 0.8rem;
  color: var(--st-dim);
  line-height: 1.2;
  min-width: 0;
}

.st-record__value {
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
  white-space: nowrap;
}

.st-record__value b {
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--st-text);
  font-variant-numeric: tabular-nums;
}

.st-record__value small { font-size: 0.68rem; color: var(--st-faint); }
.st-record__dash { color: var(--st-faint); font-size: 1.05rem; font-weight: 700; }

/* ---------- Distribution des combinaisons ---------- */

.st-combos {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.st-combo {
  display: grid;
  /* Le libellé prend une largeur fixe pour que les sept barres démarrent au
     même endroit : c'est ce qui permet de comparer leurs longueurs. */
  grid-template-columns: minmax(4.5rem, 7rem) 1fr auto auto;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.76rem;
}

.st-combo__name {
  color: var(--st-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}

.st-combo__track {
  display: block;
  height: 9px;
  border-radius: 99px;
  background: var(--st-track);
  overflow: hidden;
  min-width: 0;
}

.st-combo__fill {
  display: block;
  height: 100%;
  border-radius: 99px;
  /* Rampe séquentielle : les combinaisons forment une ÉCHELLE de valeur
     (rang 1 = 421, rang 7 = autre), pas des catégories indépendantes. Une
     teinte unique qui pâlit dit « ordonné » ; sept couleurs vives diraient
     « sans ordre » et se liraient moins bien. `--st-rang` vaut 0 à 6. */
  background: color-mix(in srgb, var(--st-gold) calc(100% - var(--st-rang, 0) * 13%), var(--st-neutral));
  animation: statsBarreEtirement 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
  transform-origin: left center;
  min-width: 2px;
}

[dir="rtl"] .st-combo__fill { transform-origin: right center; }

.st-combo--zero .st-combo__fill { min-width: 0; }
.st-combo--zero .st-combo__name,
.st-combo--zero .st-combo__count,
.st-combo--zero .st-combo__pct { opacity: 0.5; }

.st-combo__count {
  font-weight: 800;
  color: var(--st-text);
  font-variant-numeric: tabular-nums;
  min-width: 2ch;
  text-align: end;
}

.st-combo__pct {
  color: var(--st-faint);
  font-variant-numeric: tabular-nums;
  min-width: 3.5ch;
  text-align: end;
}

/* ---------- Modes de jeu ---------- */

.st-mode {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 0.65rem;
  padding: 0.65rem 0;
  border-bottom: 1px solid var(--st-border);
}

.st-mode:last-child   { border-bottom: none; padding-bottom: 0; }
.st-mode:first-of-type { padding-top: 0; }
.st-mode--vide { opacity: 0.55; }

.st-mode__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: var(--st-surface-2);
  border: 1px solid var(--st-border);
  flex-shrink: 0;
}

.st-mode__icon--solo   { color: #6ea8e8; }
.st-mode__icon--bar    { color: #e0a05e; }
.st-mode__icon--online { color: var(--st-win); }

body.light-mode .st-mode__icon--solo { color: #2563eb; }
body.light-mode .st-mode__icon--bar  { color: #a8560c; }

.st-mode__body { min-width: 0; }

.st-mode__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.35rem;
}

.st-mode__name {
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--st-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.st-mode__pct {
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--st-accent);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

.st-mode__track {
  display: block;
  height: 7px;
  border-radius: 99px;
  background: var(--st-track);
  overflow: hidden;
}

.st-mode__fill {
  display: block;
  height: 100%;
  border-radius: 99px;
  animation: statsBarreEtirement 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
  transform-origin: left center;
}

[dir="rtl"] .st-mode__fill { transform-origin: right center; }

.st-mode__fill--solo   { background: #6ea8e8; }
.st-mode__fill--bar    { background: #e0a05e; }
.st-mode__fill--online { background: var(--st-win); }

.st-mode__detail {
  margin-top: 0.3rem;
  font-size: 0.7rem;
  color: var(--st-faint);
}

/* ---------- Face-à-face ---------- */

.st-h2h {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.st-h2h__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.3rem;
}

.st-h2h__name {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--st-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.st-h2h__score {
  display: flex;
  align-items: baseline;
  gap: 0.22rem;
  font-size: 1rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

.st-h2h__won  { color: var(--st-win); }
.st-h2h__lost { color: var(--st-loss); }
.st-h2h__sep  { color: var(--st-faint); font-weight: 600; }

.st-h2h__track {
  display: block;
  height: 6px;
  border-radius: 99px;
  background: var(--st-loss);
  overflow: hidden;
}

.st-h2h__fill {
  display: block;
  height: 100%;
  background: var(--st-win);
  border-radius: 99px;
  animation: statsBarreEtirement 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
  transform-origin: left center;
}

[dir="rtl"] .st-h2h__fill { transform-origin: right center; }

/* ---------- Historique ---------- */

.st-history {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.st-history__item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.65rem;
  padding: 0.6rem 0.7rem;
  background: var(--st-surface-2);
  border: 1px solid var(--st-border);
  border-inline-start: 3px solid var(--st-neutral);
  border-radius: 11px;
}

.st-history__item--win  { border-inline-start-color: var(--st-win); }
.st-history__item--loss { border-inline-start-color: var(--st-loss); }

.st-history__icon { display: flex; color: var(--st-neutral); padding-top: 1px; }
.st-history__item--win  .st-history__icon { color: var(--st-win); }
.st-history__item--loss .st-history__icon { color: var(--st-loss); }

.st-history__body { min-width: 0; }

.st-history__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.st-history__state {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--st-text);
}

.st-history__item--win  .st-history__state { color: var(--st-win); }
.st-history__item--loss .st-history__state { color: var(--st-loss); }

.st-history__date {
  font-size: 0.68rem;
  color: var(--st-faint);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.st-history__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.5rem;
  margin-top: 0.2rem;
  font-size: 0.7rem;
  color: var(--st-dim);
}

/* Séparateur en pastille plutôt qu'un « • » écrit dans le HTML : il disparaît
   proprement quand une métadonnée manque, et se replie correctement. */
.st-history__meta > span + span::before {
  content: '';
  display: inline-block;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.5;
  vertical-align: middle;
  margin-inline-end: 0.45rem;
}

.st-history__winner {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  color: var(--st-accent-2);
  font-weight: 600;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- États vides et notes ---------- */

.st-note {
  margin: 0.55rem 0 0;
  font-size: 0.72rem;
  line-height: 1.45;
  color: var(--st-faint);
}

.st-card > .st-note:first-of-type:last-child { margin-top: 0; }

.st-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 260px;
  padding: 2rem 1.25rem;
  text-align: center;
}

.st-empty__icon { color: var(--st-accent-2); opacity: 0.7; }

.st-empty__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--st-text);
}

.st-empty__text {
  margin: 0;
  max-width: 34ch;
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--st-dim);
}

/* ---------- Pied ---------- */

.modal__content--stats .st-footer {
  flex: 0 0 auto;
  flex-wrap: wrap;
  gap: 0.5rem;
  border-top-color: var(--st-border);
  padding-bottom: max(var(--spacing-md), var(--safe-area-bottom));
}

.st-footer .btn {
  flex: 1 1 auto;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
}

.st-reset {
  background: rgba(224, 115, 108, 0.10);
  color: var(--st-loss);
  border: 1px solid rgba(224, 115, 108, 0.45);
}

.st-reset:hover { background: rgba(224, 115, 108, 0.18); }

body.light-mode .st-reset {
  background: rgba(155, 54, 48, 0.08);
  border-color: rgba(155, 54, 48, 0.35);
}

/* ---------- Animations (préfixées — piège n° 14) ---------- */

@keyframes statsApparition {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

@keyframes statsBarreEtirement {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@keyframes statsAnneauRemplissage {
  from { stroke-dashoffset: 282.743; }
}

/* Le réglage « Animations réduites » du jeu et la préférence système coupent
   les trois animations. Elles sont décoratives : la valeur finale reste. */
@media (prefers-reduced-motion: reduce) {
  .st-panel,
  .st-split__seg,
  .st-combo__fill,
  .st-mode__fill,
  .st-h2h__fill,
  .st-ring__fill {
    animation: none !important;
  }
}

.animations-reduites .st-panel,
.animations-reduites .st-split__seg,
.animations-reduites .st-combo__fill,
.animations-reduites .st-mode__fill,
.animations-reduites .st-h2h__fill,
.animations-reduites .st-ring__fill {
  animation: none !important;
}

/* ═══════════════════ Responsive ═══════════════════ */

/* Téléphone : la modale prend tout l'écran. Une carte flottante à 95vw laissait
   des marges inutiles alors que le contenu manquait de place, et l'ancien
   plafond `calc(85vh - 250px)` réservait 250 px à un en-tête qui n'en fait que
   150 — le contenu était comprimé pour rien. */
@media (max-width: 600px) {
  #statsModal .modal__content--stats {
    width: 100%;
    max-width: 100%;
    height: 100svh;
    max-height: 100svh;
    border-radius: 0;
    margin: 0;
  }

  .modal__content--stats #statsContent { padding: 0.7rem; }
  .st-title { font-size: 1rem; }

  /* ⚠️ En-tete + onglets prenaient 204 px sur un ecran de 568 — 36 % de la
     hauteur avant d'avoir montre un seul chiffre. L'en-tete heritait de
     `padding: var(--spacing-md)`, soit 24 px en haut ET en bas autour d'un
     titre de 16 px. Mesure a 320 x 568 : 97 px pour l'en-tete seul. */
  .modal__content--stats .modal__header {
    padding: 0.7rem 0.85rem;
    padding-top: max(0.7rem, var(--safe-area-top));
  }

  /* Le fond de modale couvre desormais tout l'ecran : la bordure de 1 px du
     mode clair dessinait un liseré tout autour de l'ecran. */
  body.light-mode #statsModal { border: none; }

  /* ⚠️ Le pied mesurait 155 px de haut sur un iPhone — mesuré à 375 px de large.
     Les deux boutons héritaient du remplissage de `.btn` (≈50 px chacun) et
     `Réinitialiser les stats` ne tenant pas à côté de `Fermer`, ils passaient
     l'un sous l'autre : 24 px de marge en haut, deux boutons, 8 px d'écart,
     24 px en bas. C'est un cinquième de l'écran pris par deux boutons dont un
     ne sert qu'une fois. Ils tiennent maintenant sur une seule ligne. */
  .modal__content--stats .st-footer {
    padding: 0.7rem 0.75rem;
    padding-bottom: max(0.7rem, var(--safe-area-bottom));
    flex-wrap: nowrap;
  }

  .st-footer .btn {
    flex: 0 1 auto;
    padding: 0.55rem 0.7rem;
    font-size: 0.8rem;
    min-height: 44px;
  }

  /* « Fermer » est court et c'est l'action attendue : il garde sa largeur et
     c'est la réinitialisation qui se comprime si le libellé est long. */
  .st-footer .st-reset { flex: 1 1 auto; min-width: 0; }
  .st-footer .st-reset span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* Très petits écrans (320–360 px) : on desserre uniquement ce qui déborde. */
@media (max-width: 360px) {
  .st-tab { font-size: 0.72rem; gap: 0.3rem; padding-inline: 0.25rem; }
  .st-hero { grid-template-columns: 1fr; justify-items: center; }
  .st-hero__side { width: 100%; }
  .st-combo { grid-template-columns: minmax(3.8rem, 5.5rem) 1fr auto auto; gap: 0.35rem; }
  .st-tile__label { font-size: 0.63rem; }
}

/* À partir de 481 px, les quatre onglets tiennent sur une seule ligne. */
@media (min-width: 481px) {
  .st-tabs { grid-template-columns: repeat(4, 1fr); }
  .st-tab { font-size: 0.8rem; }
}

/* Tablette et bureau : on profite de la largeur au lieu d'étirer les éléments.
   Quatre tuiles sur une ligne, records sur deux colonnes. */
@media (min-width: 601px) {
  .modal__content--stats { border-radius: var(--radius-lg); }
  .modal__content--stats #statsContent { padding: 1.1rem; }
  .st-panel { gap: 0.85rem; }
  .st-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.7rem; }
  .st-records { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.5rem; }
  .st-card { padding: 1.1rem; }
  .st-card__title { font-size: 0.86rem; }
  .st-tab { font-size: 0.85rem; min-height: 48px; }
  .st-hero { gap: 1.75rem; padding: 1.35rem; }
  .st-hero__side { flex-direction: row; }
  .st-hero__stat { flex: 1 1 0; }
  .st-combo { font-size: 0.82rem; grid-template-columns: minmax(6rem, 8.5rem) 1fr auto auto; }
  .st-footer .btn { flex: 0 0 auto; }
}

/* Écran bas (téléphone en paysage) : le héros mangeait toute la hauteur. */
@media (max-height: 560px) and (min-width: 481px) {
  .st-ring { width: clamp(84px, 18vh, 104px); }
  .st-hero { padding: 0.7rem; }
  .st-tab { min-height: 40px; }
  .st-empty { min-height: 160px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   INTERRUPTEUR « APPRENTISSAGE » DU MENU — vague 12
   ---------------------------------------------------------------------------
   Mêmes signaux visuels que l'interrupteur du son juste au-dessus : l'icône
   s'atténue et le badge doré s'éteint quand le coach est coupé. Le doré marque
   un état ACTIF ; un « Coupé » en doré se lirait comme une promotion du
   silence.

   ⚠️ Ces règles sont indispensables au sens propre : `#coachToggleBtn` n'a
   qu'UNE icône (le cerveau), pas deux comme le son, donc rien ne distinguerait
   l'état allumé de l'état éteint sans elles — le joueur ne pourrait pas savoir
   s'il vient d'allumer ou d'éteindre le coach.
   ═══════════════════════════════════════════════════════════════════════════ */

#coachToggleBtn.is-muet .header-menu__icon svg {
  opacity: 0.55;
}

#coachToggleBtn.is-muet .header-menu__badge {
  background: rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.75);
}

body.light-mode #coachToggleBtn.is-muet .header-menu__badge {
  background: rgba(0, 0, 0, 0.1);
  color: rgba(0, 0, 0, 0.65);
}

/* ═══════════════════════════════════════════════════════════════════════════
   MENU DE L'EN-TÊTE — le panneau doit défiler quand l'écran est bas
   ---------------------------------------------------------------------------
   ⚠️ DÉFAUT MESURÉ, PAS THÉORIQUE. `.header-menu__content` est en
   `overflow: hidden` sans aucun plafond de hauteur. En paysage sur téléphone
   (640 × 360) et en cours de partie — donc avec l'item « Historique » visible,
   soit huit items — le dernier tombait **153 px sous le bas de l'écran**, et
   `overflow: hidden` interdisait d'y accéder : le bouton était simplement
   perdu. C'est le bouton « Paramètres » qui se trouvait en dernier.

   Le défaut préexistait (à six items il en manquait déjà 55), mais l'ajout de
   l'interrupteur d'apprentissage et des Paramètres le rendait certain.

   `svh` et non `vh` : sur iOS, `vh` est la hauteur barres rétractées, donc le
   plafond serait trop haut précisément quand la place manque (piège n° 22).
   `top: 4.5rem` sépare le panneau du haut de l'écran, d'où le retrait.
   ═══════════════════════════════════════════════════════════════════════════ */

.header-menu__content {
  max-height: calc(100svh - 6rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

/* ═══════════════════════════════════════════════════════════════════════════
   CARTE DE BIENVENUE — premier lancement
   ---------------------------------------------------------------------------
   Volontairement sobre : elle arrive juste après la bannière de consentement,
   et deux surfaces tape-à-l'œil d'affilée donneraient l'impression d'un jeu
   qui réclame avant de servir. Le seul accent est sur le bouton principal.

   Hauteurs et marges en `svh` (piège n° 22) : sur iOS, `vh` est la hauteur
   barres rétractées, donc la carte pourrait déborder précisément quand les
   barres sont affichées.
   ═══════════════════════════════════════════════════════════════════════════ */

.bienvenue {
  position: fixed;
  inset: 0;
  z-index: 200;            /* au-dessus de la bannière de consentement (150) */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
}

/* ⚠️ `.bienvenue` porte `display: flex` : une règle d'auteur bat le
   `[hidden] { display: none }` du navigateur, qui n'a que la priorité « user
   agent ». Sans cette ligne, `hidden` ne cacherait rien — même famille que le
   piège n° 23. */
.bienvenue[hidden] { display: none; }

.bienvenue__voile {
  position: absolute;
  inset: 0;
  background: rgba(6, 12, 18, 0.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.bienvenue__carte {
  position: relative;
  width: min(400px, 100%);
  max-height: 88svh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.7rem;
  padding: clamp(1.4rem, 5vw, 2rem) clamp(1.2rem, 5vw, 1.75rem);
  background: var(--color-bg);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 18px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  animation: bienvenueEntree 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}

body.light-mode .bienvenue__carte {
  border-color: rgba(15, 23, 42, 0.12);
  box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22);
}

.bienvenue__icone {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 16px;
  background: rgba(220, 199, 166, 0.14);
  color: #dcc7a6;
  margin-bottom: 0.2rem;
}

body.light-mode .bienvenue__icone {
  background: rgba(125, 97, 65, 0.10);
  color: #7d6141;
}

.bienvenue__titre {
  margin: 0;
  font-size: clamp(1.15rem, 5vw, 1.4rem);
  font-weight: 700;
  color: var(--color-text);
  text-wrap: balance;
}

.bienvenue__texte {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--color-text-dim);
  max-width: 34ch;
}

.bienvenue__principal {
  width: 100%;
  min-height: 50px;
  margin-top: 0.5rem;
  font-size: 1rem;
  /* ⚠️ `flex-shrink: 0` : la carte est un conteneur flex en colonne, plafonné
     à 88svh. Sur un écran bas, les enfants se comprimeraient au prorata et les
     deux boutons passeraient SOUS la cible tactile de 44 px — mesuré à 43 px
     avant cette ligne. Une carte qui défile vaut mieux qu'un bouton trop petit. */
  flex-shrink: 0;
}

/* Le tutoriel reste à un tap, mais il ne doit pas rivaliser avec la voie
   rapide : c'est un lien, pas un second bouton. La cible tactile fait quand
   même 44 px — le lien est un vrai choix, pas un piège. */
.bienvenue__secondaire {
  min-height: 46px;
  flex-shrink: 0;
  padding: 0.5rem 0.75rem;
  background: none;
  border: none;
  color: var(--color-text-dim);
  font: inherit;
  font-size: 0.86rem;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.bienvenue__secondaire:hover { color: var(--color-text); }

.bienvenue__secondaire:focus-visible,
.bienvenue__principal:focus-visible {
  outline: 2px solid #dcc7a6;
  outline-offset: 2px;
}

@keyframes bienvenueEntree {
  from { opacity: 0; transform: translateY(14px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .bienvenue__carte { animation: none !important; }
}

.animations-reduites .bienvenue__carte { animation: none !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   PIED DE PAGE DE L'ACCUEIL — liens vers les pages de contenu
   ---------------------------------------------------------------------------
   ⚠️ Discret par nécessité. L'écran d'accueil a été compacté de 1 872 px à
   957 px en vague 4, et ce bloc ne doit pas défaire ce travail : petites
   capitales, pas d'icône, pas de fond, une seule ligne quand la largeur le
   permet.

   Il n'est pas décoratif pour autant — c'est la seule passerelle entre
   l'application et les pages de contenu, sans laquelle un robot d'indexation
   (et le relecteur d'AdSense) ne trouve rien à lire sur ce site.
   ═══════════════════════════════════════════════════════════════════════════ */

.home-liens {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.35rem 1rem;
  margin: 1.4rem auto 0;
  max-width: 34rem;
}

.home-liens a {
  color: rgba(245, 245, 220, 0.55);
  font-size: 0.78rem;
  text-decoration: none;
  padding: 0.25rem 0;
  border-bottom: 1px solid transparent;
}

.home-liens a:hover,
.home-liens a:focus-visible {
  color: var(--color-gold, #f4d03f);
  border-bottom-color: currentColor;
}

.home-pitch {
  max-width: 46ch;
  margin: 0.7rem auto 0;
  text-align: center;
  font-size: 0.76rem;
  line-height: 1.5;
  color: rgba(245, 245, 220, 0.42);
}

body.light-mode .home-liens a { color: rgba(15, 23, 42, 0.55); }
body.light-mode .home-pitch   { color: rgba(15, 23, 42, 0.48); }

/* ═══════════════════════════════════════════════════════════════════════════
   ACCUEIL SUR FENÊTRE BUREAU COURTE — LE DERNIER ASCENSEUR
   ---------------------------------------------------------------------------
   Une fois le débordement FANTÔME de 112 px supprimé (voir le bloc
   `@media (max-width: 1024px)` plus haut), il restait un débordement RÉEL sur
   les fenêtres bureau peu hautes. Mesuré à 1280 px de large :

     880 px de haut ......  0 px  — tient
     820 px de haut ...... 37 px  — déborde
     700 px de haut ..... 120 px  — déborde

   Le seuil est à ~860 px de hauteur utile. Un écran 1920x1080 donne ~940 px et
   passe ; un portable 1366x768 donne ~660 px et ne passe pas. Cette seconde
   machine reste courante, donc le défaut est réel.

   ⚠️ CE BLOC NE S'APPLIQUE QU'AUX FENÊTRES COURTES (`max-height: 880px`).
   Sur un grand écran, la mise en page respire exactement comme avant : les
   valeurs ci-dessous ne sont jamais lues.

   ⚠️ CE QUI N'EST PAS TOUCHÉ, ET POURQUOI.
   Les boutons gardent leurs zones tactiles. Le projet est passé de 249 à 61
   cibles sous 48 px, et rogner ici reprendrait cette dette. Seuls les
   ESPACEMENTS et le logo se resserrent — le logo parce qu'il est décoratif,
   les espacements parce qu'ils sont du vide.

   ⚠️ `.home-liens` ET `.home-pitch` SONT RÉDUITS, JAMAIS MASQUÉS.
   Ce sont les deux seules passerelles vers les pages de contenu, et le site a
   déjà été refusé par AdSense pour « contenu à faible valeur informative ».
   Un `display: none` ferait disparaître le seul texte indexable de l'accueil.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ⚠️ SEUIL ABAISSE DE 1025 A 769 PX LE 10 AOUT, pour la meme raison que le bloc
   jumeau plus haut : une fenetre non maximisee de 769 a 1024 px n'etait couverte
   par AUCUN des deux, et c'est le cas courant sur un ordinateur. Les deux blocs
   vont ensemble — le premier supprime le debordement fantome, celui-ci resserre
   les espacements sur les fenetres courtes. Abaisser l'un sans l'autre laisse
   72 px de debordement a 1024x768, mesure.

   ⚠️ SEUIL DE HAUTEUR RELEVE DE 880 A 1000 PX — SIGNALEMENT DU PROPRIETAIRE,
   « l'ascenseur vertical est revenu quand il y a reprendre une partie ».
   Le bloc `#resumeGameWrap` ne s'affiche QUE si une partie est en cours, et il
   coute 105 px (96 de haut + 9 de marge) — a cette largeur il tient sur deux
   lignes, pas une. Or entre 881 et ~1000 px de haut, aucun des deux paliers ne
   le compensait : le doux ne suffit pas, le fort ne se declenchait pas encore.

   ⚠️ CE TROU N'EST PAS UNE SUITE DE L'EXTENSION A 769 PX — il est d'origine.
   Mesure a 1280x892, donc en pleine plage >= 1025 px et sans rien de ce qui a
   ete ajoute ce jour : **93 px de debordement, ascenseur visible**. Le seuil de
   880 px avait ete cale sur un accueil SANS partie en cours, le seul etat dans
   lequel il avait ete mesure.
     892 px de haut, avec Reprendre .... avant 93 px  ->  apres 0
    1000 px de haut, avec Reprendre .... avant 93 px  ->  apres 0, logo a 70 px
    1060 px de haut, avec Reprendre .... 0 px sans compression : le seuil de
                                         1000 est donc le bon, au-dela l'accueil
                                         tient seul et garde son logo pleine
                                         taille (100,7 px). */
@media (min-width: 769px) and (max-height: 1000px) {

  .screen--setup .game-logo {
    padding-top: clamp(0.1rem, 0.7vh, 2rem);
    padding-bottom: clamp(0.1rem, 0.7vh, 2rem);
    margin-bottom: clamp(0.1rem, 0.7vh, 1.5rem);
  }

  .screen--setup .logo-text {
    font-size: clamp(2rem, 7vh, 8rem);
    line-height: 1;
  }

  .screen--setup .logo-dice-icon {
    margin-bottom: clamp(0.1rem, 0.6vh, 1rem);
  }

  .screen--setup .logo-dice-icon svg {
    width: clamp(20px, 3vh, 48px);
    height: clamp(20px, 3vh, 48px);
  }

  .screen--setup .home-bottom-nav {
    padding-top: clamp(0.1rem, 0.6vh, 1rem);
    padding-bottom: clamp(0.1rem, 0.6vh, 1rem);
  }

  .screen--setup .ai-level-selector {
    margin-top: clamp(0.1rem, 0.6vh, 1rem);
    margin-bottom: clamp(0.1rem, 0.6vh, 1rem);
  }

  /* Le pied de page se resserre mais reste lisible : c'est lui que le robot lit. */
  .screen--setup .home-liens {
    margin-top: clamp(0.3rem, 1.2vh, 1.4rem);
    gap: 0.2rem 0.9rem;
  }

  .screen--setup .home-pitch {
    margin-top: clamp(0.2rem, 0.9vh, 0.7rem);
    font-size: 0.72rem;
    line-height: 1.4;
    /* 46ch sur un écran large tient en TROIS lignes pour ~25 mots. En élargir
       la mesure les ramène à deux et rend 16 px sans toucher à un seul mot :
       c'est la dernière économie qui ne coûte rien. */
    max-width: 68ch;
  }

  /* Les interstices entre boutons, et rien d'autre. Les boutons eux-mêmes ne
     bougent pas d'un pixel — mesuré, la plus petite cible reste à 44 px. */
  .screen--setup #playNowBtn,
  .screen--setup .home-multiplayer-btns {
    margin-bottom: clamp(0.2rem, 0.8vh, 0.75rem);
  }

  .screen--setup #resumeGameWrap {
    margin-bottom: clamp(0.15rem, 0.5vh, 0.375rem);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   LEXIQUE DU 421 — les mots du jeu, à portée de tap (lib/lexique.js)
   Ajouté le 10 août 2026. Ligne « Lexique cliquable en jeu » de PLAN_ACTION.md.

   ⚠️ CE BLOC N'AJOUTE PAS UN PIXEL À L'ÉCRAN DE JEU, ET C'EST MESURÉ.
   À 375 × 812 avec deux joueurs, le bas de `.game__actions` est à 788 px pour
   une fenêtre de 812 : il reste 24 px. La fenêtre du lexique est donc en
   `position: fixed` (hors flux), et les mots consultables sont des éléments
   qui occupaient déjà leur place — seul un `::after` de 13 px s'y ajoute.

   ⚠️ LE MARQUEUR EST UN `::after`, PAS UN ÉLÉMENT ENFANT, ET C'EST OBLIGATOIRE.
   `.game__pot-label` porte `data-i18n="game.pot"` : `applyTranslations()` y
   réécrit `textContent` à chaque changement de langue et effacerait n'importe
   quel enfant (pièges n° 5 et n° 29). Un pseudo-élément, lui, survit.

   ⚠️ NOMS D'ANIMATION PRÉFIXÉS `lexique*` (piège n° 14) : un nom générique
   comme `fadeIn` gagnerait pour TOUT le document, la dernière définition
   l'emportant partout.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── L'accent des deux blocs neufs ──────────────────────────────────────
   ⚠️ NE PAS ECRIRE `var(--color-primary, #e8a87c)` ICI : LE REPLI NE SE
   DECLENCHE JAMAIS. C'est le piege n° 2 transpose au CSS — `--color-primary`
   EST definie, une seule fois, dans le <style> critique d'index.html:139, et
   elle vaut **#1a5a8a, un bleu marine**, pas la peche qu'on croit lire dans le
   repli. Ecrit ainsi, tout l'accent de ces deux blocs sortait en bleu marine
   sur fond sombre. Mesure au navigateur, avant correction, en theme sombre —
   le theme par defaut :
     surtitre « LEXIQUE »        2,23:1   (seuil 4,5)
     « Rang N/7 » en gras        2,23:1
     bouton « Toutes les combinaisons »  2,23:1
     icone d'une reaction        2,40:1   (seuil 3 pour un element graphique)
   Les cinq contours de focus clavier tombaient au meme niveau : la navigation
   au clavier y devenait invisible.
   ⚠️ ET VOILA POURQUOI CA A TENU JUSQU'ICI : toutes les mesures de contraste
   de la vague avaient ete faites en THEME CLAIR, ou ces memes couleurs sont
   ecrites en dur. Un theme mesure n'est pas deux themes mesures. */
:root            { --lexique-accent: #e8a87c; }
body.light-mode  { --lexique-accent: #a8560c; }

/* ─── Le mot consultable ──────────────────────────────────────────────────
   Posé par `marquerTerme()` sur des éléments existants (#phaseBadgeCompact,
   .game__pot-label) et par `termeCombinaison()` dans l'historique. */

.lexique-terme {
  cursor: pointer;
  /* Le badge de phase est un conteneur flex : le marqueur en devient le dernier
     élément, sans déranger l'alignement des autres. */
  -webkit-tap-highlight-color: rgba(232, 168, 124, 0.25);
  touch-action: manipulation;
}

.lexique-terme::after {
  content: "?";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  margin-inline-start: 0.3em;
  border: 1px solid currentColor;
  border-radius: 50%;
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  opacity: 0.55;
  vertical-align: super;
  /* Le point d'interrogation ne doit jamais être lu deux fois : le mot porte
     déjà un aria-label complet, et le marqueur est purement décoratif. */
  speak: none;
}

.lexique-terme:hover::after,
.lexique-terme:focus-visible::after { opacity: 1; }

.lexique-terme:focus-visible {
  outline: 2px solid var(--lexique-accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Variante en ligne, dans les lignes de l'historique : le mot est noyé dans une
   phrase, il lui faut un soulignement pour se distinguer du texte courant. */
.lexique-terme--enligne {
  text-decoration: underline dotted currentColor 1px;
  text-underline-offset: 3px;
  font-weight: 600;
}

/* ─── La fenêtre de définition ────────────────────────────────────────────
   z-index 1200 : au-dessus des modales (200) — le lexique s'ouvre depuis
   l'historique — et de la barre vocale (1100), bien en dessous du tutoriel
   plein écran (10000), qui doit rester seul à l'écran. */

.lexique {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

.lexique[hidden] { display: none; }

.lexique__voile {
  position: absolute;
  inset: 0;
  background: rgba(6, 12, 18, 0.62);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

.lexique__carte {
  position: relative;
  width: min(23rem, 100%);
  max-height: 80svh;
  overflow-y: auto;
  padding: 1rem 1.1rem 1.15rem;
  border-radius: var(--radius-lg, 14px);
  background: #16212e;
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
  color: #f1ece5;
  text-align: start;
  animation: lexiqueEntree 0.16s ease-out;
}

.lexique__entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.55rem;
}

.lexique__surtitre {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--lexique-accent);
}

.lexique__surtitre svg { display: block; }

.lexique__fermer {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44 px : la plus petite cible tactile admise par le projet. */
  width: 44px;
  height: 44px;
  margin: -0.6rem -0.5rem -0.6rem 0;
  padding: 0;
  border: none;
  background: none;
  color: inherit;
  opacity: 0.6;
  cursor: pointer;
}

.lexique__fermer:hover { opacity: 1; }
.lexique__fermer:focus-visible {
  outline: 2px solid var(--lexique-accent);
  outline-offset: -4px;
  border-radius: 8px;
}

.lexique__terme-titre {
  margin: 0 0 0.4rem;
  font-size: 1.22rem;
  font-weight: 700;
  line-height: 1.25;
}

.lexique__definition {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.55;
  color: #cbd5e1;
}

.lexique__chiffres {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0.75rem 0 0;
}

.lexique__chiffre {
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.1);
  font-size: 0.78rem;
  color: #e2e8f0;
}

.lexique__chiffre strong { color: var(--lexique-accent); }

.lexique__lien {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.85rem;
  padding: 0.5rem 0.75rem;
  border-radius: 999px;
  border: 1px solid rgba(232, 168, 124, 0.4);
  background: rgba(232, 168, 124, 0.12);
  color: var(--lexique-accent);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
}

.lexique__lien:hover { background: rgba(232, 168, 124, 0.22); }
.lexique__lien:focus-visible {
  outline: 2px solid var(--lexique-accent);
  outline-offset: 2px;
}
.lexique__lien svg { display: block; }

@keyframes lexiqueEntree {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

/* ─── Thème clair ─────────────────────────────────────────────────────────
   Écrit d'emblée, et pas dans une vague suivante : le badge de phase attend sa
   surcharge de thème clair depuis le 6 août, et les boutons d'en-tête ont été
   livrés à 1,11:1 (piège n° 31). Contrastes visés ici : titre 12,6:1,
   définition 7,3:1, lien 4,8:1 sur fond #fdfaf6. */

body.light-mode .lexique__voile { background: rgba(30, 27, 24, 0.42); }

body.light-mode .lexique__carte {
  background: #fdfaf6;
  border-color: rgba(61, 48, 41, 0.14);
  box-shadow: 0 18px 48px rgba(61, 48, 41, 0.2);
  color: #241c17;
}

body.light-mode .lexique__surtitre,
body.light-mode .lexique__chiffre strong { color: #a8560c; }

body.light-mode .lexique__definition { color: #4a3c33; }

body.light-mode .lexique__chiffre {
  background: rgba(61, 48, 41, 0.06);
  border-color: rgba(61, 48, 41, 0.12);
  color: #3d3029;
}

body.light-mode .lexique__lien {
  border-color: rgba(168, 86, 12, 0.35);
  background: rgba(168, 86, 12, 0.09);
  color: #8f480a;
}

body.light-mode .lexique__lien:hover { background: rgba(168, 86, 12, 0.16); }

/* ─── Mouvement réduit ────────────────────────────────────────────────────
   Le réglage « Animations réduites » et la préférence système passent tous
   deux par ce média (lib/animations-reglage.js). */
@media (prefers-reduced-motion: reduce) {
  .lexique__carte { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   BADGE DE PHASE — THÈME CLAIR
   Signalé le 6 août (AUDIT_UX_V3 § 5 bis), corrigé le 10 août.

   ⚠️ CE N'ÉTAIT PAS « UN CONTRASTE UN PEU JUSTE » : C'ÉTAIT UN COMPOSANT DE
   THÈME SOMBRE POSÉ SUR UNE PAGE BLANCHE. Mesuré au navigateur, en clair :
   le fond propre du badge est TRANSPARENT (l'audit le croyait à
   `rgba(13,24,33,0.5)` — faux), donc tout se lit sur le blanc de la page.

   | Élément                    | Couleur              | Contraste | Seuil |
   |----------------------------|----------------------|-----------|-------|
   | libellé CHARGE / DÉCHARGE  | #E8D5C4              | 1,42:1    | 4,5   |
   | badge (couleur héritée)    | #F5F5DC              | 1,11:1    | 4,5   |
   | séparateur / total         | rgba(255,255,255,.55)| ~1,1:1    | 4,5   |
   | sous-texte explicatif      | rgba(245,245,220,.7) | ~1,3:1    | 4,5   |

   Corrigé ici parce que le lexique (bloc précédent) rend le badge consultable :
   son marqueur hérite de `currentColor` et se mesurait donc à 1,11:1 lui aussi.
   Une affordance invisible est une affordance absente — piège n° 28.
   ═══════════════════════════════════════════════════════════════════════════ */

body.light-mode .phase-badge-compact {
  background: rgba(168, 86, 12, 0.07);
  border-color: rgba(168, 86, 12, 0.42);
  color: #2c231d;
}

body.light-mode .phase-badge-compact__text,
body.light-mode .phase-badge-compact__step { color: #2c231d; }

body.light-mode .phase-badge-compact__icon,
body.light-mode .phase-badge-compact__indicator { color: #a8560c; }

body.light-mode .phase-badge-compact__indicator { background: #a8560c; }

/* La pastille de progression portait un fond noir à 30 % avec du texte clair :
   sur page blanche cela donnait du gris pâle sur gris pâle. On inverse. */
body.light-mode .phase-badge-compact__progress { background: rgba(61, 48, 41, 0.1); }

body.light-mode .phase-badge-compact__separator,
body.light-mode .phase-badge-compact__total { color: rgba(44, 35, 29, 0.68); }

body.light-mode .phase-badge-hint__text,
body.light-mode .phase-badge-hint__icon { color: #5a4a3f; }

/* ═══════════════════════════════════════════════════════════════════════════
   RÉACTIONS — le chambrage en un tap (lib/emotes.js)
   Ajouté le 10 août 2026. Ligne « vannes / emotes » de PLAN_ACTION.md.

   ⚠️ RIEN ICI N'ENTRE DANS LE FLUX DE L'ÉCRAN DE JEU. Le bouton vit dans
   l'en-tête, qui a sa hauteur propre ; le plateau et les bulles sont en
   `position: fixed`. À 375 × 812 il reste 24 px sous le bloc d'actions : c'est
   la contrainte qui a déjà coûté le bandeau de score sous les dés.

   ⚠️ LES BULLES SONT EN BAS, AU-DESSUS DE LA ZONE DES SNACKBARS, ET C'EST
   RAISONNÉ. Le haut est pris par le bandeau vocal (`.voix-barre`, ancré sous
   l'en-tête depuis le 8 août) ; le bas de l'écran de jeu est la zone du pouce
   du bouton « Lancer » (B30) — mais `animations.js:174` y pose déjà ses
   snackbars, donc du transitoire y est admis. Les bulles se placent au-dessus
   d'elles, jamais dessus.

   ⚠️ NOMS D'ANIMATION PRÉFIXÉS `emote*` (piège n° 14).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── Le bouton d'en-tête ─────────────────────────────────────────────────
   Masqué par défaut en HTML (`style="display: none"`), montré par
   `emotes.rafraichir()` quand deux humains au moins sont à la table.

   ⚠️ `.header` EST UNE GRILLE À PLACEMENT EXPLICITE, PAS UNE RANGÉE FLEX.
   Un enfant sans `grid-column` est placé automatiquement — et le quatrième
   part À LA LIGNE SUIVANTE. Mesuré avant correction : l'en-tête passait de
   **60 à 124 px** dès que le bouton apparaissait, poussant `.game__info` de
   64 px. Les dés et le bloc d'actions ne bougeaient pas (`.game__info` se
   comprime depuis le correctif du piège n° 23), donc le défaut ne se voyait
   QUE sur le panneau des joueurs — celui-là même qui manque de place à six.
   Le bloc `styles.css:11343` avait déjà corrigé exactement ce défaut pour les
   trois enfants d'origine ; il fallait suivre sa convention, pas la contourner.

   La colonne 2 reste en `1fr` : elle absorbe la piste vide et son interstice
   quand le bouton est masqué, donc **le bouton menu ne bouge pas d'un pixel**
   selon que les réactions sont disponibles ou non. Vérifié à la mesure. */

.header { grid-template-columns: auto 1fr auto auto; }

.header > .header__btn--emotes {
  grid-column: 3;
  grid-row: 1;
  /* Les autres boutons de l'en-tête sont en `flex` centré ; sans cette ligne
     celui-ci retombait en `block` et son SVG n'était pas centré. */
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;

  /* ⚠️ TOUT L'HABILLAGE EST À REDIRE ICI, IL NE S'HÉRITE PAS.
     `.header__btn` (styles.css:291) ne pose que `position` et `transition` :
     le fond, la bordure, l'arrondi et l'ombre sont déclarés séparément par
     `.header__btn--back` (142) et `.header__btn--menu` (233). Sans ces cinq
     lignes, le troisième bouton sortait nu — sans fond ni contour — à côté de
     deux voisins habillés. Aucune erreur, juste un bouton qui n'a pas l'air
     d'un bouton : la famille des pièges n° 28 et n° 31.
     ⚠️ `--color-cream` N'EST DÉFINIE NULLE PART dans le dépôt (vérifié au
     navigateur : chaîne vide). Ses deux voisins écrivent `var(--color-cream)`
     sans repli, donc leur `color` est invalide et retombe sur l'héritage —
     ça marche par accident. On écrit la valeur, et un repli explicite. */
  background: var(--gradient-primary);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  color: var(--color-cream, #f5f5dc);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

/* Déplacé de la colonne 3 à la 4 — la règle d'origine est en styles.css:11354,
   celle-ci gagne parce qu'elle est plus bas à spécificité égale. */
.header > .header__btn--menu { grid-column: 4; }

/* ⚠️ THÈME CLAIR — le bloc « HEADER MODE CLAIR » (styles.css:8881) ne nomme que
   `--back` et `--menu`. Un troisième bouton d'en-tête n'en hérite PAS : il
   serait sorti crème sur blanc, à 1,11:1, exactement le défaut corrigé le
   9 août (piège n° 31). La couleur du SVG est écrite explicitement pour la
   même raison qu'en 8888 : `lib/icons.js` injecte l'icône après le rendu. */
body.light-mode .header__btn--emotes {
  color: #1a1a1a;
  background: rgba(0, 0, 0, 0.05);
  border-color: rgba(0, 0, 0, 0.12);
}

body.light-mode .header__btn--emotes svg { color: #1a1a1a; }

body.light-mode .header__btn--emotes:hover {
  background: rgba(0, 0, 0, 0.1);
  border-color: rgba(0, 0, 0, 0.2);
}

/* ─── Le plateau ──────────────────────────────────────────────────────────
   z-index 1150 : au-dessus du bandeau vocal (1100) puisqu'il s'ouvre par
   dessus, en dessous du lexique (1200) et du tutoriel (10000). */

.emotes-plateau {
  position: fixed;
  inset: 0;
  z-index: 1150;
}

.emotes-plateau[hidden] { display: none; }

.emotes-plateau__voile { position: absolute; inset: 0; }

.emotes-plateau__carte {
  position: absolute;
  top: calc(var(--voix-barre-ancrage, 3.75rem) + 0.25rem);
  right: 0.6rem;
  width: min(19rem, calc(100vw - 1.2rem));
  padding: 0.6rem;
  border-radius: var(--radius-lg, 14px);
  background: rgba(18, 26, 36, 0.97);
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 14px 36px rgba(0, 0, 0, 0.42);
  animation: emotePlateauEntree 0.14s ease-out;
}

.emotes-plateau__grille {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.4rem;
}

.emotes-choix {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  /* 56 px : bien au-delà des 44 px minimum du projet. On tape vite, en pleine
     partie, souvent d'une seule main. */
  min-height: 56px;
  padding: 0.4rem 0.25rem;
  border-radius: var(--radius-md, 10px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.06);
  color: #f4efe8;
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  cursor: pointer;
  touch-action: manipulation;
}

.emotes-choix:hover { background: rgba(255, 255, 255, 0.13); }
.emotes-choix:active { transform: scale(0.95); }
.emotes-choix:focus-visible {
  outline: 2px solid var(--lexique-accent);
  outline-offset: 2px;
}

.emotes-choix__icone { color: var(--lexique-accent); }
.emotes-choix__icone svg { display: block; }

/* Cadence dépassée : la réaction n'est pas envoyée, et il faut le DIRE.
   Un bouton qui ne répond pas se lit comme une panne. */
.emotes-choix--refuse {
  animation: emoteRefus 0.4s ease-out;
  border-color: rgba(232, 108, 98, 0.6);
}

.emotes-muet {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  width: 100%;
  min-height: 38px;
  margin-top: 0.45rem;
  padding: 0.35rem;
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 0 0 8px 8px;
  background: none;
  color: rgba(244, 239, 232, 0.72);
  font-size: 0.76rem;
  cursor: pointer;
}

.emotes-muet:hover { color: #f4efe8; }
.emotes-muet svg { display: block; }
.emotes-muet:focus-visible {
  outline: 2px solid var(--lexique-accent);
  outline-offset: -2px;
}

/* ─── Les bulles ──────────────────────────────────────────────────────────
   `pointer-events: none` sur la pile : elle survole le bouton « Lancer », elle
   ne doit JAMAIS lui voler un tap. C'est la seule chose qui rend acceptable de
   la poser là. */

.emotes-pile {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(4.25rem + env(safe-area-inset-bottom, 0px));
  z-index: 1150;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  width: max-content;
  max-width: calc(100vw - 1.5rem);
  pointer-events: none;
}

.emotes-bulle {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 100%;
  padding: 0.4rem 0.7rem;
  border-radius: 999px;
  background: rgba(18, 26, 36, 0.94);
  border: 1px solid var(--emote-couleur, rgba(255, 255, 255, 0.15));
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.34);
  color: #f4efe8;
  font-size: 0.84rem;
  white-space: nowrap;
  animation: emoteBulleEntree 0.2s ease-out;
}

.emotes-bulle--sortie {
  animation: emoteBulleSortie 0.26s ease-in forwards;
}

.emotes-bulle__icone { color: var(--emote-couleur, #e8a87c); display: inline-flex; }
.emotes-bulle__icone svg { display: block; }

.emotes-bulle__nom {
  font-weight: 700;
  color: var(--emote-couleur, #e8a87c);
  /* Le pseudo vient d'un autre joueur : il est borné ici, pas seulement en JS. */
  max-width: 8rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

.emotes-bulle__nom:not(:empty)::after { content: ":"; opacity: 0.6; }

.emotes-bulle__texte { overflow: hidden; text-overflow: ellipsis; }

@keyframes emotePlateauEntree {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

@keyframes emoteBulleEntree {
  from { opacity: 0; transform: translateY(10px) scale(0.94); }
  to   { opacity: 1; transform: none; }
}

@keyframes emoteBulleSortie {
  to { opacity: 0; transform: translateY(-6px); }
}

@keyframes emoteRefus {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-3px); }
  75%      { transform: translateX(3px); }
}

/* ─── Thème clair ─────────────────────────────────────────────────────── */

body.light-mode .emotes-plateau__carte {
  background: #fdfaf6;
  border-color: rgba(61, 48, 41, 0.14);
  box-shadow: 0 14px 36px rgba(61, 48, 41, 0.2);
}

body.light-mode .emotes-choix {
  background: rgba(61, 48, 41, 0.05);
  border-color: rgba(61, 48, 41, 0.12);
  color: #2c231d;
}

body.light-mode .emotes-choix:hover { background: rgba(61, 48, 41, 0.11); }
body.light-mode .emotes-choix__icone { color: #a8560c; }

body.light-mode .emotes-muet {
  border-top-color: rgba(61, 48, 41, 0.12);
  color: #5a4a3f;
}
body.light-mode .emotes-muet:hover { color: #2c231d; }

/* ⚠️ La palette des joueurs est faite pour un fond sombre : le rouge tombe à
   3,03:1 sur #fdfaf6, et le vert, l'orange et le turquoise passent sous 3:1.
   `--emote-couleur-claire` est la variante assombrie, posée par emotes.js en
   même temps que l'autre. Chaque joueur garde sa couleur, le nom reste lisible. */
body.light-mode .emotes-bulle {
  background: #fdfaf6;
  border-color: var(--emote-couleur-claire, rgba(61, 48, 41, 0.18));
  box-shadow: 0 6px 20px rgba(61, 48, 41, 0.22);
  color: #2c231d;
}

body.light-mode .emotes-bulle__nom,
body.light-mode .emotes-bulle__icone { color: var(--emote-couleur-claire, #8f480a); }

/* ─── Mouvement réduit ────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .emotes-plateau__carte,
  .emotes-bulle { animation: none; }
  .emotes-bulle--sortie { opacity: 0; }
  .emotes-choix--refuse { animation: none; }
}

/* ─── Écran étroit ────────────────────────────────────────────────────────
   Sous 360 px, trois colonnes de 56 px ne tiennent plus avec les interstices :
   on passe à deux, le plateau grandit en hauteur, il est en `fixed`. */
@media (max-width: 359px) {
  .emotes-plateau__grille { grid-template-columns: repeat(2, 1fr); }
}

/* ══════════════════════════════════════════════════════════════════════════
   LOGO 421 — UN CONTOUR QUI LE DÉTACHE DU FOND BLANC (thème clair, 10 août)
   ══════════════════════════════════════════════════════════════════════════

   ⚠️ IL N'Y AVAIT AUCUNE BORDURE À « ACCENTUER » : `-webkit-text-stroke`
   valait `0px`, mesuré au navigateur. Le logo n'était pas discret, il était
   quasi invisible.

   Cause : la règle `.logo-digit` de la ligne 10510 impose son dégradé crème
   avec `!important` — POUR LES DEUX THÈMES. `body.light-mode .logo-digit`
   existait déjà (ligne 8951) mais ne touchait que le `filter`. Le crème
   #F5F5DC se retrouvait donc sur le blanc de l'écran d'accueil :

   | Point du dégradé      | Avant | Après (contour) | Seuil (64 px → 3:1) |
   |-----------------------|-------|-----------------|---------------------|
   | #F5F5DC (0 % et 100 %)| 1,02  | 9,19            | 3                   |
   | #C4B5A0 (50 %)        | 1,86  | 9,19            | 3                   |

   Le contour #5C4326 mesure 8,50:1 sur le bas du fond (#f8f6f0) et 9,19:1
   sur le blanc pur : c'est LUI qui porte la lisibilité. Le remplissage passe
   du crème à un or franc (#E6B422 → #B8860B), ce qui garde l'identité dorée
   du logo au lieu de l'aplatir en brun.

   ⚠️ `!important` est REQUIS sur `background-image` — sans lui, la règle de
   la ligne 10510 gagne malgré une spécificité inférieure. Même piège que
   celui documenté là-bas.

   ⚠️ `paint-order` empêche le contour de rogner le remplissage. S'il n'est
   pas supporté (vieux WebView Android), la dégradation est VISIBLE et non
   silencieuse : le contour est toujours peint, le glyphe reste lisible, il
   maigrit d'un pixel. C'est la raison pour laquelle on ne passe pas par
   `color-mix()` ici (piège n° 35).

   Thème sombre : NON TOUCHÉ. Vérifié à la mesure, 13 défauts avant comme
   après, 0 régression introduite. */
body.light-mode .logo-digit {
  background-image: linear-gradient(135deg, #E6B422 0%, #B8860B 50%, #E6B422 100%) !important;
  -webkit-text-stroke: 2px #5C4326;
  paint-order: stroke fill;
}

/* ══════════════════════════════════════════════════════════════════════════
   THÈME CLAIR — LES 97 DÉFAUTS DE CONTRASTE MESURÉS LE 10 AOÛT
   ══════════════════════════════════════════════════════════════════════════

   Balayage de tous les écrans, modales et panneaux, thème clair actif :
   **97 défauts pour 27 causes racines**. Le thème clair n'avait jamais été
   passé en revue en entier — il couvre l'accueil et quelques modales, et
   s'arrête aux composants ajoutés depuis.

   ⚠️ TROIS MOTIFS REVIENNENT, ET LE TROISIÈME EST LE PLUS VICIEUX.

   1. **Un fond sombre codé en dur que le thème clair n'a pas converti**,
      pendant que le texte, lui, passait au noir. `.play-mode-card`
      (rgba(13,24,33,.5)), `.modal__content--quick-play` (rgba(13,24,33,.92))
      et `.welcome-screen__content` : texte #1a1a1a sur fond sombre, 1,01:1.
      `body.light-mode .modal__content` ne redéfinit QUE `color`, jamais
      `background` — le fond ne suivait que s'il venait de `var(--color-bg)`.

   2. **Un texte crème ou mocha jamais converti**, sur un fond devenu clair :
      `.game__turn` à 1,10:1 — l'indicateur « C'est au tour de … », soit
      l'information centrale de l'écran de jeu — `.language-card__native`,
      `.help-panel p`, les compteurs de jetons.

   3. **L'ÉTAT SÉLECTIONNÉ N'EST JAMAIS CONVERTI, ET LUI SEUL.** Le thème
      clair repeint le fond du bouton actif en pastel mais laisse son texte
      en blanc, hérité de l'époque où ce fond était plein. Mesuré sur
      l'accueil : « Facile » 4,39:1 et « Difficile » 5,37:1 se lisent, mais
      **« Moyen », le niveau SÉLECTIONNÉ, est à 1,14:1**. Le joueur ne voit
      pas lequel il a choisi. Même défaut sur `.ai-level-card--active`.
      ➡️ Un état est une variante à part entière : le corriger « en général »
      ne le corrige pas.

   Résultat mesuré après ce bloc : **97 → 0**. Les 7 défauts qui subsistent
   au balayage sont ceux des écrans réseau, présents À L'IDENTIQUE dans les
   deux thèmes (bouton mocha 3,01:1, « Quitter » 3,89:1) : ils n'appartiennent
   pas au thème clair et ne sont pas traités ici.

   ⚠️ Les écrans réseau gardent volontairement leur fond sombre dans les deux
   thèmes. La seule règle qui les concerne ci-dessous REND sa clarté à
   `.lobby-qr-hint`, que le thème clair assombrissait sur un fond resté
   sombre (1,06:1 → 16,18:1).

   Thème sombre remesuré : 13 défauts avant, 13 après, **0 régression**. */

/* — Fonds sombres codés en dur, jamais convertis — */
body.light-mode .modal__content--play-mode { background-image: linear-gradient(135deg, #ffffff 0%, #f8f6f0 100%); }
body.light-mode .modal__content--quick-play { background: #f8f6f0; }
body.light-mode .welcome-screen__content { background-image: linear-gradient(#ffffff 0%, #f8f6f0 100%); }
body.light-mode .play-mode-card { background: rgba(15, 23, 42, 0.05); }
body.light-mode .ai-level-card { background: rgba(15, 23, 42, 0.05); }
body.light-mode .player-solo__tokens { background: rgba(15, 23, 42, 0.06); }
body.light-mode .player-count-btn { background: rgba(15, 23, 42, 0.06); color: #1a1a1a; }

/* — L'état sélectionné, que le thème clair oubliait (motif n° 3) — */
body.light-mode .ai-level-btn--easy .ai-level-btn__text { color: #14532d; }
body.light-mode .ai-level-btn--medium .ai-level-btn__text { color: #7c2d12; }
body.light-mode .ai-level-btn--hard .ai-level-btn__text { color: #7f1d1d; }
body.light-mode .ai-level-card__name,
body.light-mode .ai-level-card--active .ai-level-card__name,
body.light-mode .ai-level-card--selected .ai-level-card__name { color: #1a1a1a; }
body.light-mode .ai-level-card__desc,
body.light-mode .ai-level-card--active .ai-level-card__desc,
body.light-mode .ai-level-card--selected .ai-level-card__desc { color: rgba(15, 23, 42, 0.72); }

/* — Textes crème / mocha restés clairs sur un fond devenu clair — */
body.light-mode .game__turn { color: #1a1a1a; }
body.light-mode .player-solo__tokens span { color: #1a1a1a; }

/* ⚠️ LE PANNEAU DU MULTIJOUEUR LOCAL EST UN AUTRE COMPOSANT, ET LE BALAYAGE NE
   L'AVAIT PAS VU. Demande du propriétaire : « il faut vérifier que c'est pareil
   en multijoueur local et multijoueur réseau ». Bien vu — mesure à 4 joueurs :
   **8 défauts à 1,11:1**, les quatre noms et les quatre compteurs de jetons, en
   crème `#F5F5DC` sur le panneau blanc. Invisibles, littéralement.

   `renderMultiPlayers` produit `.player-multi__*`, un balisage entièrement
   distinct de `.player-solo__*` corrigé plus haut. Toutes les mesures avaient
   été faites en partie SOLO : le composant n'était jamais rendu.
   ➡️ **Un mode de jeu non joué est un écran non mesuré.** Solo, local et réseau
   ne partagent pas le même panneau de joueurs. Thème sombre : 0 défaut avant
   comme après — là-bas le crème est à sa place. */
body.light-mode .player-multi__name { color: #1a1a1a; }
body.light-mode .player-multi__tokens,
body.light-mode .player-multi__tokens span { color: #1a1a1a; }
body.light-mode .language-card__native { color: #1a1a1a; }
body.light-mode .play-mode-card__title { color: #1a1a1a; }
body.light-mode .play-mode-card__desc { color: rgba(15, 23, 42, 0.72); }
body.light-mode .multiplayer-header__title { color: #1a1a1a; }
body.light-mode .setup__config label > span { color: #1a1a1a; }
body.light-mode .welcome-screen__section-title { color: #1a1a1a; }
body.light-mode .help-panel p,
body.light-mode .help-panel li,
body.light-mode .help-panel strong { color: rgba(15, 23, 42, 0.86); }
body.light-mode .help-tab { color: rgba(15, 23, 42, 0.70); }
body.light-mode .help-tab--active,
body.light-mode .help-panel h3,
body.light-mode .cookie-card__link { color: #1d4ed8; }
body.light-mode #resetStatsBtn,
body.light-mode #resetStatsBtn span { color: #b91c1c; }

/* — Titres en `background-clip: text` : c'est le DÉGRADÉ qui fait la couleur,
     pas `color`. Une sonde qui lit `color` y voit #1a1a1a et conclut à tort
     que tout va bien — les deux titres se lisaient à 1,78 et 1,95:1. — */
body.light-mode #quickPlayTitle,
body.light-mode #playModeTitle {
  background-image: linear-gradient(135deg, #6b5545 0%, #4a3930 100%) !important;
}
body.light-mode .welcome-screen__title {
  background-image: linear-gradient(135deg, #8a6a00 0%, #6b5200 50%, #8a6a00 100%) !important;
}

/* — Textes secondaires trop transparents pour leur fond (3,16 et 3,91:1) — */
body.light-mode .home-liens a { color: rgba(15, 23, 42, 0.72); }
body.light-mode .home-pitch { color: rgba(15, 23, 42, 0.72); }

/* — Le bleu du bouton principal ne portait pas du blanc : 3,68:1 sur son
     premier arrêt. Le dégradé descend d'un cran, la teinte est conservée. — */
body.light-mode .btn--primary,
body.light-mode .home-play-btn { background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%); }
body.light-mode .header-menu__badge { background: #6b5545; }

/* — Écrans réseau : le fond RESTE sombre dans les deux thèmes. Le thème clair
     assombrissait ce texte par-dessus, d'où 1,06:1. — */
body.light-mode .lobby-qr-hint { color: rgba(255, 255, 255, 0.75); }

/* — « L'IA réfléchit… », visible seulement PENDANT le tour de l'IA : un violet
     sur un violet translucide. Aucun balayage d'écran ne le voit — il a fallu
     jouer une partie pour l'atteindre.
     ⚠️ Il échoue dans LES DEUX THÈMES (clair 2,72:1 · sombre 3,89:1, seuil 4,5).
     Seul le versant clair est corrigé ici — le sombre demande de revalider ce
     composant dans son thème, et c'est une autre demande. Consigné au plan. — */
body.light-mode .ai-indicator__text { color: #6b21a8; }

/* — LE COACH SE LISAIT SUR UN FOND QUI N'EST PAS LE SIEN.
     Son conteneur `.game__actions` porte un fond sombre `rgba(13,24,33,.3)`
     en petite largeur ; sur la page claire il vire au gris moyen, et le coach,
     dont le propre fond est un vert à 9 % d'opacité, le laissait traverser.
     Mesuré à 375 px, coach affiché : titre 2,64:1 et détail 2,62:1 — alors que
     ses règles de thème clair étaient, elles, parfaitement correctes.

     ⚠️ PREMIÈRE TENTATIVE RETIRÉE : éclaircir `.game__actions` lui-même.
     Elle corrigeait la mesure et ABÎMAIT L'ÉCRAN — le propriétaire l'a vue tout
     de suite. En thème sombre ce conteneur est TRANSPARENT sur grand écran
     (`rgba(0,0,0,0)`, mesuré) : lui donner un fond en clair inventait une bande
     grise sous les dés qui n'existe dans aucun des deux thèmes. Corriger la
     mesure ne suffit pas si le remède se voit.
     ➡️ On rend le coach OPAQUE : il ne dépend plus de ce qu'il y a derrière,
     et aucun autre élément n'est touché. — */
body.light-mode .coach { background: #eaf3ee; }

/* — L'ÉCRAN DE JEU EMPILAIT DES BANDES DE COULEURS DIFFÉRENTES — signalement du
     propriétaire sur capture, en deux temps : d'abord « le carré gris et le
     rectangle autour des dés », puis, une fois passés au blanc, « il y a
     toujours le fond gris, et le fond blanc derrière le bouton Lancer ; il
     faudrait des fonds transparents ».

     ⚠️ LA PREMIÈRE RÉPONSE — LES PASSER AU BLANC — ÉTAIT LA MAUVAISE, ET LA
     SECONDE DEMANDE EXPLIQUE POURQUOI. Blanchir deux blocs sur trois ne
     supprimait pas l'empilement, il le déplaçait : l'écran alternait panneau
     blanc, interstice beige, bloc blanc, encart vert, bloc blanc. Le défaut
     n'était pas la COULEUR de chaque bloc, c'était qu'ils en aient une.

     ➡️ LE THÈME SOMBRE AVAIT DÉJÀ LA RÉPONSE : `#dice` et `.game__actions` y
     sont **transparents** (mesuré : `rgba(0,0,0,0)`, bordure `0px`). Seul
     `.game__info` porte un fond, dans les deux thèmes. Le thème clair s'en
     écartait en donnant à `#dice` un dégradé `#ffffff → #f5f5f5` et une bordure
     `rgba(0,0,0,.15)` — un habillage qui n'existe nulle part ailleurs.
     Ces règles rendent au thème clair la structure du thème sombre : un seul
     panneau, le reste posé à même le fond de l'écran.

     ⚠️ CE QUE ÇA COÛTE, ET POURQUOI C'EST ACCEPTÉ : les dés sont blancs et se
     retrouvent sur le beige du fond (`#ede8df` en pied d'écran), soit **1,23:1**.
     C'est faible, mais un dé n'est pas identifié par son fond : il l'est par son
     **symbole**, très contrasté, par son **ombre portée** et par sa **bordure**,
     montée de `.12` à `.22`. C'est exactement la situation du thème sombre, où
     personne n'a jamais eu de mal à voir les dés.
     ➡️ Retirer un fond, c'est retirer le contraste de tout ce qui se posait
     dessus : c'est pour ça que la bordure et l'ombre sont renforcées ici. — */
/* ⚠️ ET LE RECTANGLE SE VOYAIT ENCORE, FOND ET BORDURE POURTANT TRANSPARENTS.
   Signalé une quatrième fois, capture à l'appui : « il reste le rectangle où
   sont les dés à faire disparaître, bordure et fond ». Les deux ÉTAIENT déjà
   transparents — mesuré. Ce qui dessinait le cadre était une TROISIÈME
   propriété, que ni « fond » ni « bordure » ne nomme :

     body.light-mode .dice { box-shadow: 0 6px 20px rgba(0,0,0,.1),
                                         0 -3px 10px rgba(0,0,0,.05); }

   Deux ombres portées, une vers le bas et une vers le haut — ce qui dessine un
   halo sur les quatre côtés, donc un rectangle. En thème sombre, `#dice` n'a
   **aucune** ombre : c'était, comme le dégradé et la bordure avant elle, un
   habillage ajouté par le seul thème clair.
   ➡️ **« Rendre transparent » ne se limite pas à `background` et `border`.**
   Une `box-shadow`, un `outline`, un `filter: drop-shadow` ou un pseudo-élément
   dessinent tout autant, et ne se voient dans aucune des deux propriétés qu'on
   inspecte d'abord. Le contrôle qui l'a trouvée les liste toutes.
   ⚠️ `#dice` (ID) est nécessaire pour battre `body.light-mode .dice` (classe). */
body.light-mode #dice {
  background: transparent;
  background-image: none;
  border-color: transparent;
  box-shadow: none;
}
body.light-mode .game__actions {
  background: transparent;
}

/* ⚠️ ET LE GRIS RESTAIT, PARCE QUE LE CALQUE N'ÉTAIT PAS CELUI QU'ON CROYAIT.
   `#dice` et `.game__actions` rendus transparents, le fond gris était toujours
   là — signalé une troisième fois : « tout ce qui est gris doit être en
   transparent sur le thème clair ». La sonde a trouvé le vrai coupable en
   remontant la chaîne des ancêtres depuis les dés :

     #dice ................ transparent
     .game__dice-area ..... transparent
     .screen--game ........ linear-gradient(#f8f6f0 -> #ede8df)   <-- LUI
     body.light-mode ...... linear-gradient(#ffffff -> #f8f6f0)

   L'écran de jeu posait donc son PROPRE dégradé, plus foncé que celui de la
   page, par-dessus. D'où une zone beige qui commençait pile sous le panneau des
   joueurs, et que rien de ce qu'on éclaircissait au-dessus ne pouvait effacer.
   Les trois autres écrans ne font pas ça : `setupScreen` reprend exactement le
   dégradé de la page, `advancedSetupScreen` et `endScreen` sont transparents.
   `.screen--game` était le seul à s'en écarter.

   ⚠️ CE QUE ÇA COÛTE — ET LE PROPRIÉTAIRE A TRANCHÉ CONTRE LA MESURE, EN
   CONNAISSANCE DE CAUSE. Le fond sous les dés devient `#f8f6f0` au lieu de
   `#ede8df` : plus clair, donc le dé BLANC y ressort encore moins — **1,08:1**
   contre 1,22. Mesuré alpha par alpha sur ce fond exact :
     .12 (origine) 1,28  ·  .22 1,60  ·  .35 2,21  ·  .45 2,90  ·  .5 **3,36**
   `.5` est le premier qui franchit le seuil de **3:1** des éléments graphiques,
   et il a été essayé. Verdict : « faut laisser transparent et pas détourer la
   zone ». **La bordure reste donc à sa valeur d'origine.**
   ➡️ C'est un arbitrage assumé, pas un oubli : un contour à 3:1 sur trois dés
   côte à côte se voit comme un cadre, et l'écran y perdait plus qu'il n'y
   gagnait. Le dé reste identifiable par son **symbole à 17,95:1** et par son
   ombre portée, un peu appuyée ici — une ombre donne du relief sans dessiner de
   trait, ce qui est exactement ce qui était demandé.
   ⚠️ **Ne pas « corriger » cette valeur lors d'un futur audit de contraste sans
   revenir à cette décision.** Elle sera signalée comme non conforme.
   ➡️ Leçon générale, elle : chaque fois qu'on éclaircit un fond, ce qui était
   posé dessus perd du contraste. Remesurer les enfants, pas seulement le bloc
   qu'on a touché. */
body.light-mode .screen--game {
  background: transparent;
  background-image: none;
}

/* — LES DEUX BADGES DE PHASE : FOND TRANSPARENT, BORDURE CONSERVÉE.
     Demande du propriétaire : « pour la partie Charge/Décharge, je veux garder
     la bordure mais mettre le fond transparent ; pour la partie grise, fond
     transparent ». Deux pastilles, deux fonds hérités du thème sombre :
       `#phaseBadgeCompact` .... `rgba(168, 86, 12, .07)` — un voile orangé
       `#phaseBadgeHint` ....... `rgba(0, 0, 0, .2)`      — le « gris »
     Sur le panneau blanc, le second donnait une pastille franchement grise.

     ⚠️ LA BORDURE EST EXPLICITEMENT CONSERVÉE — c'est elle qui garde au badge
     de phase sa forme de pastille, et le propriétaire l'a demandée nommément.
     Elle mesure 1,86:1 sur blanc, sous le seuil de 3:1 des éléments graphiques :
     **c'est un choix, pas un oubli.** Le badge reste identifié par son texte, et
     le rendre plus foncé reviendrait à dessiner le cadre qu'on cherche à alléger.

     ✅ RIEN N'EST PERDU CÔTÉ LISIBILITÉ, AU CONTRAIRE — les textes se lisent
     désormais sur le blanc franc du panneau au lieu d'un voile :
       CHARGE / DÉCHARGE et le numéro de manche .... **15,38:1** (inchangé)
       « Le perdant prend des jetons du pot » ....... 15,38 -> **17,40:1**
     Le travail du 10 août sur ce badge (1,42 -> 15,38) est donc préservé.
     ⚠️ Ne pas toucher au thème sombre : `rgba(0,0,0,.2)` y est utile, le fond
     sur lequel il se pose étant sombre lui aussi. — */
body.light-mode #phaseBadgeCompact {
  background: transparent;
  background-image: none;
}
body.light-mode #phaseBadgeHint {
  background: transparent;
}
body.light-mode .die {
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.16);
}

/* ═══════════════════════════════════════════════════════════════════════════
   ORDINATEUR — LE PANNEAU DES JOUEURS NE DOIT PAS AVOIR D'ASCENSEUR
   ═══════════════════════════════════════════════════════════════════════════
   Demande du propriétaire, le 10 août : « je ne veux pas de barre de
   défilement ». Elle est apparue avec le correctif du même jour qui étend la
   compression de `#gameScreen` au bureau (styles.css, bloc « SEUIL DE LARGEUR
   RETIRÉ ») : ce correctif a bien remis `#rollBtn` à l'écran, mais il l'a fait
   en déplaçant le manque de place DANS `.game__info`, qui est la cible de
   compression désignée. Le débordement de page est devenu un ascenseur de
   panneau — moins grave, et toujours pas ce qu'on veut voir.

   ➡️ LA VRAIE RÉPONSE N'EST PAS DE CHOISIR OÙ ÇA DÉBORDE, C'EST QUE ÇA TIENNE.
   Comptabilité mesurée à 900x800, partie solo, coach affiché :
     `.game__info` 354 + 16 · dés 131 + 8 + 16 · `.game__actions` 236  =  761 px
     disponible dans `#gameScreen` .............................. =  688 px
     manque ..................................................... =   73 px

   ① LE COACH REND SES 18 PX MORTS, ET SEULEMENT CEUX-LÀ.
      Sa réserve de 8,5 rem (136 px) est calée sur 375 px de large, où le plus
      long message des 8 langues réclame 148 px. Au-delà de 768 px, `#gameScreen`
      est plafonné à `max-width: 520px` (styles.css:11190) : la largeur du coach
      — donc sa hauteur naturelle — est **constante sur tout le bureau**. Mesuré
      identique à 900 et à 1920 : 75 px au plus court, **117 px au plus long**.
      7,4 rem (118 px) est donc la valeur juste ici, et elle conserve
      INTÉGRALEMENT la propriété qui justifie la hauteur fixe : à largeur donnée
      le bloc ne bouge plus d'un pixel, donc les dés ne remontent jamais.
      ⚠️ Ne pas propager cette valeur sous 769 px : elle y serait 30 px trop
      courte et le message défilerait à chaque tour.

   ② LE RYTHME VERTICAL SE RESSERRE, ET SEULEMENT SUR UNE FENÊTRE COURTE.
      Au-dessus de 900 px de haut il y a la place : rien ne change, pour ne pas
      tasser un écran qui respire. En dessous, les écarts entre blocs sont
      ramenés à la moitié — ce sont des blancs, pas du contenu : aucune
      information n'est retirée, aucune taille de texte n'est touchée.

   ⚠️ CE QUE CE BLOC NE PROMET PAS. À 5 ou 6 joueurs sur une fenêtre courte, le
   contenu ne PEUT pas tenir : le panneau défilera encore, et c'est la
   contrepartie déjà assumée du piège n° 23 — un panneau qui défile reste
   utilisable, un bouton hors de l'écran non. Le relevé complet est en
   RECETTE.md.                                                                */

@media (min-width: 769px) {
  .coach { height: 7.4rem; }
}

@media (min-width: 769px) and (max-height: 900px) {
  #gameScreen[style*="display: block"] > .game__info { margin-bottom: 0.5rem; }

  #gameScreen[style*="display: block"] > .game__dice-area {
    margin-top: 0.25rem;
    margin-bottom: 0.5rem;
  }

  #gameScreen[style*="display: block"] .game__phase-indicator { margin-bottom: 0.5rem; }
  #gameScreen[style*="display: block"] .game__pot { margin-bottom: 0.75rem; }

  /* Le rembourrage PROPRE du panneau : 16 px en haut et en bas, soit 32 px de
     blanc pour un panneau qui manquait justement de 19. Ramené à 8 + 8. */
  #gameScreen[style*="display: block"] > .game__info {
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
  }

  #gameScreen[style*="display: block"] .game__actions {
    gap: 0.6rem;
    /* ⚠️ `env(safe-area-inset-bottom)` conservé : il vaut 0 sur un ordinateur,
       mais ce bloc s'applique aussi à une tablette en paysage, où il ne vaut
       pas 0. Le retirer ferait passer le bouton sous l'encoche. */
    padding-bottom: max(0.4rem, env(safe-area-inset-bottom, 0px));
  }

  /* ⚠️ LES LIGNES NE SE RESSERRENT QUE QUAND LA TABLÉE EST NOMBREUSE, et le
     sélecteur est le mécanisme du comptage — il n'y a pas de classe posée par le
     JS et on n'en ajoute pas. `:nth-last-child(n+5)` ne correspond qu'aux
     éléments qui ont au moins 4 éléments APRÈS eux : il ne peut donc s'activer
     qu'à partir de 5 joueurs. Le `~ *` qui suit rattrape les 4 derniers, que le
     premier sélecteur laisse forcément de côté.
     ➡️ À 2, 3 ou 4 joueurs, RIEN ne change : l'espacement confortable reste.
     Mesuré à 936x892, 6 joueurs : ligne 62 → 54 px, panneau 412 → 364, et
     l'ascenseur passe de 34 px à 0. Le rembourrage de 8 px garde la ligne
     au-dessus de la cible tactile, l'avatar faisant déjà 40 px. */
  #gameScreen[style*="display: block"] .game__players > *:nth-last-child(n+5),
  #gameScreen[style*="display: block"] .game__players > *:nth-last-child(n+5) ~ * {
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
  }
}

/* ③ FENÊTRE VRAIMENT BASSE — les blancs ne suffisent plus, on touche aux tailles.
   Mesuré à 1024x768 après ① et ② : il manquait encore **30 px**. Le seuil de
   780 px n'est pas inventé ici, c'est déjà celui qu'emploient les deux blocs
   « fenêtres basses » de l'accueil (styles.css:11682 et :11722).
   Deux réductions, choisies pour ne retirer AUCUNE information :
     · les dés passent de 124 à 104 px — ils restent au-dessus des 85 px du
       rendu téléphone, que le propriétaire juge bon ;
     · le coach ramène sa réserve à 106 px. C'est 11 px sous son plus long
       message, et c'est exactement le cas que `overflow-y: auto` attend : dans
       cette fenêtre-là, une phrase rare défile de 11 px CHEZ ELLE au lieu de
       faire défiler tout le panneau des joueurs. La hauteur reste FIXE, donc
       les dés ne bougent toujours pas d'un tour à l'autre. */
@media (min-width: 769px) and (max-height: 780px) {
  .dice { grid-template-columns: repeat(3, minmax(0, 104px)); }
  .coach { height: 6.6rem; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   ARABE — LE MIROIR DE L'INTERFACE                              [dir="rtl"]
   Ajouté le 10 août 2026. Ligne « RTL arabe » de PLAN_ACTION.md.
   `dir` est posé sur <html> par lib/i18n.js quand la langue vaut `ar`.

   ⚠️ CE BLOC EST STRICTEMENT ADDITIF, ET C'EST DÉLIBÉRÉ. Chaque règle est
   préfixée `[dir="rtl"]`. Si l'une d'elles est fausse, seul l'arabe régresse :
   les sept locales LTR — 100 % des joueurs d'aujourd'hui — ne peuvent pas être
   touchées. Annulation complète : supprimer ce bloc.

   ⚠️ ET C'EST POURQUOI IL N'EMPLOIE AUCUNE PROPRIÉTÉ LOGIQUE.
   `inset-inline-end`, `margin-inline-start`, `border-inline-start` diraient la
   même chose en deux fois moins de lignes, et le disent mieux — mais elles
   datent de Chrome 87 et le WebView Android de Capacitor n'est pas garanti à
   jour. Une propriété inconnue est **ignorée en silence** : l'élément perdrait
   son ancrage dans TOUTES les langues, sans la moindre erreur. C'est le
   raisonnement qui a déjà écarté `color-mix()` (piège n° 35). Physique et
   verbeux, mais lisible partout.

   ⚠️ CE QUI N'EST PAS ICI L'EST PARCE QUE C'EST DÉJÀ JUSTE, ET C'EST LA
   MAJORITÉ. Mesuré au navigateur en arabe, accueil et écran de jeu : le flux,
   les rangées flex, les grilles et l'ordre BIDI (« 4-2-1 », « USG7US »,
   « الجولة 3 / 7 ») se miroitent seuls. N'ont donc rien à faire ici :
     · les balayages décoratifs `left: -100%` -> `left: 100%` ;
     · les éléments centrés `left: 50%` + `translateX(-50%)` — leur miroir,
       c'est eux-mêmes ;
     · les zones sûres `env(safe-area-inset-left/right)`, physiques par nature
       (l'encoche ne change pas de côté avec la langue) ;
     · `.voix-barre`, dont la DERNIÈRE définition est un bandeau pleine largeur
       `left: 0; right: 0` — les deux définitions antérieures, dont un
       `right: 0.6rem`, sont écrasées ;
     · `.header-menu__badge { margin-left: auto }` : mesuré INERTE, le libellé
       voisin est en `flex: 1` et absorbe tout l'espace libre.

   ⚠️ LA SONDE QUI A SERVI, ET QU'IL FAUT REPRENDRE POUR LA RECETTE : relever
   le centre horizontal de chaque élément visible en LTR puis en RTL. Un
   élément correctement miroité vérifie cx_rtl ≈ largeur − cx_ltr ; ceux qui NE
   BOUGENT PAS sont les coupables. Exclure les éléments déjà centrés, sinon ils
   polluent le relevé. ⚠️ Elle a DEUX faux positifs connus, tous deux mesurés :
   un groupe centré dont le libellé arabe est plus court que le français ne
   peut pas satisfaire l'égalité ; et une règle écrasée plus bas dans la
   feuille est relevée comme si elle s'appliquait.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── En-tête et son menu ─────────────────────────────────────────────────
   Le bouton menu passe à gauche tout seul (la grille se miroite), le panneau
   déroulant NON : il est en position absolue. Mesuré avant correction, en
   1280 px : bouton à x=24-72, panneau à x=1069-1259 — le menu s'ouvrait à
   l'opposé de ce qui l'ouvre. */
[dir="rtl"] .header-menu__content {
  right: auto;
  left: 1rem;
}

[dir="rtl"] .header-menu__item {
  text-align: right;
}

@media (hover: hover) and (pointer: fine) {
  /* L'item s'indente vers l'intérieur au survol : côté lecture, donc à droite. */
  [dir="rtl"] .header-menu__item:hover {
    padding-left: var(--spacing-md);
    padding-right: 1.25rem;
  }

  /* Les deux flèches d'en-tête s'écartent dans le sens où elles pointent. */
  [dir="rtl"] .header__btn--back:hover { transform: translateX(4px); }
  [dir="rtl"] .header__btn--menu:hover { transform: translateX(-4px); }
}

[dir="rtl"] .header__btn--back:active { transform: translateX(2px) scale(0.95); }
[dir="rtl"] .header__btn--menu:active { transform: translateX(-2px) scale(0.95); }

@media (prefers-color-scheme: light) {
  [dir="rtl"] body:not(.light-mode):not(.dark-mode-forced) .header-menu__item#themeToggleBtn::after {
    right: auto;
    left: 0.5rem;
  }
}

/* ─── Choix de la langue ──────────────────────────────────────────────────
   ⚠️ `.language-card[data-lang="ar"] .language-card__info` garde son
   `text-align: right` : il sert en LTR, où la carte arabe doit aligner son
   propre nom du bon côté au milieu de sept cartes latines. */
[dir="rtl"] .language-card { text-align: right; }

[dir="rtl"] .language-card--active::after {
  left: auto;
  right: 0;
}

[dir="rtl"] .language-grid--welcome .language-card__check {
  right: auto;
  left: 3px;
}

@media (hover: hover) and (pointer: fine) {
  [dir="rtl"] .language-card:hover { transform: translateX(-4px); }
}

/* ─── Aide : les sept combinaisons ────────────────────────────────────────
   Le filet de couleur passe du bord gauche au bord droit — c'est le bord où
   commence la lecture. Les deux variantes doivent suivre : corriger `.x` ne
   corrige pas `.x--best` (piège n° 37). */
[dir="rtl"] .help-panel ul,
[dir="rtl"] .help-panel ol {
  margin-left: 0;
  margin-right: 1.5rem;
}

[dir="rtl"] .combo-item {
  border-left: 0;
  border-right: 3px solid #64748b;
}

[dir="rtl"] .combo-item--best  { border-right-color: #eab308; }
[dir="rtl"] .combo-item--worst { border-right-color: #ef4444; }

[dir="rtl"] .combo-name {
  margin-left: 0;
  margin-right: 0.75rem;
}

/* ─── Historique de la partie ─────────────────────────────────────────────
   ⚠️ La couleur du filet vient d'un style EN LIGNE écrit par
   lib/ui-manager.js. Elle voyage donc par la variable `--histo-accent` : sans
   elle, il n'y aurait aucun moyen de repeindre le bord droit depuis la
   feuille de styles, et le filet serait resté à gauche en arabe. */
[dir="rtl"] .history__content {
  padding-right: 0;
  padding-left: 0.5rem;
}

[dir="rtl"] .history-item {
  border-left: 0;
  border-right: 3px solid var(--histo-accent, transparent);
}

/* ─── Écran de jeu ────────────────────────────────────────────────────────
   Le coach est le seul bloc à alignement figé du jeu ; les pastilles
   « déconnecté » sont en position absolue sur leur avatar. */
[dir="rtl"] .coach { text-align: right; }

[dir="rtl"] .coach__titre {
  margin-right: 0;
  margin-left: auto;
}

[dir="rtl"] .coach__fermer {
  margin-right: 0;
  margin-left: -0.4rem;
}

[dir="rtl"] .ai-level-dots {
  margin-left: 0;
  margin-right: 6px;
}

[dir="rtl"] .player-solo__disconnected-icon,
[dir="rtl"] .player-multi__disconnected-icon {
  right: auto;
  left: -4px;
}

/* ⚠️ Ces deux marges sont ÉCRITES EN LIGNE par lib/ui-manager.js (l'icône du
   bouton « Valider » et celle du bouton « Lancer »). Un style en ligne bat
   toute feuille — sauf `!important`, qui est ici le seul moyen de les
   reprendre sans toucher au rendu des sept locales LTR. */
[dir="rtl"] #endTurnBtn > svg,
[dir="rtl"] #rollBtn > svg {
  margin-right: 0 !important;
  margin-left: 0.5rem;
}

/* ─── Multijoueur en ligne ────────────────────────────────────────────────
   La flèche du menu déroulant et la place qu'elle réclame changent de bord
   ensemble : les désolidariser ferait chevaucher le texte et la flèche. */
[dir="rtl"] .network-select {
  background-position: left 1rem center;
  padding-right: 1rem;
  padding-left: 2.5rem;
}

[dir="rtl"] .lobby-player__disconnected-icon {
  right: auto;
  left: -6px;
}

[dir="rtl"] .lobby-player--playing .lobby-player__status::after,
[dir="rtl"] .player-typing-indicator,
[dir="rtl"] .typing-dots,
[dir="rtl"] .player-typing-dots {
  margin-left: 0;
  margin-right: 0.5rem;
}

/* ─── Canal audio et réactions ────────────────────────────────────────────
   La marque d'état est collée au coin de la pastille ; le plateau de
   réactions s'ancre sous le bouton d'en-tête, qui est passé à gauche. */
[dir="rtl"] .voix-pastille__marque {
  right: auto;
  left: -3px;
}

[dir="rtl"] .emotes-plateau__carte {
  right: auto;
  left: 0.6rem;
}

/* ─── Lexique ─────────────────────────────────────────────────────────────
   Marge négative de compensation : elle doit rester du côté du bord, sinon
   la cible tactile de 44 px mord sur le titre au lieu de la bordure. */
[dir="rtl"] .lexique__fermer {
  margin-right: 0;
  margin-left: -0.5rem;
}

/* ─── Bannière de consentement ────────────────────────────────────────────
   Un interrupteur coulisse vers le côté « activé », qui suit le sens de
   lecture. La pastille part de la droite et va vers la gauche. */
[dir="rtl"] .cookie-toggle__slider::before {
  left: auto;
  right: 3px;
}

[dir="rtl"] .cookie-toggle input:checked + .cookie-toggle__slider::before {
  transform: translateX(-20px);
}

@media (hover: hover) and (pointer: fine) {
  [dir="rtl"] .cookie-feature:hover { transform: translateX(-4px); }
}

/* ─── Tutoriel plein écran ────────────────────────────────────────────────
   ⚠️ Ces règles visent un `<style>` INJECTÉ par lib/tutorial.js, donc écrit
   dans le document APRÈS cette feuille. C'est la spécificité qui tranche, pas
   l'ordre : `[dir="rtl"] .goal-item--win` (0,2,0) bat `.goal-item--win`
   (0,1,0). Ne pas les déplacer vers le module : elles y perdraient ce gain. */
[dir="rtl"] .goal-item--win,
[dir="rtl"] .goal-item--lose {
  border-left: 0;
  border-right: 3px solid var(--tut-success);
}

[dir="rtl"] .goal-item--lose { border-right-color: var(--tut-error); }

[dir="rtl"] .goal-item:hover {
  transform: translateX(-8px);
  box-shadow: 8px 0 20px rgba(0, 0, 0, 0.2);
}

/* ═══════════════════════════════════════════════════════════════════════════
   VAGUE 23 — LE THÈME SOMBRE, REMESURÉ (10 août 2026)
   ═══════════════════════════════════════════════════════════════════════════

   ⚠️ `--color-cream` ÉTAIT UTILISÉE 12 FOIS ET DÉFINIE ZÉRO FOIS.
   Huit de ces douze usages n'ont AUCUN repli : leur déclaration `color` est donc
   « invalide au moment du calcul » et la propriété retombe sur l'héritage. Rien
   n'échoue, rien n'avertit — et dans la modale de partie rapide, l'héritage vaut
   NOIR : « Facile » et « Difficile » s'y affichaient à 1,17:1 sur le fond sombre.
   C'est le pendant exact du piège n° 35 : là, un repli n'était jamais évalué
   parce que la variable EXISTAIT ; ici, c'est la variable qui manque et la
   moitié des usages n'a pas de repli du tout.
   Le dépôt le savait à moitié : styles.css:14586 le documente en commentaire et
   ajoute un repli — à UN seul endroit, laissant les huit autres en l'état.

   ⚠️ ET ON LA DÉFINIT PAR THÈME, PAS UNE FOIS POUR TOUTES. Poser simplement le
   beige aurait réparé le thème sombre en CRÉANT des défauts en thème clair :
   `.play-mode-card__title` et `.quick-play-label` n'ont pas de surcharge claire
   et seraient passés en beige sur blanc. Deux valeurs, une par thème — c'est la
   leçon déjà écrite pour `lib/emotes.js`. */
:root { --color-cream: #f5f5dc; }
body.light-mode { --color-cream: #1a1a1a; }

/* ⚠️ LA CORRECTION DE LA VAGUE 18 REPOSAIT SUR UN FOND QUI N'EXISTE PAS.
   Son commentaire (styles.css:10704) explique qu'elle inverse ces trois textes
   en sombre « parce qu'ils reposent sur un bleu vif rgb(59,130,246) ». Mesuré au
   navigateur le 10 août : `.language-modal__footer-info` porte
   `rgba(59, 130, 246, 0.1)` — DIX POUR CENT de bleu — composité sur le
   `rgb(13,24,33)` de `.modal__content`, soit **rgb(18,35,54)**, un bleu nuit.
   Le texte sombre #0d1821 y tombait à **1,12:1**, c'est-à-dire invisible.
   ➡️ Une sonde qui lit `background-color` sans composer l'ALPHA lit un fond qui
   n'est pas à l'écran. Même famille que le piège n° 41 (les dégradés), et cette
   fois l'erreur a été COMMISE, pas seulement évitée.
   On repasse en `var(--color-cream)` : beige en sombre (13,0:1), encre en clair,
   sans avoir à écrire deux règles. */
.language-modal__footer-info,
.language-modal__info-text,
.language-modal__info-icon {
  color: var(--color-cream);
}

/* ⚠️ LE THÈME CLAIR AVAIT ÉTÉ CORRIGÉ, LE SOMBRE NON — et c'est le thème par
   DÉFAUT. `body.light-mode .home-play-btn` (styles.css:15003) porte déjà le
   dégradé foncé `#2563eb → #1d4ed8` ; la règle de base, elle, est restée sur
   `#3b82f6 → #2563eb`. Le texte blanc du bouton principal de l'accueil tombait
   donc à **3,68:1** sur son premier arrêt, pour un seuil de 4,5.
   ➡️ C'est le piège n° 37 pris à l'envers : une correction de thème qui ne
   traite qu'un thème laisse l'autre en l'état, et c'est le plus visité qui reste
   derrière. Mesuré après : 4,98:1 sur le premier arrêt, 6,29:1 sur le second.
   La teinte ne change pas de famille — un cran plus foncé, le même bleu. */
.home-play-btn {
  background: linear-gradient(to right, #2563eb, #1d4ed8);
}

/* ⚠️ DEUX VARIABLES DE PLUS, UTILISÉES ET JAMAIS DÉFINIES — trouvées par le
   contrôle n° 8 de `verifier-inertes`, écrit le jour même pour remplacer
   l'action V03 de l'audit V2 (écartée : voir PLAN_ACTION.md).

   · `--color-gold-dark` : `.tutorial__btn--primary:hover` (styles.css:6953) lui
     demandait son FOND, et une règle de tutoriel sa `border-color`. Déclaration
     invalide au calcul, donc fond transparent : le bouton principal du tutoriel
     n'avait aucun état de survol. La valeur retenue n'est pas inventée — c'est
     l'or foncé déjà présent dans le dégradé du logo (styles.css:14881).
   · `--color-text-muted` : un paragraphe des petites modales héritait de la
     couleur du parent au lieu de la teinte sourdine voulue. On reprend le gris
     `#A8A39A` que le reste de la feuille utilise pour ce rôle, et son pendant
     clair — deux valeurs, une par thème, comme pour `--color-cream`. */
:root {
  --color-gold-dark: #B8860B;
  --color-text-muted: #A8A39A;
}
body.light-mode {
  --color-text-muted: #5a5a5a;
}

/* ⚠️ LE SEUL TEXTE INDEXABLE DE L'ACCUEIL ÉTAIT SOUS LE SEUIL AA.
   `.home-pitch` (styles.css:14097) écrit `rgba(245, 245, 220, 0.42)` en dur :
   mesuré **3,68:1** sur le dégradé sombre, pour 11,5 px et un seuil de 4,5.
   Le thème clair, lui, passait déjà.
   ⚠️ Il n'était pas ressorti au premier balayage du thème sombre : l'écran de
   JEU était affiché à ce moment-là, donc l'accueil n'était pas mesuré. Un
   balayage ne vaut que pour ce qui est à l'écran au moment où il tourne.
   0,42 → 0,58 : mesuré 4,93:1, et le texte reste discret. C'est aussi le
   paragraphe que lit le robot d'AdSense (CLAUDE.md § 9). */
.home-pitch { color: rgba(245, 245, 220, 0.58); }

/* ⚠️ LES LIENS DE CONTENU SONT MASQUÉS DANS L'APPLICATION NATIVE, ET PAS SUR LE SITE.
   Mesuré le 10 août 2026 sur un Redmi 25028RN03Y, premier essai du projet sur un
   appareil réel : taper « Règles du 421 » dans l'APK mène à un écran de chargement
   FIGÉ, et le bouton retour d'Android FERME L'APPLICATION au lieu de revenir au jeu.
   Cause : le serveur local de Capacitor ne résout pas les index de dossier, donc
   `/regles-du-421/` retombe sur l'index.html du JEU et ses chemins relatifs se
   résolvent dans le mauvais dossier. Détail complet en tête de `init()` (app.js).

   ⛔ NE PAS masquer `.home-liens` globalement : sur le SITE, c'est la seule
   passerelle vers les cinq pages de contenu, celles-là mêmes qui ont répondu au
   refus d'AdSense pour « contenu à faible valeur informative » (CLAUDE.md § 9).
   Les masquer partout rendrait l'accueil invisible pour un robot.

   `.home-pitch` est CONSERVÉ dans l'application : c'est un paragraphe de
   présentation utile à un nouveau joueur, pas un lien piégé. */
body.app-native .home-liens { display: none !important; }

/* ⚠️ ZONE SÛRE EN BAS — mesuré le 10 août sur un Redmi 25028RN03Y.
   La dernière ligne de `.home-pitch` passait SOUS la barre de navigation
   Android (les trois boutons système), qui se dessine par-dessus la page.
   `env(safe-area-inset-bottom)` vaut 0 partout où il n'y a pas de barre
   superposée : la règle est donc sans effet sur ordinateur et sur le web.
   ⚠️ Le repli `0px` est obligatoire — sans lui, un navigateur qui ne connaît
   pas `env()` rend la déclaration invalide ENTIÈRE (piège n° 46). */
.home-pitch {
  padding-bottom: calc(0.5rem + env(safe-area-inset-bottom, 0px));
}

/* ⚠️ UN FILET SÉPARATEUR SANS RIEN APRÈS LUI. Le menu d'en-tête dessinait un
   trait sous « Apprentissage » : c'est la bordure du conteneur de
   « Confidentialité des publicités », masqué tant qu'AdMob ne l'exige pas
   (`privacyOptionsRequirementStatus !== REQUIRED`). L'élément disparaissait,
   sa séparation restait. Vu à l'écran sur l'appareil.
   ⛔ NE PAS masquer le conteneur lui-même : son absence quand Google l'exige
   est un MOTIF DE REJET Play Store (CLAUDE.md § 8). On ne retire que le trait
   quand il ne sépare plus rien. */
.header-menu__item[hidden] + .header-menu__separator,
.header-menu__separator:last-child { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   « DÉCHARGE ! » — UN OVERLAY QUI N'EN ÉTAIT PAS UN
   ═══════════════════════════════════════════════════════════════════════════
   `showPhaseTransition()` (lib/ui-manager.js) crée un `div.phase-transition-
   overlay` et l'ajoute à `document.body` pendant 2 s au passage en décharge.
   ⚠️ CETTE CLASSE N'AVAIT AUCUN STYLE, NULLE PART — ni ici, ni dans le
   `<style>` critique d'index.html. Un `div` sans style est un bloc en flux
   normal : au lieu de RECOUVRIR la page, il la POUSSAIT.

   Mesuré à 360×740, en jouant jusqu'au changement de phase avec un mouchard
   sur la hauteur du panneau d'information : celui-ci tombait de 280 px de
   besoin à 150 px de place, soit **130 px cachés** — ascenseur brutal, dés
   remontés de 130 px, pendant deux secondes, à chaque partie.
   Introuvable à la relecture : le JS a l'air juste, la classe a l'air posée.
   C'est le piège n° 31 pris par l'autre bout — là un sélecteur CSS ne visait
   aucun élément, ici un élément ne trouve aucun sélecteur.

   ⚠️ `lib/animations.js:62` documente EXACTEMENT le même défaut, déjà corrigé
   sur `createConfetti` et `createPhaseConfetti` (« passaient de position:
   fixed… »). L'overlay qu'ils accompagnent avait été oublié : une correction
   s'applique à la CLASSE de défaut, pas au seul endroit où on l'a vu.

   z-index 9000 : au-dessus du jeu, sous le tutoriel plein écran (10000).
   `pointer-events: none` — il disparaît seul au bout de 2 s, il ne doit pas
   avaler le tap du joueur pendant ce temps.                                */
.phase-transition-overlay {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  padding: 2rem;
  text-align: center;
  pointer-events: none;
  background: rgba(13, 24, 33, 0.82);
  color: #fb923c;
  animation: phaseTransitionApparition 0.35s ease-out;
}

body.light-mode .phase-transition-overlay,
[data-theme="light"] .phase-transition-overlay {
  background: rgba(253, 250, 246, 0.9);
  color: #b45309;
}

.phase-transition-overlay__icon { color: #fb923c; }
body.light-mode .phase-transition-overlay__icon,
[data-theme="light"] .phase-transition-overlay__icon { color: #b45309; }

.phase-transition-overlay__text {
  font-size: clamp(1.8rem, 9vw, 2.6rem);
  font-weight: 800;
  letter-spacing: 0.06em;
}

/* Nom préfixé — piège n° 14. Un `fadeIn` de plus gagnerait pour tout le
   document, y compris pour les 63 animations ressuscitées par 7ccd675. */
@keyframes phaseTransitionApparition {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   « VOTRE PRÉNOM » — la modale de lib/pseudo-ui.js
   ═══════════════════════════════════════════════════════════════════════════
   ⚠️ ÉCRIT EN MÊME TEMPS QUE LE MODULE, PAS APRÈS. Le piège n° 50 vient d'être
   consigné le jour même : un élément dont la classe n'a aucune règle CSS est un
   bloc en flux normal, et l'« overlay » DÉCHARGE poussait la page au lieu de la
   recouvrir. On ne pose plus une classe sans sa règle.

   ⚠️ LES DEUX THÈMES, pas seulement le sombre qui est celui par défaut. C'est
   la leçon du piège n° 37 : un thème ajouté après coup est partiel, et c'est
   l'état le plus visible qui passe à travers les mailles.

   Le reste (`.modal`, `.modal__overlay`, `.modal__content`, `.modal__header`,
   `.modal__body`, `.btn`) est déjà porté par la feuille : on ne redéfinit que
   ce qui est propre à cette modale.                                          */
.pseudo-modale {
  max-width: 22rem;
  width: calc(100% - 2rem);
}

.pseudo-modale__champ {
  width: 100%;
  padding: 0.75rem 0.9rem;
  font-size: 1.05rem;
  border-radius: var(--radius-md, 10px);
  border: 1px solid rgba(245, 245, 220, 0.22);
  background: rgba(245, 245, 220, 0.07);
  color: #f5f5dc;
}

.pseudo-modale__champ::placeholder { color: rgba(245, 245, 220, 0.45); }

.pseudo-modale__champ:focus-visible {
  outline: 2px solid #5da283;
  outline-offset: 2px;
  border-color: transparent;
}

.pseudo-modale__actions {
  display: flex;
  gap: 0.6rem;
  margin-top: 1rem;
}

/* Les deux boutons se partagent la largeur : à 320 px, « Annuler » et
   « Valider » côte à côte tiennent, et la cible reste au-dessus de 44 px. */
.pseudo-modale__actions .btn {
  flex: 1 1 0;
  min-height: 44px;
}

body.light-mode .pseudo-modale__champ,
[data-theme="light"] .pseudo-modale__champ {
  border-color: rgba(26, 35, 50, 0.22);
  background: #ffffff;
  color: #1a2332;
}

body.light-mode .pseudo-modale__champ::placeholder,
[data-theme="light"] .pseudo-modale__champ::placeholder { color: #6b7280; }

/* Champ « Votre prénom » de la carte de bienvenue (index.html, #welcomeCard).
   Facultatif : la carte propose, elle n'enferme pas. Le style reste discret
   pour que « Je me lance » demeure la voie évidente — c'est tout l'objet de
   cette carte, et l'ajout d'un champ ne doit pas la transformer en formulaire. */
.bienvenue__label {
  display: block;
  margin: 0.25rem 0 0.35rem;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-text-dim, #a8a39a);
  text-align: left;
}

.bienvenue__prenom {
  width: 100%;
  margin-bottom: 1rem;
  padding: 0.7rem 0.85rem;
  font-size: 1rem;
  border-radius: var(--radius-md, 10px);
  border: 1px solid rgba(245, 245, 220, 0.22);
  background: rgba(245, 245, 220, 0.07);
  color: #f5f5dc;
}

.bienvenue__prenom::placeholder { color: rgba(245, 245, 220, 0.45); }

.bienvenue__prenom:focus-visible {
  outline: 2px solid #5da283;
  outline-offset: 2px;
  border-color: transparent;
}

body.light-mode .bienvenue__label,
[data-theme="light"] .bienvenue__label { color: #5a6472; }

body.light-mode .bienvenue__prenom,
[data-theme="light"] .bienvenue__prenom {
  border-color: rgba(26, 35, 50, 0.22);
  background: #ffffff;
  color: #1a2332;
}

body.light-mode .bienvenue__prenom::placeholder,
[data-theme="light"] .bienvenue__prenom::placeholder { color: #6b7280; }

/* ============================================================
   VAGUE 28 — LE SALON EN LIGNE (proposition P4, partie vérifiée)
   ============================================================

   Trois défauts, tous constatés sur l'appareil réel (capture du 11 août,
   `store/captures/pretes/06-salon-en-ligne.png`).

   ⚠️ CE QUE CE BLOC NE FAIT **PAS**, ET POURQUOI.
   La proposition demandait aussi d'ajouter des règles `light-mode` pour
   `.lobby-code`, `.lobby-code-label`, `.lobby-counter` et
   `.connection-indicator`, au motif que « le thème clair n'a jamais été
   traité » et que le code s'afficherait « crème sur crème, ≈ 1,9:1 ».
   **C'est faux, et le remède aurait créé le défaut qu'il prétendait retirer.**
   `#networkContainer` porte un dégradé sombre EN DUR (#0d1821 → #1a2332) sans
   aucune surcharge claire : les écrans réseau ne basculent pas de thème, ce que
   documente déjà le commentaire de la ligne ~15116. Mesuré dans le navigateur
   avec `body.light-mode` actif, sur ce fond réel :
     .lobby-qr-hint ......... 10,01:1
     .lobby-code ............  6,80:1
     .lobby-code-label ......  5,64:1
     .lobby-counter .........  5,64:1
     .connection-indicator ..  6,80:1
   Les cinq passent AA. Leur poser du texte sombre les ferait tomber sous 2:1.
   ➡️ Ne pas « corriger » le thème clair du salon : il n'y en a pas.
   ➡️ Et le nom du joueur n'est pas en cause non plus : `network-ui.js` sert
      déjà une variante `texte:` par couleur (rouge #ee8175, 4,62:1). */

/* — 1. « Connecté » passait sous la barre d'état —
   Il était en `position: absolute; top: 1rem; right: 1rem`, sans
   `--safe-area-top` : sur la capture, l'indicateur est coupé en deux par la
   barre système. Les trois états — connecté, reconnexion, déconnecté — sont
   donc illisibles au moment précis où ils comptent.
   Le remettre dans le flux le rend increvable : plus rien ne peut le recouvrir,
   et il encaisse « متصل » comme « Verbunden » sans qu'on mesure quoi que ce soit.
   ⚠️ `width: fit-content` plutôt que `align-self` : le parent n'est pas garanti
   flex, et `align-self` ne ferait alors rien — en silence. */
#connectionIndicator.connection-indicator {
  position: static;
  width: fit-content;
  max-width: 100%;
  margin: 0 auto 0.75rem;
}

/* — 1 bis. …ET LE REMETTRE DANS LE FLUX NE SUFFISAIT PAS : ça DÉPLAÇAIT le
   débordement au lieu de le supprimer (piège n° 39).
   Mesuré à 375x812, salon d'une partie à un joueur : l'indicateur ressortait
   encore à `top: -27`, statique et pourtant hors écran.
   La vraie cause est le conteneur, pas l'indicateur. `#networkContainer` est
   `position: fixed`, pleine hauteur, `display: flex` + `align-items: center`,
   `overflow: visible` — et `body` est en `overflow: hidden`. Dès que le salon
   dépasse la hauteur visible, il déborde **également en haut et en bas**, et
   le haut devient inatteignable : rien ne défile.
   ⚠️ Ne PAS résoudre ça par `align-items: flex-start` seul : le salon cesserait
   d'être centré sur les écrans où il tient, ce qui est le cas courant.
   ⚠️ Ni par `align-items: safe center` : le WebView Android de Capacitor n'est
   pas garanti assez récent, et la dégradation serait silencieuse — même motif
   que le refus de `color-mix()`.
   ⚠️⚠️ ET LE CONTENEUR EST EN `flex-direction: column` — DONC C'EST
   `justify-content` QUI CENTRE VERTICALEMENT, PAS `align-items`.
   Première tentative ici : `align-items: flex-start`. Mesuré, elle n'a rien
   changé au débordement (indicateur toujours à `top: -27`) et a désaligné le
   salon horizontalement — une régression pour zéro gain. L'axe se lit, il ne
   se devine pas :
       getComputedStyle(el).flexDirection
   ➡️ `justify-content: flex-start` : le contenu part du haut, donc il est
   toujours atteignable, et `overflow-y: auto` le rend défilable. Un salon plus
   haut que l'écran ne peut plus cacher ni son indicateur ni son bouton Quitter.
   Mesuré : contenu 855 px pour 812 visibles. */
#networkContainer.network-container {
  justify-content: flex-start;
  overflow-y: auto;
  padding-top: max(1rem, var(--safe-area-top));
  padding-bottom: max(1rem, var(--safe-area-bottom));
}

/* — 2. Le code de partie ressemblait à un champ vide —
   `border: 2px dashed` est la convention d'une zone à remplir ou d'une cible de
   dépôt. Or ce sont les six lettres à transmettre : la seule donnée de l'écran
   dont dépend l'arrivée d'un invité. Un trait plein en fait une valeur. */
.lobby-code {
  border: 1px solid rgba(196, 181, 160, 0.35);
  border-radius: 0.625rem;
}

/* — 3. L'accessoire était plus visible que l'action principale —
   « Inviter un ami » et « Lancer la partie » portaient tous deux
   `.network-btn--primary`, donc le même aplat mocha. L'écran mettait en avant
   le partage, et l'action qui démarre réellement la partie passait pour
   désactivée. On garde UN seul aplat, celui qui lance ; le partage devient un
   contour.
   ⚠️ Le contour reprend `--color-mocha-light`, pas `--color-mocha` : mesuré sur
   le fond du salon, 6,80:1 contre 3,01:1 sous du texte blanc. Un accent et un
   fond de bouton sont deux rôles (cf. `--color-mocha-plein`). */
#shareInviteBtn.network-btn--primary {
  background: transparent;
  border: 1px solid rgba(196, 181, 160, 0.4);
  color: var(--color-mocha-light, #C4B5A0);
  box-shadow: none;
}

#shareInviteBtn.network-btn--primary:hover {
  background: rgba(168, 144, 128, 0.12);
  border-color: rgba(196, 181, 160, 0.6);
}

/* L'astuce sous le bouton désactivé : elle dit POURQUOI il est gris.
   ⚠️ Elle ne doit apparaître que si le bouton est à la fois VISIBLE et
   DÉSACTIVÉ. `network-ui.js` écrit un `display` INLINE (`flex` / `none`) pour
   réserver le bouton à l'hôte — on le lit donc par l'attribut, comme le fait
   déjà le reste de la feuille pour les écrans (piège n° 23). Sans la seconde
   condition, un invité verrait une consigne sans le bouton qu'elle explique. */
.lobby-hint {
  display: none;
  margin: -0.25rem 0 0;
  text-align: center;
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--color-mocha, #A89080);
}

#startOnlineGameBtn[disabled]:not([style*="display: none"]) ~ .lobby-hint {
  display: block;
}

/* ============================================================
   VAGUE 28 — L'ACCUEIL (proposition P1, partie mesurée)
   ============================================================ */

/* — 1. LE NIVEAU CHOISI ÉTAIT INVISIBLE EN THÈME CLAIR, ET LA CAUSE EST UNE
        INVERSION DE SPÉCIFICITÉ —

   Ce n'est pas une valeur de couleur qui manque : c'est la règle « sélectionné »
   qui perd contre la règle « au repos ».

     .ai-level-btn--easy.ai-level-btn--active   (0,2,0)   le fond plein
     body.light-mode .ai-level-btn--easy        (0,2,1)   la teinte translucide

   (0,2,1) > (0,2,0), donc en thème clair le fond plein n'est JAMAIS appliqué.
   Mesuré sur « Facile » AVEC `--active` : fond calculé `rgba(22,163,74,.12)`,
   c'est-à-dire la valeur au repos. Sélectionné ou non, le bouton est identique.

   ⚠️ C'est le motif n° 3 dans sa forme la plus vicieuse : le balayage du thème
   clair (vague 23) a corrigé l'état au repos avec un sélecteur PLUS SPÉCIFIQUE
   que celui de l'état sélectionné — il a donc éteint ce dernier en réparant le
   premier. Un état est une variante à part entière, et sa règle doit gagner.
   ⚠️ « Moyen » échappait au défaut par accident : il possède une seconde
   déclaration plus bas dans le fichier, qui repassait devant. D'où un écran où
   un seul des trois niveaux se signalait — et mal (1,87:1).

   ➡️ On rétablit la hiérarchie en donnant à l'état sélectionné un sélecteur qui
   le place au-dessus de la règle au repos du thème clair. On maîtrise alors le
   fond ET le texte ensemble, ce qui est la seule façon de ne pas réparer l'un
   en cassant l'autre. */
body.light-mode .ai-level-btn--easy.ai-level-btn--active {
  background: #15803d;
  border-color: #16a34a;
}

body.light-mode .ai-level-btn--medium.ai-level-btn--active {
  background: #b45309;
  border-color: #d97706;
}

body.light-mode .ai-level-btn--hard.ai-level-btn--active {
  background: #b91c1c;
  border-color: #dc2626;
}

/* Le libellé ET l'icône, dans la même règle. Les séparer est exactement ce qui
   a laissé passer le défaut : l'icône était déjà blanche quand le libellé
   restait brun sur l'aplat orange. */
/* ⚠️ LE TEXTE ET SON FOND DOIVENT PARTAGER LA MÊME CONDITION, AU CARACTÈRE PRÈS.
   Version précédente : `body.light-mode .ai-level-btn--active .ai-level-btn__text`.
   Elle s'appliquait même quand l'aplat, lui, ne s'appliquait pas — d'où du
   blanc sur la teinte claire au repos : mesuré **1,14:1** sur « Facile » et
   **1,18:1** sur « Difficile » en thème clair. Le correctif était pire que le
   défaut, et c'est la seconde fois dans cette vague qu'une couleur de texte est
   posée sans son fond.
   ➡️ On répète les sélecteurs COMPOSÉS des trois aplats. Si le fond ne gagne
   pas, le texte ne change pas non plus : les deux tombent ou tiennent ensemble,
   et il devient impossible d'obtenir du blanc sur du clair.
   ⚠️ Reste à élucider : sur « Facile » et « Difficile », l'aplat ne s'applique
   pas alors que sa règle est la plus spécifique et la dernière. Une sonde a
   montré que `ai-level-btn--active` est retirée de l'élément de façon
   ASYNCHRONE après le clic — il y a un re-rendu non identifié. Tant qu'il n'est
   pas trouvé, toute mesure de cet état est suspecte. */
/* ⚠️ SEUL « MOYEN » EST TRAITÉ ICI, ET C'EST DÉLIBÉRÉ.
   Mesuré au clic réel, thème clair, après le re-rendu asynchrone :
     Facile     aplat NON appliqué, teinte claire au repos, texte sombre   8,01
     Moyen      aplat #b45309 appliqué                                     5,02
     Difficile  aplat NON appliqué, teinte claire au repos, texte sombre   8,47
   Les trois règles d'aplat sont pourtant identiques, adjacentes, et les plus
   spécifiques du document. Sur « Facile » et « Difficile » le fond ne cède pas
   — cause NON élucidée, à reprendre (une sonde a montré que la classe
   `--active` est retirée de façon asynchrone après le clic : il y a un
   re-rendu non identifié).
   ➡️ Tant que ce n'est pas su, on ne pose du blanc QUE là où l'aplat est
   vérifié. Poser du blanc sur les trois donnait 1,14:1 et 1,18:1 — un correctif
   pire que le défaut, et livré une fois par erreur. */
body.light-mode .ai-level-btn--medium.ai-level-btn--active .ai-level-btn__text,
body.light-mode .ai-level-btn--medium.ai-level-btn--active svg {
  color: #ffffff;
}

/* — 2. « JOUER MAINTENANT » N'APPARTENAIT À AUCUN TOKEN —

   `linear-gradient(to right, #3b82f6, #2563eb)` : un bleu de la famille
   Tailwind, étranger à la palette, sur un écran dont le logo est or et les
   gris sont chauds. C'est aussi la teinte la plus répandue du dépôt hors
   palette — 47 occurrences pour le seul trio #3b82f6 / #2563eb / #1d4ed8.

   L'action primaire reprend l'or de la marque en thème sombre, et le mocha
   plein en thème clair — c'est ce que montre la planche P1.
   ⚠️ `--color-mocha-plein` et non `--color-mocha` : sous du texte blanc, le
   second ne vaut que 3,01:1. Un accent et un fond de bouton sont deux rôles.
   ⚠️ En thème sombre l'or porte du texte SOMBRE, pas du blanc : blanc sur
   #f4d03f ne vaut que 1,8:1. La couleur du texte suit donc le fond, dans la
   même règle. */
.home-play-btn {
  background: var(--gradient-gold, linear-gradient(135deg, #f4d03f 0%, #d4a574 100%));
  color: #1a1410;
  box-shadow: 0 4px 12px rgba(212, 165, 116, 0.35);
}

.home-play-btn svg {
  color: #1a1410;
}

/* ⚠️ C'EST L'ARRÊT LE PLUS CLAIR D'UN DÉGRADÉ QUI DÉCIDE POUR DU TEXTE BLANC.
   Première version : `#9c8069 → #8a705e`. Mesuré, l'extrémité claire ne donnait
   que **3,68:1** — exactement le score du bleu qu'on venait de retirer. On part
   donc du seuil (#8a705e, 4,61) et on assombrit vers #6B5D53 (6,33). */
body.light-mode .home-play-btn {
  background: linear-gradient(135deg, var(--color-mocha-plein, #8a705e) 0%, var(--color-mocha-dark, #6B5D53) 100%);
  color: #ffffff;
  box-shadow: 0 4px 12px rgba(107, 93, 83, 0.3);
}

body.light-mode .home-play-btn svg {
  color: #ffffff;
}

/* — 3. LE VERT « FACILE » EN THÈME SOMBRE ÉTAIT DÉJÀ SOUS LE SEUIL —
   Défaut préexistant, trouvé en mesurant les six états sélectionnés : blanc sur
   #16a34a ne vaut que **3,30:1**. Ses deux voisins passaient (moyen 5,02,
   difficile 4,83) — d'où un défaut isolé, invisible tant qu'on ne relève pas
   les trois. On aligne le vert sur la valeur déjà retenue en thème clair,
   #15803d, qui donne 5,02 : même teinte, luminosité descendue au seuil. */
.ai-level-btn--easy.ai-level-btn--active {
  background: #15803d;
  border-color: #16a34a;
}

/* ⚠️ LE SURVOL REPASSAIT AU BLEU — signalé à l'écran, et c'est la même leçon
   que l'état sélectionné du sélecteur de niveau, à trois heures d'intervalle :
   REPEINDRE `.x` NE REPEINT PAS `.x:hover`.
   Le survol vivait ailleurs dans le fichier (`@media` de survol, ligne ~1521)
   et gardait `linear-gradient(#2563eb, #1d4ed8)`, plus une ombre bleue en
   thème clair. Le bouton virait donc au bleu au passage de la souris, sur un
   écran d'où le bleu venait d'être retiré.
   ➡️ Le survol s'éclaircit d'un cran DANS la plage lisible, il ne change pas de
   famille. Texte sombre sur l'or, blanc sur le mocha : inchangé, donc les
   ratios du repos restent valables (8,19 et 4,61 au pire arrêt). */
.home-play-btn:hover {
  background: linear-gradient(135deg, #f7d95c 0%, #dfb488 100%);
  box-shadow: 0 6px 16px rgba(212, 165, 116, 0.45);
}

/* ⚠️ ET EN THÈME CLAIR LE SURVOL DOIT FONCER, PAS ÉCLAIRCIR.
   Première version ici : `#9c8069 → #7a6352`. Mesuré, l'arrêt clair donnait
   **3,68:1** — pour la troisième fois de la journée, et toujours la même cause :
   sous du texte blanc, c'est l'arrêt le PLUS CLAIR d'un dégradé qui décide, pas
   la moyenne ni celui qu'on a en tête. Le repos part déjà du seuil (#8a705e,
   4,61) : l'éclaircir ne pouvait que le faire échouer.
   ➡️ Les deux arrêts descendent sous le repos. Pire arrêt mesuré : 5,42. */
body.light-mode .home-play-btn:hover {
  background: linear-gradient(135deg, #7a6352 0%, #5c5047 100%);
  box-shadow: 0 6px 16px rgba(92, 80, 71, 0.4);
}

/* ============================================================
   VAGUE 28 — LE SÉLECTEUR DE NIVEAU PASSE AUX POINTS
   ============================================================

   Ce bloc REMPLACE l'approche par teintes. Il est écrit après une énumération
   complète de la cascade de ces boutons — 34 règles peuvent les atteindre — et
   après trois passes de réfutation qui ont toutes trouvé des défauts dans une
   première version. Ce qui suit intègre leurs correctifs.

   CE QUI ÉTAIT CASSÉ, mesuré sur DOM réel, thème clair, niveau sélectionné :
     Facile      libellé #14532d sur aplat #15803d ....... 1,82:1
     Difficile   libellé #7f1d1d sur aplat #b91c1c ....... 1,55:1
     Moyen       blanc sur #b45309 ....................... 5,02:1
   Sur le MÊME bouton, l'icône SVG était blanche (héritée de la ligne 10341, qui
   gagne pour `color`) pendant que le mot juste à côté restait sombre. Moyen
   échappait au défaut grâce à DEUX déclarations que ses voisins n'ont pas,
   ajoutées à 6 000 lignes d'écart pour deux motifs sans rapport.

   ⚠️ LA CAUSE DE FOND EST LA COMBINATOIRE, PAS UNE VALEUR. Trois niveaux × deux
   thèmes × (repos, survol, appui, sélectionné) = 24 couples texte/fond à tenir
   justes. Personne ne les tient. On les ramène à quatre en retirant la teinte
   du niveau : l'aplat ne dit plus que « c'est celui-ci », les POINTS disent la
   difficulté. */

/* — 1. AU REPOS : une surface neutre, et une BORDURE QUI SE VOIT.
     ⚠️ Retirer la couleur retire aussi le seul contour coloré : la bordure
     devient le seul signal qui dit « ceci est un contrôle » (WCAG 1.4.11,
     seuil 3:1 pour un élément d'interface). Une première version neutralisait
     à `rgba(255,255,255,.12)` — mesuré 1,96:1, soit MOINS que les 2,28:1
     qu'elle remplaçait. On monte donc l'opacité jusqu'au seuil au lieu de
     l'abaisser. — */
.ai-level-selector__buttons .ai-level-btn--easy,
.ai-level-selector__buttons .ai-level-btn--medium,
.ai-level-selector__buttons .ai-level-btn--hard {
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(245, 245, 220, 0.55);
  color: var(--color-text);
}

body.light-mode .ai-level-selector__buttons .ai-level-btn--easy,
body.light-mode .ai-level-selector__buttons .ai-level-btn--medium,
body.light-mode .ai-level-selector__buttons .ai-level-btn--hard {
  background: rgba(15, 23, 42, 0.05);
  border-color: rgba(26, 26, 26, 0.42);
  color: var(--color-text);
}

/* Le libellé au repos. Il faut battre 15133-15135 (0,3,1), écrites en vague 23
   pour les teintes qu'on vient de retirer. */
body.light-mode .ai-level-selector__buttons .ai-level-btn--easy .ai-level-btn__text,
body.light-mode .ai-level-selector__buttons .ai-level-btn--medium .ai-level-btn__text,
body.light-mode .ai-level-selector__buttons .ai-level-btn--hard .ai-level-btn__text,
.ai-level-selector__buttons .ai-level-btn .ai-level-btn__text {
  color: inherit;
  font-weight: 600;
}

/* — 2. SÉLECTIONNÉ : un seul aplat, dans les deux thèmes.
     Blanc sur --color-mocha-plein = 4,61:1. Le mocha PLEIN, jamais
     `--color-mocha` (3,01:1 sous du blanc) : un accent et un fond de bouton
     sont deux rôles. — */
.ai-level-selector__buttons .ai-level-btn--easy.ai-level-btn--active,
.ai-level-selector__buttons .ai-level-btn--medium.ai-level-btn--active,
.ai-level-selector__buttons .ai-level-btn--hard.ai-level-btn--active,
body.light-mode .ai-level-selector__buttons .ai-level-btn--easy.ai-level-btn--active,
body.light-mode .ai-level-selector__buttons .ai-level-btn--medium.ai-level-btn--active,
body.light-mode .ai-level-selector__buttons .ai-level-btn--hard.ai-level-btn--active {
  background: var(--color-mocha-plein, #8a705e);
  border-color: var(--color-mocha-light, #C4B5A0);
  color: #ffffff;
}

/* Le libellé suit son fond, TOUJOURS dans la même règle que lui. Les séparer
   est exactement ce qui a produit le blanc-sur-blanc puis le sombre-sur-sombre
   de cette vague. */
.ai-level-selector__buttons .ai-level-btn--active .ai-level-btn__text,
body.light-mode .ai-level-selector__buttons .ai-level-btn--active .ai-level-btn__text {
  color: #ffffff;
}

/* — 3. L'APPUI : `filter` TEINTE AUSSI LE TEXTE.
     `.touching:not(.die):not(.tooltip-trigger)` (styles.css:11068) pose
     `filter: brightness(0.92)`, qui s'applique à l'élément ENTIER, descendants
     compris. Le blanc du libellé devient donc #ebebeb, et le couple mesuré
     tombe à 4,43:1 — sous le seuil, à CHAQUE appui, c'est-à-dire à chaque
     sélection sur mobile.
     ⚠️ On neutralise le filtre sur ce seul bouton et on assombrit le fond
     explicitement. On ne redéclare PAS `transition` : la règle 11068 porte un
     retour tactile de 60 ms, et une déclaration concurrente à spécificité égale
     posée plus bas l'écraserait — le bouton sauterait sans transition. — */
.ai-level-selector__buttons .ai-level-btn--active:active,
.ai-level-selector__buttons .ai-level-btn--active.touching {
  filter: none;
  background-color: #7c6353;
}

/* — 4. LES POINTS —
     `currentColor` les fait suivre le texte du bouton : lisibles dans les deux
     thèmes ET dans les deux états sans qu'une seule valeur soit écrite ici.
     C'est la seule façon de ne pas rouvrir la combinatoire qu'on vient de
     fermer. — */
.ai-level-btn { flex-direction: column; gap: 0.28rem; }

.ai-level-btn__points {
  display: flex;
  gap: 0.22rem;
  align-items: center;
  justify-content: center;
}

.ai-level-btn__point {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.3;
}

.ai-level-btn__point--plein { opacity: 1; }

/* ═══════════════════════════════════════════════════════════════════════════
   P5 — LE BANDEAU D'ÉTAT : TROIS PASTILLES EMPILÉES DEVIENNENT UNE LIGNE

   `.game__info` est le SEUL bloc compressible de l'écran de jeu
   (`flex: 0 100 auto`, piège n° 23) : chaque pixel qu'on lui prend en haut est
   un pixel de moins pour le panneau des joueurs, celui qui grandit avec le
   nombre de joueurs. Or il commençait par trois pastilles empilées qui disent
   la même chose — où on en est, et à qui.

   Mesuré à 375×812, thème sombre, avant :
     .phase-badge-compact ....  48,0 px
     .phase-badge-hint .......  30,4 px
     .game__turn .............  33,2 px   (+ 4 px de marge)
     ────────────────────────────────────
     avant la 1re ligne de joueur : 157 px

   `#turnIndicator` est passé DANS `.game__phase-indicator` (index.html) ; il ne
   reste plus qu'à mettre la rangée en ligne.

   ⚠️ AUCUNE NOUVELLE CLÉ DE TRADUCTION. Le badge, l'astuce et le tour gardent
   leurs chaînes et leurs id : `ui-manager.js` continue d'écrire au même endroit.
   ⚠️ AUCUN NOUVEAU SEUIL @media. Le masquage de l'astuce réutilise EXACTEMENT
   la condition qui comprime déjà l'écran de jeu (styles.css:12957) — inventer
   un seuil de plus rouvrirait le piège n° 38 (deux `@media` désolidarisés) ou
   le n° 39 (une plage que personne ne couvre).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Le conteneur DEVIENT le bandeau ──
   C'est lui qui porte désormais le fond, la bordure et le rayon. Sans ça, on
   garde deux pastilles côte à côte au lieu d'une barre : le badge faisait
   221 px de large à lui seul, pour 295 px disponibles, et le tour (142 px)
   repartait donc à la ligne — un pavé qui flotte à droite, exactement ce que
   la maquette ne montre pas. */
.game__phase-indicator {
  flex-direction: row;
  /* ⚠️ `nowrap`, et c'est le cœur du réglage. Avec `wrap`, un conteneur flex
     RENVOIE À LA LIGNE l'élément qui ne rentre pas au lieu de le rétrécir :
     `flex-shrink` ne joue jamais. Mesuré à 375 px, il manquait exactement
     1 px — et le tour repartait sur une deuxième ligne, en pavé flottant à
     droite. En `nowrap`, les deux se compriment, et le tour tronque par
     l'ellipse au lieu de casser la mise en page. */
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.5rem;
  border-radius: 12px;
  background: rgba(212, 165, 116, 0.1);
  border: 1px solid rgba(212, 165, 116, 0.4);
}

body.light-mode .game__phase-indicator {
  background: rgba(138, 112, 94, 0.07);
  border-color: rgba(138, 112, 94, 0.35);
}

/* ── Le badge s'aplatit DANS le bandeau ──
   Il garde son contenu, ses id et son rôle de terme du lexique (`lexique.js`
   le marque, un appui donne la définition de la phase) ; il perd seulement le
   décor qui le faisait exister comme pastille autonome — bordure de 2 px,
   dégradé, ombre portée, rayon de 50 px — puisque le bandeau le porte à sa
   place. C'est ce que montre la maquette : une barre, pas deux pilules. */
.game__phase-indicator > .phase-badge-compact {
  flex: 0 1 auto;
  min-width: 0;
  gap: 0.4rem;
  padding: 0;
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

/* L'icône flèche sort du bandeau — c'est ce que montre la maquette : une
   pastille de couleur, puis le mot. Elle coûtait 20 px + 6 px de gouttière,
   soit exactement ce qui manquait pour tenir sur une ligne, et elle ne dit
   rien de plus : le mot « CHARGE » / « DÉCHARGE » porte déjà l'information,
   et la pastille change de couleur avec la phase. Une flèche de plus n'aide
   personne — surtout pas qui distingue mal les couleurs, pour qui c'est le
   MOT qui tranche. */
.game__phase-indicator > .phase-badge-compact > .phase-badge-compact__icon {
  display: none;
}

/* « CHARGE » reste en capitales espacées mais à la taille d'un libellé de
   barre, pas d'un titre : 16 px pour six lettres capitales mangeaient la
   place du tour. */
.game__phase-indicator .phase-badge-compact__text {
  font-size: 0.8125rem;
}

/* ── Le tour n'est plus DIT dans le bandeau, il est MONTRÉ sur la carte ──
   La pastille « à vous » se pose sur la carte du joueur (`pastilleVotreTour`,
   ui-manager.js) : l'information est donnée là où l'œil est déjà, et le
   bandeau ne garde que la phase et son aide. C'est ce que demandait déjà P2.

   ⚠️ `#turnIndicator` N'EST PAS SUPPRIMÉ, ni mis en `hidden`. Il porte
   `role="status"` et `aria-live="polite"` (BACKLOG #B09) : c'est lui qui
   ANNONCE le changement de tour aux lecteurs d'écran. Le masquer pour de bon
   rendrait l'information visuelle seulement — exactement le défaut que B09
   avait corrigé. Il devient donc invisible À L'ŒIL, et lui seul : même
   technique que `.sr-only`, jamais `display: none`, qui le retirerait aussi de
   l'arbre d'accessibilité et tuerait la région live. */
.game__phase-indicator > .game__turn {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── La pastille « à vous », sur la carte du joueur ──
   Contrastes mesurés sur les fonds réels, dans les DEUX thèmes (piège n° 35) :
   sombre  #0d1821 sur #C4B5A0 = 8,98:1
   clair   #ffffff sur #8a705e = 4,66:1
   Les deux passent le seuil texte de 4,5:1. */
.player-turn-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  max-width: 8.5rem;
  /* `auto` cale la pastille à droite du groupe, contre le compte de jetons.
     `margin-inline-*` et pas `margin-left/right` : en arabe elle doit filer à
     gauche toute seule. */
  margin-inline-start: auto;
  margin-inline-end: 0.1rem;
  padding: 0.15rem 0.45rem;
  border-radius: 6px;
  background: var(--color-mocha-light);
  color: #0d1821;
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

body.light-mode .player-turn-chip {
  background: var(--color-mocha-plein);
  color: #fff;
}

/* L'astuce vit sous le bandeau (elle en est sortie dans le balisage) : elle
   est trop longue pour tenir à côté du badge dans les 8 langues. */
.game__info > .phase-badge-hint {
  margin-top: 0.4rem;
}

@media (max-height: 880px) and (max-width: 768px) {
  /* Sur l'écran étroit, l'astuce sort du flux : elle répète ce que donne déjà
     le badge, qui est un terme du lexique — un appui sur « CHARGE » en affiche
     la définition, traduite dans les 8 langues. C'est la place d'une carte de
     joueur entière qu'on rend au panneau. */
  #gameScreen[style*="display: block"] .phase-badge-hint {
    display: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   P3 — STATISTIQUES : L'ANNEAU, LES ONGLETS, ET LE POIDS DE L'ACTION DESTRUCTRICE
   ═══════════════════════════════════════════════════════════════════════════ */

/* — 1. LE LIBELLÉ NE DOIT PLUS TOUCHER L'ANNEAU —
   `.st-ring__label` était plafonné à `max-width: 86%`, soit 94 px pour un
   anneau de 109 px. Or la corde disponible à la hauteur du libellé ne fait que
   73 px : le plafond était plus large que la place. Mesuré dans les 8 langues,
   à 375 px :
     es  « Tasa de victorias » ..... 76 px pour 73 → TRAVERSE l'anneau
     fr  « Taux de victoire » ...... 73 px pour 73 → pile sur le trait
     pt  « Taxa de vitórias » ...... 72 px pour 73 → 1 px de marge
   Le français passait donc de justesse, ce qui explique qu'on ne l'ait pas vu
   en le regardant en français. On ramène le plafond à la corde réelle : les
   libellés longs passent sur deux lignes courtes, qui tiennent dans le disque.
   ⚠️ On ne touche PAS à `font-size` : le plancher de 0,66rem est déjà le
   résultat d'une mesure (styles.css:13294), 9,3 px étant illisible. */
.st-ring__label {
  /* 62 % et pas 66 : à 66 %, « Taxa de vitórias » (pt-br) tenait tout juste sur
     une ligne et son encre arrivait à 41 px du centre — exactement le rayon de
     la ligne MÉDIANE du trait, donc à cheval sur sa moitié intérieure. À 62 %
     il passe sur deux lignes comme l'espagnol et le français, et les 8 langues
     rentrent avec de la marge. Mesuré à l'encre, glyphe par glyphe (`Range`),
     et non sur la boîte : les coins d'une boîte de texte centré sont vides et
     donnaient 3 fausses alertes. */
  max-width: 62%;
  text-wrap: balance;
}

/* — 2. LES QUATRE ONGLETS SUR UNE LIGNE QUAND IL Y A LA PLACE —
   ⚠️ La grille 2×2 est un choix DÉLIBÉRÉ, documenté en styles.css:13154 : elle
   remplace une version qui tassait les quatre onglets sur une ligne à 0,58rem.
   On ne revient donc pas dessus sous 480 px — on ajoute seulement la ligne
   unique là où les libellés tiennent à taille normale, sans rien comprimer. */
@media (min-width: 620px) {
  .st-tabs { grid-template-columns: repeat(4, 1fr); }
}

/* — 3. L'ACTION DESTRUCTRICE NE DOIT PAS ÊTRE LA PLUS LARGE —
   Mesuré à 375 px : « Réinitialiser les stats » 269 px contre 64 px pour
   « Fermer » — l'action irréversible occupait 4,2 fois la place de l'action
   attendue. Ce n'est pas ce que voulait la règle de styles.css:14031, dont le
   commentaire dit : « Fermer garde sa largeur et c'est la réinitialisation qui
   SE COMPRIME ». `flex: 1 1 auto` autorise pourtant la CROISSANCE autant que la
   compression — `flex-grow: 1` — donc le bouton se gonflait jusqu'à remplir la
   ligne. `0 1 auto` dit ce que le commentaire disait déjà : il peut rétrécir,
   jamais grandir. */
.st-footer .st-reset { flex: 0 1 auto; }

/* Et « Fermer » prend la place rendue : c'est l'action attendue, elle doit être
   la plus large autant que la plus visible. Après : 168 px pour la
   réinitialisation contre 64 px auparavant pour « Fermer ». */
.st-footer .btn--primary { flex: 1 1 auto; }

/* ═══════════════════════════════════════════════════════════════════════════
   P7 — L'ACCUEIL TIENT DANS L'ÉCRAN, DESCRIPTION COMPRISE

   Mesuré à 375×812, état le PLUS CHARGÉ (bloc « reprendre la partie » affiché,
   c'est le corollaire du piège n° 38) :

     .game-logo ............ 186,0 px   92 → 278
     .home-bottom-nav ...... 362,1 px  286 → 648
     .home-liens ............ 93,6 px  671 → 765
     .home-pitch ............ 80,9 px  775 → 856   ← 44 px DEHORS

   `.screen--setup` est en `overflow-y: hidden` et ne défile pas : ces 44 px ne
   sont pas sous un ascenseur, ils sont INATTEIGNABLES. Or `.home-pitch` est le
   seul texte indexable de l'accueil (CLAUDE.md § 9) — celui qui répond au refus
   AdSense « contenu à faible valeur informative ». En couper la moitié, c'est
   rouvrir ce dossier.

   ⚠️ Le `flex: 1` que la proposition P7 accuse N'EXISTE PLUS : `.game-logo` est
   déjà en `flex: 0 1 0%`. La cause restante n'est pas une croissance, c'est un
   cumul de blancs. On les reprend là où ils ne portent aucune information —
   jamais sur du texte, jamais sur une cible tactile (les boutons gardent leurs
   48 px).
   ═══════════════════════════════════════════════════════════════════════════ */

/* 20 px : le logo est décoratif, son rembourrage n'a pas à valoir celui d'une
   carte de contenu. */
#setupScreen .game-logo { padding: 6px; }

/* 12 px : la gouttière du sélecteur de niveau, symétrique, reste lisible à 10. */
#setupScreen .ai-level-selector { margin-top: 10px; margin-bottom: 10px; }

/* 10 px : l'écart avant les six liens de contenu. */
#setupScreen .home-liens { margin-top: 12px; }

/* 3 px : et celui avant la description. */
#setupScreen .home-pitch { margin-top: 8px; }

/* — Les deux boutons « Amis » ne doivent pas changer de hauteur avec le thème —
   Mesuré à 375 px : 48 px en sombre, 59,6 px en clair. Le libellé passait sur
   DEUX lignes en clair, parce que le bouton y fait 141 px de large contre 149
   en sombre — 8 px qui suffisent à faire replier « Amis en local ».
   ⚠️ Ce n'est pas la bordure de `body.light-mode .home-multiplayer-btn` : les
   deux thèmes ont bien `border: 1px` et `box-sizing: border-box`. C'est la
   largeur du bouton elle-même qui diffère.
   Ces 11,6 px sont exactement ce qui manquait à la description pour tenir dans
   l'écran en thème clair — le défaut de rythme et le défaut de thème étaient le
   même défaut. On empêche le repli et on rend la gouttière au texte. */
#setupScreen .home-multiplayer-btn {
  padding-left: 10px;
  padding-right: 10px;
  white-space: nowrap;
  /* ⚠️ `nowrap` SEUL échangeait un repli contre un DÉBORDEMENT : mesuré, le
     libellé sortait du bouton en espagnol, en portugais et en anglais clair.
     Interdire le repli sans donner la place, c'est déplacer le défaut.
     Le pire des 8 langues est l'espagnol « Amigos en persona » : 127 px
     nécessaires pour 119 disponibles, soit un corps de 13,12 px maximum.
     0,8125rem = 13 px passe partout, avec de la marge. */
  font-size: 0.8125rem;
}

/* — La barre de défilement de l'accueil ne doit pas changer la LARGEUR selon le thème —
   `body.light-mode ::-webkit-scrollbar` (styles.css:9287) est un sélecteur
   UNIVERSEL. Or déclarer la moindre propriété sur ce pseudo-élément fait
   basculer Chrome de la barre en surimpression — qui ne prend aucune place —
   à la barre CLASSIQUE, qui réserve sa gouttière dans la mise en page.
   Le thème sombre, lui, n'a aucune règle : il garde la surimpression.

   Mesuré à 375×812 sur `.container` : gouttière 0 px en sombre, 15 px en clair.
   Ces 15 px rétrécissaient `.home-multiplayer-btns` de 303 à 288 px, donc
   chaque bouton de 149 à 141, ce qui suffisait à faire replier « Amis en
   local » sur deux lignes — d'où 11,6 px de hauteur en plus, et la description
   poussée hors de l'écran. Une règle de COULEUR déplaçait une mise en page.

   ⚠️ On aligne le clair sur le sombre, et pas l'inverse : réserver la
   gouttière dans les deux thèmes coûterait 15 px de large partout, y compris
   là où le contenu tient déjà. Le contenu reste défilable — molette, geste
   tactile et clavier sont inchangés. */
body.light-mode .container::-webkit-scrollbar {
  width: 0;
  height: 0;
}

/* Le NOM cède la place à la pastille, pas l'inverse.
   Mesuré avec un prénom de 20 caractères (le `maxlength` réel des quatre
   champs de saisie) : la carte débordait en français (pastille de 135 px) et
   en anglais (108 px). Les six autres langues tiennent — `game.yourTurn` y est
   court : « دورك » 39 px, « 你的回合 » 61 px, « ตาของคุณ » 72 px.
   ⚠️ On ne rogne PAS la pastille : c'est du texte d'interface, il se lit en
   entier ou il ne sert à rien. Le prénom, lui, est une donnée du joueur, déjà
   variable par nature, et l'avatar continue de l'identifier — c'est donc lui
   qui s'ellipse. `min-width: 0` sur le conteneur flex est indispensable :
   sans lui un élément flex refuse de descendre sous sa taille de contenu, et
   l'ellipse ne se déclenche jamais. */
/* `flex: 1` pour que le groupe de gauche occupe la place libre : c'est ce qui
   permet à la pastille de se caler à DROITE par `margin-inline-start: auto`,
   juste avant le compte de jetons. Sans lui, le groupe reste à sa largeur
   naturelle et la marge automatique ne pousse rien. */
.player-solo__left,
.player-multi__left { flex: 1 1 auto; min-width: 0; }

.player-solo__name,
.player-multi__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════════════════════
   LE PANNEAU DU HAUT RESPIRE — signalé « ça fait tassé », et c'était juste.

   Mesuré à 375×812 avant : `.game__info` avait 6 px de rembourrage vertical,
   4 px sous le bandeau et 6 px sous le pot, pour **526 px de vide sous le
   panneau**. La compression de P5 avait rendu 78 px : elle les avait rendus
   au BAS de l'écran, pas au contenu. On en remet une partie dans le bloc.

   ⚠️ Ces valeurs sont plafonnées par le cas à 6 JOUEURS, pas par celui qu'on
   a sous les yeux — c'est le corollaire du piège n° 38. Vérifié à 6 joueurs
   après coup : le panneau ne défile toujours pas et le bouton d'action reste
   à l'écran. Ne pas les augmenter sans refaire CETTE mesure-là.
   ═══════════════════════════════════════════════════════════════════════════ */
#gameScreen[style*="display: block"] > .game__info {
  padding-top: 12px;
  padding-bottom: 12px;
}

#gameScreen[style*="display: block"] .game__phase-indicator {
  margin-bottom: 10px;
}

#gameScreen[style*="display: block"] .game__pot {
  margin-bottom: 10px;
}

#gameScreen[style*="display: block"] .game__players {
  gap: 10px;
}

/* Les 31 px qui restaient à l'accueil, pris sur le rembourrage du conteneur.
   `.container` porte 24 px sur ses quatre côtés — confortable sur un écran
   large, coûteux sur 812 px de haut où ils valent 48 px de contenu. Ramenés à
   10 px en vertical sous 769 px seulement ; l'horizontal ne bouge pas, c'est
   lui qui tient le texte à distance du bord.
   ⚠️ `.container` sert TOUS les écrans : vérifié aussi sur l'écran de jeu, qui
   n'y perd rien et y gagne 28 px pour le panneau des joueurs. */
@media (max-width: 768px) {
  .container {
    padding-top: 10px;
    /* 6 et pas 10 : il restait 3 px de débordement à 375×812, et un ascenseur
       pour 3 px reste un ascenseur. Mesuré après : 0. */
    padding-bottom: 6px;
  }
}

/* — Le logo maigrit SEULEMENT sur les écrans courts —
   Mesuré après la correction du `min-height` :
     375×812 -> débordement 0, logo entier à 166 px
     360×740 -> débordement 65 px, contenu réel cette fois
   Un écran de 740 px ne peut pas montrer tout l'accueil avec un logo de
   166 px. On le réduit donc là, et LÀ SEULEMENT : le dé décoratif sort, le
   « 421 » passe de 64 à 46 px de corps — soit 70 px rendus, pour 65 demandés.
   ⚠️ Condition en HAUTEUR seule. Le défaut est une histoire de hauteur : le
   borner en largeur ferait maigrir le logo sur des écrans qui ont la place
   (piège n° 39, la plage que personne ne couvre) et le laisserait intact sur
   des écrans qui ne l'ont pas.
   ⚠️ 780 px n'est pas un seuil neuf : c'est déjà celui du coach, plus haut
   dans ce fichier. On n'en invente pas un de plus. */
@media (max-height: 780px) {
  #setupScreen .logo-dice-icon { display: none; }
  #setupScreen .logo-text { font-size: 46px; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   P9 — LA MISE EN SCÈNE PROPORTIONNÉE AU RÉSULTAT (vague 30)
   ═══════════════════════════════════════════════════════════════════════════
   165 lancers sur 216 (76,4 %) ne valent qu'un jeton — et n'ont déjà aucune
   célébration ('other', rang 7 : event-handlers.js le commente). Le défaut
   VÉRIFIÉ n'était donc pas « un halo pour tout » comme l'énonçait la
   proposition : c'est que fiche 5-6, baraque 5-6 et brelan d'as recevaient
   EXACTEMENT le même `golden-glow` que le 421. Trois niveaux désormais :
     · ordinaire (rangs 5-7)          → rien, comme avant ;
     · notable (fiche/baraque ≥ 5,
       brelan d'as)                   → `die--notable`, le filet or ci-dessous ;
     · 421                            → seul à garder golden-glow + éclat.
   Posées par game-logic.js (celebrateFiche / celebrateBaraque).

   ⚠️ AUCUNE PROPRIÉTÉ DE MISE EN PAGE : border-color et box-shadow seulement.
   La largeur de bordure reste celle de l'état porteur (2 px, ou 4 px si le dé
   est `die--kept`) — un filet qui change l'épaisseur ferait bouger les dés
   (piège n° 49, vérifié au mouchard).

   ⚠️ Les !important sont NÉCESSAIRES, pas cosmétiques : une baraque ≥ 5
   auto-bloque les trois dés (event-handlers.js:579), et `.die--kept` pose
   border et box-shadow en !important (styles.css:2948). À spécificité égale
   (0,1,0 contre 0,1,0), c'est la dernière règle du fichier qui gagne : ce
   bloc DOIT rester sous la règle `.die--kept`. */
.die--notable {
  border-color: var(--color-gold-dark) !important;
  box-shadow: 0 0 14px rgba(244, 208, 63, 0.45), var(--shadow-md) !important;
}

/* Le halo or clair se voit moins sur fond clair : on appuie l'ombre portée
   au lieu de monter l'opacité du halo — même geste que `body.light-mode .die`
   (styles.css:15405), qui renforce déjà l'ombre en clair. Décoratif et
   redondant par construction : le popup de score nomme la combinaison. */
body.light-mode .die--notable {
  box-shadow: 0 0 10px rgba(184, 134, 11, 0.5), 0 4px 16px rgba(0, 0, 0, 0.16) !important;
}

/* ---- Le cadre du coach ne félicite plus un lancer ordinaire ----
   `.coach` (styles.css:12688) posait un cadre VERT (--color-success) en
   permanence : la couleur du succès encadrait aussi « ça vaut 1 jeton ».
   Neutre mocha désormais ; le vert reste la signature du coach là où il
   CONSEILLE (titre, icône, pointillé `.die--coach`) — il ne cautionne plus
   chaque résultat. L'or n'apparaît que sur `coach--421`, posé par coach-ui.js
   quand le tour expliqué est un 421 (rang 1 — piège n° 33).
   Les couleurs de TEXTE ne changent pas : les contrastes mesurés en vague 27
   restent valables, seuls cadre et fond (quasi transparents) bougent. */
.coach {
  background: rgba(168, 144, 128, 0.08);
  border-color: rgba(196, 181, 160, 0.28);
}

[data-theme="light"] .coach,
body.light-mode .coach {
  background: rgba(107, 93, 83, 0.05);
  border-color: rgba(107, 93, 83, 0.22);
}

/* ⚠️ ORDRE ET SPÉCIFICITÉ : `body.light-mode .coach` (0,1,1) battrait
   `.coach--421` (0,1,0) — la variante claire ci-dessous, à (0,2,0), est donc
   OBLIGATOIRE, pas une redite (piège n° 37 : l'état sélectionné passe en
   dernier à travers les mailles). */
.coach--421 {
  background: rgba(244, 208, 63, 0.08);
  border-color: rgba(244, 208, 63, 0.45);
}

[data-theme="light"] .coach--421,
body.light-mode .coach--421 {
  background: rgba(184, 134, 11, 0.06);
  border-color: rgba(184, 134, 11, 0.5);
}

/* ═══════════════════════════════════════════════════════════════════════════
   CARTE DE BIENVENUE — LA CASE DES CONDITIONS (17 août 2026)

   ⚠️ EN FIN DE FICHIER, ET C'EST LA RÈGLE DU PROJET (piège n° 6) : styles.css
   fait 16 000 lignes, une règle posée trop tôt se fait écraser.

   ⚠️ ET UNE CLASSE SANS RÈGLE N'EST PAS « NON STYLÉE », C'EST UN BLOC EN FLUX
   NORMAL (piège n° 50) : sans ce bloc, la case et son libellé s'empileraient
   sur deux lignes au lieu de se lire comme une phrase.

   Les couleurs reprennent celles de `.bienvenue__prenom` juste au-dessus —
   crème sur fond sombre, ardoise en thème clair — pour que la carte reste
   d'une seule pièce. Aucune couleur nouvelle.
   ═══════════════════════════════════════════════════════════════════════════ */

.bienvenue__conditions {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  margin: 0.25rem 0 0.5rem;
  text-align: left;
}

.bienvenue__conditions input[type="checkbox"] {
  /* 20 px : sous cette taille la cible tactile devient pénible, et c'est la
     seule commande OBLIGATOIRE de la carte. */
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin-top: 0.1rem;
  accent-color: #5da283;
  cursor: pointer;
}

.bienvenue__conditions label {
  font-size: 0.9rem;
  line-height: 1.35;
  color: rgba(245, 245, 220, 0.85);
  cursor: pointer;
}

.bienvenue__lien {
  display: inline-block;
  margin: 0 0 1rem;
  font-size: 0.85rem;
  color: var(--color-mocha);
  text-decoration: underline;
}

.bienvenue__lien:hover { color: var(--color-cream); }

.bienvenue__lien:focus-visible,
.bienvenue__conditions input[type="checkbox"]:focus-visible {
  outline: 2px solid #5da283;
  outline-offset: 2px;
}

/* ⚠️ UN BOUTON DÉSACTIVÉ DOIT SE VOIR DÉSACTIVÉ. Sans cela le joueur tape sur
   « Je me lance », rien ne se passe, et rien ne lui dit pourquoi. */
.bienvenue__principal[disabled],
.bienvenue__secondaire[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.35);
}

body.light-mode .bienvenue__conditions label,
[data-theme="light"] .bienvenue__conditions label { color: #3d4654; }

/* ── « Voir le tutoriel » : icône + libellé, et il passe AU-DESSUS ──────────
   Le bouton ne portait qu'un texte ; il aligne désormais une icône Lucide et
   son libellé. Sans `display: flex` ils s'empileraient (piège n° 50 : une
   classe sans règle est un bloc en flux normal).
   ⚠️ `margin-bottom` sur le tutoriel et non `margin-top` sur le principal :
   c'est le tutoriel qui vient d'être déplacé, autant que l'espacement suive
   l'élément déplacé plutôt que d'être réparti sur les deux. */
.bienvenue__secondaire {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  margin-bottom: 0.6rem;
}

.bienvenue__secondaire svg {
  flex: 0 0 auto;
  /* L'icône suit la couleur du libellé, y compris à l'état désactivé et au
     survol : `currentColor` est déjà posé par lib/icons.js. */
  stroke: currentColor;
}

/* ⚠️ ÉCRANS COURTS — LA CARTE A GRANDI DE DEUX ÉLÉMENTS, ET ÇA SE PAYE EN BAS.
   Mesuré à 320x568 juste après l'ajout de la case et du lien : « Je me lance »
   tombait 22 px sous la zone visible. La carte défile (overflow-y: auto), donc
   rien n'était inatteignable — mais l'action principale n'était plus visible
   d'emblée, ce qui est un défaut à soi seul sur l'écran d'accueil.
   On récupère la hauteur sur les BLANCS des éléments ajoutés, jamais sur la
   taille de la case à cocher (20 px est déjà le minimum tactile) ni sur le
   libellé, qui porte l'information juridique. */
@media (max-height: 640px) {
  .bienvenue__conditions { margin: 0.1rem 0 0.25rem; }
  .bienvenue__lien { margin-bottom: 0.5rem; }
  .bienvenue__secondaire { margin-bottom: 0.35rem; }
  .bienvenue__prenom { margin-bottom: 0.6rem; }
}

/* ⚠️ LE LIEN DE LA POLITIQUE ÉCHOUAIT EN THÈME CLAIR — 2,79:1, MESURÉ.
   `--color-mocha` (#A89080) est un accent prévu pour être posé SUR un fond
   sombre : sur la carte claire (#f8f6f0) il tombe sous le seuil de 4,5 du
   texte. C'est le piège n° 35 mot pour mot — une couleur qui passe dans un
   thème et échoue dans l'autre, et qu'on ne voit pas si on ne mesure qu'un
   seul thème. On ne touche PAS à `--color-mocha`, employée partout ailleurs
   comme texte sur fond sombre où elle est juste : on surcharge par thème,
   avec le jeton assombri que le projet possède déjà. */
body.light-mode .bienvenue__lien,
[data-theme="light"] .bienvenue__lien { color: var(--color-mocha-dark); }

body.light-mode .bienvenue__lien:hover,
[data-theme="light"] .bienvenue__lien:hover { color: #3d4654; }

/* ⚠️ PLUS AUCUN ASCENSEUR SUR LA CARTE — demande du propriétaire.
   Mesuré à 320x568 : `scrollHeight - clientHeight` = 49 px de trop.

   ⚠️ CE QU'ON NE TOUCHE PAS, ET POURQUOI. Le lien de la politique fait 44 px
   pour une seule ligne de 13,6 px : ce n'est pas du gras, c'est le
   `min-height: 44px` de CIBLE TACTILE que le projet pose sur ses liens. Le
   rogner ferait gagner 26 px et casserait une règle d'accessibilité — on
   cherche les pixels ailleurs. Même raison pour la case (20 px) et pour les
   deux boutons.

   On les prend donc sur du DÉCORATIF et sur des blancs :
   · l'icône de dés en tête de carte est `aria-hidden`, elle ne porte aucune
     information — 28 px + 3,2 de marge ;
   · le rembourrage de la carte, 22,4 px en haut et en bas.
   Aucune phrase supprimée, aucune cible tactile réduite. */
/* ⚠️ DEUX PALIERS, ET LE PREMIER EXISTE POUR UNE MARGE, PAS POUR UN DÉFAUT.
   Mesuré langue par langue : à 375x667 — un iPhone SE2/8, l'un des formats les
   plus répandus — le contenu faisait 585 px pour un plafond de 586,96. Soit
   2 px. Aucun ascenseur ce jour-là, dans les 8 langues livrées… et rien pour
   encaisser une traduction plus longue, une police système agrandie, ou la
   prochaine phrase ajoutée à cette carte. Un écran qui tient à 2 px près ne
   tient pas : il attend.
   ➡️ Sous 780 px on reprend les blancs, ce qui porte la marge à ~30 px.
   ➡️ Sous 640 px on y ajoute l'icône décorative (`aria-hidden`, aucune
      information perdue), qui était le seul moyen de rendre les 49 px qui
      manquaient vraiment. */
@media (max-height: 780px) {
  .bienvenue__carte {
    padding-top: 1rem;
    padding-bottom: 1rem;
  }

  /* ⚠️ ON NE TOUCHE PAS À LA TAILLE DE POLICE ICI, ON RESSERRE L'INTERLIGNE.
     Première version écrite avec `font-size: 0.92rem` en croyant réduire :
     la règle de base vaut 0.9rem, donc cette ligne AGRANDISSAIT le texte et
     annulait les 12,8 px repris sur le rembourrage. Mesuré : 4 px gagnés au
     lieu des 13 attendus. C'est la règle du projet — on lit la valeur
     CALCULÉE, jamais celle qu'on croit avoir écrite. */
  .bienvenue__texte { line-height: 1.35; }

  /* ⚠️ L'ICÔNE DÉCORATIVE TOMBE DÈS 780 px DE HAUT, ET C'EST UN ARBITRAGE
     ASSUMÉ. Rembourrage et interligne resserrés ne rendaient que ~14 px : le
     hindi restait à 5 px du plafond à 375x667, c'est-à-dire un ascenseur au
     premier mot ajouté. L'icône vaut 31 px à elle seule (28 + marge) et ne
     porte AUCUNE information — elle est `aria-hidden`, un lecteur d'écran ne
     l'annonce même pas. Entre un ornement sur téléphone et la certitude de
     n'avoir jamais d'ascenseur, la demande du propriétaire tranche.
     Elle reste affichée sur les écrans hauts (ordinateur, tablette). */
  .bienvenue__icone { display: none; }
}

@media (max-height: 640px) {
  .bienvenue__carte {
    padding-top: 0.9rem;
    padding-bottom: 0.9rem;
  }

  .bienvenue__icone { display: none; }

  .bienvenue__texte {
    font-size: 0.9rem;
    line-height: 1.3;
  }
}

/* ⚠️ TROISIÈME PALIER — LE PORTUGAIS EST LA LANGUE QUI DÉCIDE ICI.
   Mesuré langue par langue à 320x568, le plus petit écran encore courant :
   le français, l'anglais et l'arabe gardaient 22 px de marge, le chinois 55 —
   et le PORTUGAIS 3. C'est lui qui fixe le besoin, pas la langue du source.
   (Même leçon que le corollaire du piège n° 38 : un seuil se cale sur l'état
   le plus chargé de l'écran, pas sur celui qu'on a sous les yeux.)
   On reprend encore ~24 px sur les blancs et l'interligne. La case, les deux
   boutons et le lien gardent leurs dimensions tactiles. */
@media (max-height: 600px) {
  .bienvenue__carte {
    padding-top: 0.6rem;
    padding-bottom: 0.6rem;
  }

  .bienvenue__texte { line-height: 1.22; }
  .bienvenue__prenom { margin-bottom: 0.4rem; }
  .bienvenue__lien { margin-bottom: 0.3rem; }
  .bienvenue__secondaire { margin-bottom: 0.25rem; }
  .bienvenue__conditions { margin: 0.05rem 0 0.2rem; }
}
